A Laboratory For Teaching Object-Oriented Thinking


July 17, 2018

last edit : September 5, 2018

객체지향 사고를 가르치는 실험실

원문 : http://c2.com/doc/oopsla89/paper.html

  • 켄트 벡, 애플 컴퓨터, Inc.
  • 워드 커닝햄, 와이엇 소프트웨어 서비스, Inc.
  • 번역 : 김승범 picxenk@gmail.com

초보자와 숙련된 절차적 프로그래머 모두에게 객체지향 설계에서 필요한 의인화된 관점을 소개하는 일은 어려운 일입니다.우리는 학습자가 객체를 바로 경험할 수 있는 방법으로 객체를 클래스(Class)의 이름, 책임(Responsibilities), 그리고 협력자(Collaborators)로 나타내는 CRC 카드를 소개합니다.우리는 이 접근법이 초보 프로그래머에게 객체의 개념을 가르칠 때와 숙련된 개발자에게 기존의 복잡한 설계를 소개할 때에 성공적임을 발견하였습니다.

1장. 문제점

객체지향 프로그래밍을 가르치면서 가장 어려운 문제는 학습자가 그들에게 주어진 작업을 수행하기 위해 절차형 프로그램에서나 가능한 제어방식인 전역적인 지식을 포기하게 하고, 객체들의 지역적인 지식에 의존하도록 만드는 것입니다. 초보자의 설계에는 쓸데없는 전역변수, 불필요한 포인터, 그리고 다른 객체 구현과의 부적절한 의존성처럼 전역적인 사고방식으로 되돌아가려는 경향으로 가득합니다.

객체를 배우려면 종합적인 접근 방식의 전환이 필요하므로, 객체 가르치기를 객체의 설계를 가르치는 방식으로 범위를 줄이겠습니다. 초보자에게 기본적인 개념을 가르치든 혹은 숙련된 객체지향 프로그래머에게 복잡한 설계의 미묘함을 설명하든 우리는 설계에 초점을 맞춥니다.

우리는 객체 설계를 가능한한 절차형 설계처럼 만들기보다는 학습자가 재료들의 “객체성 (object-ness)”에 몰입하도록 만드는 것이 객체로 사고하는 특유의 방식을 가르치는 가장 효과적인 방법임을 알게 되었습니다. 이를 위해 으레 배우게 되는 문법이나 프로그래밍 환경 사용법과 같은 세부사항들은 최대한 배제해야 합니다. 이런 내용들은 핵심을 완전히 이해하고 나면 금방 익힐 수 있습니다.

이 글에서 우리는 객체 설계에 대한 우리의 관점과 그 관점의 구체적인 산물인 CRC카드를, 그리고 이 카드를 사용해서 객체로 사고하는 핵심과 미묘한 차이들을 가르친 경험에 대해서 설명하겠습니다.

2장. 관점

절차형 설계는 구현 언어나 운영체제에 상관없이 추상적인 수준에서 프로세스, 데이터 흐름, 그리고 데이터 저장으로 특징지을 수 있습니다[1]. 우리는 객체 설계에 있어서도 유사한 기본 원칙을 제안해보려고 합니다. 설계 내에서 객체의 역할을 파악하는 세 가지 측면으로 클래스 이름 (class name), 책임 (responsibilities), 그리고 협력자 (collaborators) 를 정하였습니다.

객체의 클래스 이름은 설계 논의의 어휘를 구성합니다. 사실, 많은 사람들이 객체 설계는 절차적 프로그램의 설계 보다는 언어 설계와 비슷하다고 말합니다. 우리는 학습자가 더 큰 설계 환경의 맥락에서 내부적으로 일관되고 맥락을 상기시킬 수 있는 적절한 단어 세트를 찾기를 바랍니다. (우리도 설계 과정에서 이 일에 많은 시간을 보냅니다.)

책임 (Responsibilities) 은 해결해야 할 문제들을 드러냅니다. 해결법은 여러 버전과 거듭한 개선으로 만들어집니다. 책임은 잠재적인 해결책을 논의할 때 핸들 역할을 합니다. 객체의 책임은 능동적 동사를 포함한 짧은 동사구로 표현합니다. 더 짧고 능동적인 어구로 표현이 될 수록 설계는 더 강력하고 간결해집니다. 다시 말하자면, 설계 단계에서 올바른 단어를 찾기위해 소모한 시간은 가치있는 일입니다.

객체 설계의 구별되는 특징 중 하나는 섬으로 존재하는 객체가 없다는 점입니다. 모든 객체는 서비스와 제어를 위해 의지하는 다른 객체들과의 관계 속에서 존재합니다. 객체 설계를 특징짓기 위해 사용하는 마지막 요소는 객체의 협력자입니다. 책임을 충족시키기 위해 서로 메시지를 주고 받는 객체들을 협력자라고 부릅니다. 협력은 대칭적인 관계일 필요는 없습니다. 예를 들면 Smalltalk-80[2]에서 뷰(View)와 컨트롤러(Controller)는 후에 나올 예처럼 거의 동등하게 협력하는 반면, OrderedCollection은 상대 객체에 대한 고려나 인식이 없이도 서비스를 제공합니다.

이 글 전반에 걸쳐 우리는 의도적으로 클래스와 인스턴스를 딱히 구분하지 않겠습니다. 우리가 사용할 방법이 인스턴스 이름 짓기를 대신할만큼 구체적이므로 이런 비공식적인 방식이 생각만큼 혼란스럽지는 않습니다. 이 점 덕분에 사용하는 언어가 클래스 기반이든 프로토타입 기반이든 상관없이 독립적으로 우리의 방법을 가르칠 수 있습니다.

3장. CRC 카드

제2 저자인 워드 커닝햄은 협업적인 설계 결정을 문서화하기 위해 CRC 카드를 발명하였습니다. CRC 카드는 처음에 하이퍼카드 (Hypercard [3]) 스택을 이용해 만들어서 자동으로 협력자에 대한 인덱스가 제공되었지만, 이동성과 시스템 독립성의 문제를 해결하기 위해 현재의 형태로 바뀌었습니다.

객체의 협력을 문서화하는 우리의 초기 작업[4]과 마찬가지로, CRC 카드는 동시에 여러 개의 객체를 명시적으로 표현합니다. 그러나 협력의 세부 과정을 메시지 전달의 형태로 단순히 추적하기 보다는, CRC 카드는 많은 메시지를 (잠재적으로는) 일상 언어로 표현해서 설계자가 협력의 동기에 집중할 수 있도록 도와줍니다.

현재 우리는 4” x 6” 크기의 인덱스 카드를 사용해서 객체의 정보를 기술합니다. 인덱스 카드는 저렴하고, 가지고 다니기 쉬우며, 구하기도 쉬울 뿐만 아니라 친숙하다는 장점이 있습니다. 그림 1은 이상적인 카드 예를 보여줍니다. 클래스의 이름은 좌측 상단 모서리에 밑줄과 함께 적고, 카드의 좌측 3분의 2 공간에는 책임의 목록을 나열합니다. 그리고 우측에는 협력자의 목록을 적습니다.

그림 1. 클래스(Class)-책임(Responsibility)-협력자(Collaborator) (CRC) 인덱스 카드

그림 2는 Smalltalk-80 이미지에서 가져온 한 가지 예로, 흔히 오해되는 모델-뷰-컨트롤러 사용자 인터페이스 프레임워크입니다. 명확한 설명을 위해 일부러 각 객체가 수행하는 책임의 일부만을 표시하였습니다. 뷰와 컨트롤러 카드의 경우 (긴밀한 협력관계를 보여주도록) 서로 겹쳐놨고, (지휘관계를 보여주도록) 모델 카드의 위에 배치하였습니다. 우리는 이들 및 기타 비격식적인 배치 방법이 설계를 이해하는 데 도움이 됨을 발견하였습니다. 예를 들어, 부분을 나타내는 카드는 전체의 아래에 배열합니다. 마찬가지로 나머지를 대표하는 가장 추상적인 카드를 위에 얹은 한 묶음의 카드로 추상화의 개선을 이끌어내거나 다룰 수 있습니다.

그림 2. CRC 카드로 설명한 스몰토크(Smalltalk)의 모델, 뷰, 컨트롤러의 책임과 협력자

설계가 불완전하거나 제대로 이해가 되지 않을 경우 인덱스 카드를 재빨리 재구성하고 직접 지시할 수 있는 점은 큰 장점입니다. 우리는 설계자들이 카드가 완성되면 놓일 장소를 지시하면서 그들이 작성하고자 하는 카드를 언급한다는 것을 자주 보았습니다.

카드를 이용해 설계를 하면 하향식이나 상향식 설계와는 달리 아는 내용에서 모르는 내용으로 진행하기 쉽습니다. 우리는 한 쪽은 디바이스 드라이버로, 다른 쪽은 고차원적인 모델에서 시작했던 두 팀이 거의 정반대의 과정을 거쳐 근본적으로 동일한 설계에 다다르는 것을 관찰하였습니다. 문제상황은 특정 종류의 능력이 요구되는 것으로 두 팀 모두 설계의 요구사항을 만족시키는 과정에서 해당 능력을 발견하였습니다.

우리는 실행 시나리오의 도움을 얻어 설계를 완성하도록 제안합니다. 하나 혹은 두 장의 명확한 카드로 시작하고, “만약에~ (what-if)” 질문을 수행합니다. 어떤 상황에서 기존의 객체들로 수행할 수 없는 책임이 요구되면 기존의 객체 중 하나에 책임을 추가하거나 해당 책임을 수행하는 새로운 객체를 생성합니다. 만약 이 과정을 진행하면서 객체 중 하나가 너무 혼잡해지면, 객체가 하는 일을 더 간결하고 명확하게 부를 수 있는 방법을 찾으면서 카드의 정보를 새로운 카드로 옮깁니다. 정보를 더 이상 줄여나가기 힘듦에도 객체가 여전히 너무 복잡하다면 일부 책임을 맡을 수 있는 새로운 객체를 생성합니다.

우리는 학습자들이 시나리오를 “실행”하는 동안 자신이 맡고 있는 역할에 해당하는 카드를 손으로 들어 올리도록 장려합니다. 설계자가 양 손에 카드를 들고 흔들면서 객체 간의 협업관계를 설명하고, 객체를 더 분명하게 식별하는 과정은 보기드문 일이 아닙니다.

우리는 가상의 미래에 필요한 내용을 충족시키기 보다는 당장의 요구사항에 따라 객체를 만드는 것이 중요하다고 강조합니다. 이를 통해 설계는 설계자가 직접 경험한 만큼만의 정보를 담게되고, 너무 일찍 복잡해지는 것을 막을 수 있습니다. 이 과정에서 팀으로 작업을 하면 도움이 됩니다. 왜냐하면 염려스러운 점이 생긴 설계자가 미심쩍은 약점이나 누락상황에 해당하는 시나리오를 제안함으로써 팀 구성원들에게 영향을 끼칠 수 있기 때문입니다.

4장. 경험

프로그래밍을 해본 경험은 있지만 매일 일로써 프로그래밍을 하지는 않는 컴퓨팅 전문가를 대상으로, “객체로 생각하기”라는 3시간 짜리 수업에서 CRC 카드를 사용하였습니다. 이 수업은 (학생을 가르치고 운영하는 학교와 같은) 데이터 흐름 예제를 소개하고, (교사, 관리인, 교장 등) 책임과 협업관계에 있는 객체들의 용어로 다시 기술해보는 과정으로 진행하였습니다. 그 후에 수업은 둘씩 짝지어 한 시간정도 현금인출기를 객체로 디자인해보는 시간을 가졌습니다. 현금인출기로 연습한 이유는 이 응용프로그램이 모두에게 친숙할 뿐만 아니라 기기 제어 부분, 중앙 은행의 데이터베이스와 통신하는 부분, 사용자 인터페이스를 제어하는 부분이 객체로 나눠져있기 때문입니다. (풀이 예제는 부록을 참고하십시오) 그 이후에 “클래스”, “인스턴스”, “메쏘드”, 그리고 “메시지”에 대한 용어 정의를 하였고, 다양한 객체지향 프로그래밍 언어의 기능과 역사에 대해 간단히 토론하는 것으로 수업을 마무리하였습니다.

수 백명의 학생을 대상으로 이 수업을 진행해보니 매 수업마다 보통 한 팀 정도는 시작하기 위해 약간의 힌트가 필요했지만, 그 외 모든 사람들은 도움 없이도 이 과정을 완수할 수 있었습니다. 후속 연구를 진행하지는 않았지만, 회사에서 이 수업은 가치있는 과정으로 생각되고 있고, 처음 이 과정이 개설된 이후 일 년이 되도록 많은 대기자와 함께 좋은 참석률을 보이고 있습니다.

우리는 숙련된 객체지향 프로그래머에게도 CRC 카드를 사용해볼 것을 부탁했습니다. 아직 완전한 방법론이라 말할 수는 없지만, 우리의 개인적인 경험을 통해 소프트웨어 엔지니어링에서 카드의 역할을 제안하는 바입니다. (CRC 방법의 장점을 얻을 수 있는 방법론을 더 충분히 개발한 곳도 있습니다. [5],[6]) 우리는 완성된 카드가 설계 문서의 일부로 고객에게 제공된 한 가지 사례를 알고 있습니다. 카드를 만든 팀은 설계에 대해 매우 만족했지만, 받는 입장에서는 카드의 맥락을 이해할 수 없었습니다.

또 다른 실험에서는 카드를 직접 만지고 토론하면서 만들어지는 맥락의 중요성에 대해 보여줍니다. 우리는 설계자들이 은행 시스템과 유사한 문제에 대해 일하는 장면을 촬영하였습니다. 카메라는 카드와 설계자들의 손은 보이지만 카드에 적은 내용은 보이지 않도록 배치했습니다. 영상 기록을 관람하는 사람들은 개발 과정을 따라가는 데 문제가 없었고, 종종 자신의 의견을 말하기 위해 영상을 멈추도록 요청했습니다. 가장 효과적인 순간은 관람자가 화면 정지영상의 흐릿한 카드를 손가락으로 지시하면서 설명할 때였습니다.

마지막으로, 우리는 복잡한 설계를 편리하게 설명하기 위해 CRC 카드를 사용하였습니다. 카드 기반의 발표를 위해서 청중들을 준비시키는 데에는 몇 분의 소개면 충분합니다. 카드는 미리 만들어두거나 혹은 현장에서 작성할 수 있습니다. 후자의 경우 설계의 복잡성이 천천히 드러나는데, 이 과정은 데이브 토마스의 “거짓말 관리 (lie management)“와 관련이 있습니다. 카드는 컴퓨테이션의 과정을 이야기할 때 도움을 주는 소품으로 사용됩니다. 카드를 이용하면 프로그래밍 언어의 구문이나 용어를 사용하지 않고도 이야기를 할 수 있습니다.

5장. 결론

우리의 관점을 기반으로 초보자와 숙련된 개발자들에게 객체에 관한 유용한 것을 가르치는 학습의 경험을 제공하였습니다. CRC 카드는 객체를 접하지 못한 학습자가 객체성에 대해 물리적으로 이해하도록 돕고, 특정 프로그래밍 언어의 어휘나 세부내용을 이해할 수 있도록 준비시켜줍니다. CRC 카드는 객체의 메커니즘을 배웠으나 아직 그 진가를 모르는 사람에게도 객체의 유용하고 납득할 수 있는 경험을 제공합니다.

라구 라그하반 (Ragu Raghavan [7]) 은 객체로 전향하면 뛰어난 프로그래머는 더 뛰어나 지지만, 서툰 개발자들은 뒤처진다고 말했습니다. 우리는 카드를 사용한 그룹활동에서 객체에 대한 깊은 이해가 없는 서툰 개발자들도 객체 설계에 기여할 수 있음을 발견하였습니다. 그 이유는 카드를 통한 설계가 훨씬 더 구체적이고, 객체들 사이의 논리적 관계가 뚜렷하기 때문에 설계를 이해, 평가, 및 수정하기가 더 쉽다고 생각합니다.

우리는 카드를 이리저리 물리적으로 움직이는 것의 유용성에도 놀랐습니다. 학습자가 객체를 손으로 들어 올릴 때 더 손쉽게 식별이 가능했고, 해당 객체의 관점에서 설계의 나머지 객체들을 다룰 수 있었습니다. 이것은 물리적 상호작용의 가치로 우리가 카드를 컴퓨터로 옮기지 않는 이유이기도 합니다.

단지 문제가 있다면 CRC 카드를 더 규모있는 설계 방법론이나 특정 언어 환경과 통합하는 것인데 이는 앞으로 가장 유망한 분야라 생각합니다. 물리적 상호작용의 가치를 유지하기 위해서는 현재의 시스템이 과거의 도구지향적 환경을 넘어서는 것처럼 현재 우리가 가진 것 이상의 새로운 종류의 사용자 인터페이스와 프로그래밍 환경이 필요합니다.

참고 문헌

[1] DeMarco, T.: Structured Analysis and Syhstem Specification, Yourdon, 1978.

[2] Smalltalk-80 image, Xerox Corp, 1983

[3] Hypercard manual, Apple Computer, Inc.

[4] Cunningham, W. and Beck, K.: “A Diagram for Object-Oriented Programs”, in Proceedings of OOPSLA-86, October 1986.

[5] Wirfs-Brock, R. and Wilkerson, B. “Object-Oriented Design: a Responsibility-Driven Approach”, submitted to OOPSLA ‘89.

[6] Reenskaug, T.: “A Methodology for the Design and Description of Complex, Object-Oriented Systems”, technical report, Center for Industrial Research, Oslo, Norway, November 1988.

[7] Raghavan, R.: “Panel: Experiences with Reusability”, in the Proceedings of OOPSLA ‘88, October, 1988.

부록

여기서는 4장에서 다뤘던 은행 시스템 문제에 대한 예제 풀이를 제공하겠습니다.

‘계좌 (Account)’ 와 ‘거래 (Transaction)’ 는 은행 모델을 제공합니다. ‘거래’는 돈이 나오는 동안에는 능동적 역할을 하고 그 이후에는 수동적 역할을 한다고 가정합니다.

‘거래’는 장치 드라이버 역할을 하는 여러 객체의 도움으로 책임을 다합니다. 예를 들어 ‘분배기 (Dispenser)’ 객체는 최종적으로 분배 장치를 동작시킵니다.

‘카드판독기 (CardReader)’ 객체는 은행카드의 마그네틱 선에서 정보를 읽고 해독합니다. 흔한 실수로는 은행 카드에 저장된 모든 정보를 조목별로 나열하는 것입니다. 카드의 부호화 형식은 분명히 잘 고려되고 문서화되어야 합니다. 그러나 객체 설계가 목적이라면 해당 정보가 프로그램의 어느 곳에 위치하는지만 파악하면 됩니다.

‘원격데이터베이스 (RemoteDataBase)’ 는 통신 라인을 운영하고 그 사이 전송되는 데이터를 해석합니다. 그것은 ‘계좌’ 객체를 생성하고 ‘거래’ 객체를 소모합니다.

장치 드라이버는 ‘이벤트 (Event)’ 객체를 공유 대기열에 추가하는 방식으로 예외적이거나 비동기적인 이벤트 신호를 보냅니다.

‘이벤트 (Events)‘는 화면의 작업 순서대로 ‘실행 (Action)‘을 유발시키면서 사용자 인터페이스를 동작시킵니다. 화면의 실제 형식과 순서는 사용자 인터페이스 설계에 따라 결정되며, 아마 은행마다 다를 것입니다. 우리는 상태기계(state-machine) 라인 인터페이스가 구성될 수 있는 객체를 제공합니다.

‘화면 (Screen)’ 객체는 상태에, ‘실행 (Action)’ 객체는 화면전환에 해당합니다. ‘화면’은 ‘실행’을 처리하는 방식에 따라 차이가 있을 수 있습니다. ‘실행’ 그 자체는 이벤트를 어떻게 처리하느냐에 따라 달라집니다. ‘실행’은 최종적으로 현금 인출기의 그 이후 동작을 위임하는 ‘거래 (Transaction)’ 를 구성합니다.

© Metakits 2018