SmalltalkObjectsandDesign:Chapter 04

From 흡혈양파의 번역工房
Jump to navigation Jump to search
제 4장 연습 - 기본 요소(foundations)

연습 - 기본 요소(foundations)

스몰토크는 매우 작아서 대부분의 본질적 요소들을 한 자리에서 실습할 수 있다. 이번 장은 이러한 기본 요소들을 다룬다-제 3장의 언어 요소들을 비롯해 스몰토크로 연습하기 위한 도구들이 포함된다. 이번 장은 직접 해봐야 하는 연습으로만 구성된다. 스몰토크는 직접 경험해봐야만 스몰토크에서의 객체 지향 프로그래밍에 독특한 점을 이해할 수 있을 것이다. 스몰토크를 사용해보지 않고 이 책 또는 유사 서적을 읽는다면 아무 것도 얻을 수 없다. 연습하고 둘러보고 실험해보고 호기심을 가져라. 연습을 위해서는 최소한 반나절 정도는 시간을 비우도록 한다.


경고

스몰토크 환경에 나타나는 무수한 메뉴와 옵션, 그리고 이용 가능한 코드 버전이나 관리 도구에 주의를 뺏기지 않도록 하라. 이러한 연습은 스몰토크에서 객체의 특성, 생존하기에 충분한 메뉴 옵션과 도구만 강조한다. 나머지는 경험과 자기발견을 통해 차츰차츰 얻게 될 것이다. 지금으로선 근본적인 개념적 문제에 직면하는 것이 기계적 기술을 숙달하기보다 더 가치가 있다.


예방책

제 1장에서 필자는 각 스몰토크 워크스테이션에는 "이미지" 라고 알려진 중요한 파일이 있다고 언급한 바 있다. 이미지는 실행 객체를 포함하므로 자라기도 하고 줄어들기도 한다. 이 파일의 이름은 스몰토크 dialect 마다 다양하지만, IBM Smalltalk 와 VisualAge 에서 기본 이름은 image로 단순하다. 그것은 객체와 그 메서드로 구성되어 있지만 소스 코드는 구성에 포함되지 않도록 상상해야 한다. 일부 dialect의 이미지에는 소스 코드가 나타나기도 하지만 이는 스몰토크의 내재적 특성이라기보다는 dialect의 인공물이라 할 수 있다. 소스 코드는 객체와 그들의 행위를 설명하는 데에 필요하지만 객체 자체의 일부는 아니다; 따라서 이미지의 필수 요소는 아니다.

물론 개발자들은 그들의 소스 코드에 대한 신뢰성 있는 복사본(copy)으로 접근할 필요가 있다. 스몰토크는 당신이 작성하는 코드를 텍스트 파일 또는 특수 라이브러리 또는 저장소(repository)로 저장한다. IBM Smalltalk 또는 VisualAge 의 표준 버전들은 각 워크스테이션에만 적용되는(local) changes.log 라고 불리는 텍스트 파일을 사용한다:

Chapter 04 01.png


(스몰토크 소스를 포함하는 changes.log 처럼 느낌표가 표시된 파일이 눈에 띌 것이다. 이 파일들은 메서드와 그 외 다른 코드 덩어리를 묘사하며, 소스 코드를 처리하는 도구들에 의해 자동으로 읽히고 삽입된다.)

이러한 제품의 단체 버전과 전문가 버전에서는 manager.dat 또는 abtmgr30.dat 과 같이 dat 를 수식자(qualifier)로 하는 공유 라이브러리를 이용한다:

Chapter 04 02.png


이미지가 X 클래스 자체를 비롯해 X 클래스의 인스턴스들을 포함하고 있음을 주목하라. 이는 스몰토크에선 클래스는 일급 객체임을 상기시켜준다. 클래스 X 는 C++ 에선 live 객체가 아닐 뿐더러 관련 이미지와도 상관이 없을 것이다.

이 논의의 요점은 이미지가 복잡한 개발 프로젝트에서는 종종 불가피하게 오류가 발생하는 경향이 있으므로, 이런 경우 두 가지가 필요하다는-온전한 백업 이미지와 신뢰성 있는 소스 코드의 저장소-것을 알리기 위함이다. 그리고 나면 자신의 작업을 백업 이미지와 결합함으로써 최신 이미지로 재구성할 수 있다. changes.log 또는 manager.dat 를 공들여 백업하고, 신뢰성 있는 이미지를 최소한 하나를 백업해야 한다는 것이 요점이다.


스몰토크에서 검색하기

무언가를 검색하는 것은 기본적인 생존 기술이다. 스몰토크 개발자들은 클래스나 메서드를 검색하는 것이 보통이다.


클래스

❏ 한 가지 정말 유용한 도구로 시스템에 모든 클래스를 표시하는 브라우저를 들 수 있다. 하나를 열려면 시스템 transcript 창의 메뉴바에서 Smalltalk tools > Browse Classes 를 선택하라. 클릭, 더블클릭, 스크롤을 이용해 이러한 브라우저에 익숙해지길 바란다; 아직은 코드를 읽지 않도록 한다. 몇 분간만 (그 이상은 소요하지 말라) Set 클래스의 검색을 시도하고, 실패할 경우 읽어라.

Chapter 04 03.png


❏ 계층구조에 깊게 중첩된 클래스를 검색 중이라면 이 브라우저의 알파벳순 리스트는 별 도움이 되지 않는다. 예를 들어, SetObject 의 직접적 자손이 아니므로 브라우저의 첫 번째 알파벳순 리스트에 표시되지 않을 것이다. 이를 검색하려면 브라우저의 메뉴바에서 Classes > Find Class 를 선택하라. 대화상자에서 Set 를 입력하고 OK 를 클릭하라.


메서드

at:put: 메서드의 모든 구현부(implementations)를 검색하길 원한다고 가정하자. 다시 transcript 에서 Smalltalk tools 메뉴를 선택하되, 이번엔 Browse Implementors 를 선택하라. 대화상자가 나타나면 at:put: 을 입력하고 OK를 클릭한다. 스몰토크에 존재하는 at:put:의 많은 구현부의 리스트가 나타난다. 하나를 선택해 소스 코드를 살펴보라; 아직은 소스 코드를 이해하기 위해 너무 노력하지 않아도 된다.

❏ 스몰토크에서 발생하는 모든 일은 궁극적으로 메시지를 통해서 이루어진다. 특히 메뉴에서 Smalltalk tools > Browse Implementors 를 순서대로 선택하면 아래 메시지를 실행하는 효과가 발생한다:

System implementors


메뉴를 사용하는 대신 transcript 에 이 메시지를 입력하고 강조하여 실행한 다음에 + 메시지의 모든 구현부 리스트를 생성하라.

덧붙여 transcript 는 메시지를 입력하고 실험하기에 분명한 장소이지만 실제로 스몰토크에선 어떤 텍스트로 된 창이든 입력하고 시도해볼 수 있다. 일반적으로 입력이 가능하면 실행도 가능하다. 메시지를 입력하고 실행한 창이 어떤 창이든 상관없이 스몰토크가 이미지에서 live 객체를 사용하도록 만드는 것이다.

❏ 이와 비슷하게 메시지를 전송하는 메서드를 모두 찾아낼 수 있는데, 그 방법은 아래를 실행하거나:

System senders


Smalltalk tools > Browse Senders를 선택하여 가능하다. 둘 중 하나를 이용해 at:put: 메시지의 모든 전송자(sender) 리스트를 생성한다. 그리고 나머지 기법을 이용해 factorial (재귀) 메시지의 모든 전송자 리스트를 생성한다.

❏ 가령 remove: 와 같은 메서드를 이해하려면 보통 그것이 차례로 호출하는 메서드를 이해할 필요가 있다. Collection 클래스의 remove: 를 알아보자. remove: 메서드들은 다수의 클래스에 존재하기 때문에 스몰토크 개발자들은 Collection의 메서드를 다른 클래스들의 메서드와 구별하기 위해 Collection>>remove: 표기를 사용한다. 리스트에서 Collection>>remove: 를 선택한 후 Methods 메뉴를 내려서 (또는 오른쪽 마우스 클릭으로 팝업시켜) Browse Message 메뉴 항목을 선택한 후, 그것의 Implementors 하위항목을 선택한다. 스몰토크는 remove: 가 전송하는 메시지 리스트를 가져올 것이다. 이번 사례에서는 두 개만 나타날 것이다. 둘 중 하나를 선택하면 그것의 모든 구현자(implementor) 리스트가 생성된다. 이 절차는 무기한으로 반복할 수 있다. 즉, Browse Messages > Implementors 를 반복 클릭하여 어떤 메서드든 그 구현부로 원하는 만큼 깊이 파고들 수 있다는 말이다.

❏ 서로 다른 두 가지에서 date가 발생하는지 시험하는 메서드에 위의 기법을 적용해보라. 다시 말해, Browse MessagesImplementors 를 교대로 선택함으로써 between:and:, <=, <, year 메서드 사슬의 구현부를 추적하라.

스몰토크의 요소들

전역 변수 구축하기

❏ 전역 변수 T 를 이미지에 추가하라:

Smalltalk declareVariable: #T.


이 메시지는 시스템 사전이라 불리는 곳에 T 에 대한 엔트리를 구축한다.

전역 변수는 일반적으로 피해야 한다. 전문 개발자들은 정보의 가시성을 제한하는 객체 지향의 정신에 모순되기 때문에 전역 변수의 사용을 삼가는데, 항상 다른 방법은 있기 마련이다. 하지만 전역 변수들은 생각을 표현하고 실험을 촉진하도록 도와줄 수 있으므로 이번 장을 시작하면서 몇 가지 사용해보고자 한다.


전역 변수 대 임시 변수

❏ Transcript 창에 이 코드 덩어리 전체를 표시하라 (공백은 각 문들을 구분하고, 마지막 공백은 선택적임을 기억하라):

T := 'dig' copy.
T at: 2 put: $o.
T.


❏ 아래의 코드로 실험을 반복하라.

|t|
t := 'dig' copy.
t at: 1 put: $p.
t.

❏ 이제 전역 변수 T표시하라. 그리고 임시 변수 t표시를 시도하라. 임시 변수 t 는 더 이상 정의되지 않는다; 그것의 선언


^ (탈자 기호)

❏ 스몰토크 메서드는 항시 어떤 객체를 리턴하도록 되어 있다. ^는 객체를 명시한다. 클래스 브라우저를 사용해 14 페이지에서 살펴봤던 Magnitude 클래스 하의 계층구조에 다시 익숙해지길 바란다. 선택자가 max: 인 메서드를 위치시켜 그 코드를 맞추어 읽어 보라. ^는 메서드가 리턴하는 객체를 앞선다. ^는 메서드의 실행을 종료하기도 한다.

많은 메서드들이 ^를 전혀 포함하지 않는다. 그럼에도 불구하고 메서드는 객체를 리턴해야 한다. 이러한 기본 상황의 경우, 그 객체는 메시지의 수신자인 self 가 된다. 메서드의 코드를 살펴보지 않는 한 ^의 존재 유무는 물론이고 ^의 위치도 알 수 없으므로 그것이 무엇을 리턴할지 확신할 수 없음을 명심하라. 곧 살펴보겠지만 때로는 리턴된 객체가 당신이 예상한 결과가 아닌 경우도 있다.


실행하기 대 표시하기

실행하기(Execute)표시하기(Display) 모두 강조된 코드를 컴파일 및 실행한다. 둘의 유일한 차이는 리턴된 객체를 처리하는 방식이다. 실행하기는 리턴된 객체를 무시하는 반면 표시하기는 화면에 내용을 인쇄한다.

❏ 새 전역 변수 W 를 선언하라. 그리고 아래를 실행하라:

W := 3.14  2 * 2.


다음으로 아래를 실행하라:

W


화면에 어떤 것도 나타나지 않는다. 하지만 표시하기를 통해 코드가 컴파일 및 실행되었음을 확인할 수 있다:

W


❏ 이와 비슷하게 아래를 실행하면:

Array with: 'Tolstoy' with: W


화면에 어떤 내용도 표시되지 않지만, 표시하기를 통해서는:

Array with: 'Tolstoy' with: W

결과가 되는 배열 인스턴스가 화면에 표시된다. (with:with: 클래스 메서드는 배열 인스턴스를 쉽게 생성하는 방법이다.)


❏ 마지막 예로 다음을 실행하라:

Transcript show: 'War and Peace'


이번 경우 실행의 효과로 transcript 창에 문자열이 반영된다. 하지만 이러한 효과를 리턴과 혼동하지 않길 바란다. 리턴된 객체는 무엇인가? 소스 코드 외에는 표시하기를 통해 알아낼 수 있다:

Transcript show: 'War and Peace'


동일한 효과가 발생할 것이며 스몰토크는 리턴된 객체-an EtTranscript-를, 즉 EtTranscript 클래스의 인스턴스에 대한 스몰토크의 설명을 인쇄할 것이다. 사실 이것이 Transcript 의 모습이다-EtTranscript의 인스턴스를 가리키는 전역 변수. 3

다음 연습은 여러 리턴값을 보여주는데, 그 중 하나는 거의 모든 사람들을 깜짝 놀라게 만들 것이다.


메시지

❏ 아래 메시지 각각으로부터 리턴값을 표시하라:

4 factorial
8 max: 5
8 between: 5 and: 7
#(vanilla ice cream) at: 2
#(vanilla 'ice' cream) at: 2
'milk and ' , 'honey'
'salt' at: 1 put: $m "Remember that literals are immutable"
'salt' copy at: 1 put: $m

마지막 메시지에서 리턴된 값은 대부분의 사람들을 놀라게 한다. 그들은 at:put: 이 메시지를 수신한 (수정된) 문자열을 리턴할 것으로 예상한다; 하지만 대신 $m argument를 리턴할 것이다! 이러한 경험은 리턴값을 항상 예상할 수••••는 없음을 강조한다. 메서드의 코드를 읽거나 메시지를 전송하는 실험을 해야만 한다.


❏ 마지막으로 아래를 표시하거나 실행할 경우:

#salt copy at: 1 put: $m


Walkback 창이 뜬다. 기호가 유일하기 때문에 오류가 발생하여 copy 메시지가 목표를 얻지 못한다; 변경할 수 없는 원본 리터럴 기호를 리턴한다.


파싱 순위(parsing precedence)

괄호로 덮어쓰지 않는 한 단항 메시지가 이항 메시지보다 우선하고, 이항 메시지는 키워드 메시지보다 우선한다.

❏ 이 규칙을 적용하여 아래에 대한 리턴값을 예상하고 확인하라:

#(6 5 4 3 2 1) at: 2*3


그리고

'oat bran' size * 4 between: 6 negated and: 3 factorial * 5


클래스와 인스턴스

❏ 26 페이지에서 빌드한 Animal 클래스의 계층구조를 살펴보라. 아래 메시지들의 리턴값을 예상하고 확인하라:

'oat bran' class
Penguin new class superclass
(2/7) class superclass superclass
String allInstances size
Penguin allInstances size


❏ 새 전역 변수 P 를 생성하고 P := Penguin new 를 실행하라. 아래의 리턴값을 표시하라:

P isKindOf: Animal
P isMemberOf: Animal


❏ 마지막으로 다시 표시하기를 통해 시스템 내의 펭귄(penguin)의 수를 세어라:

Penguin allInstances size

Inspector

P inspect를 실행하여 penguin 객체를 조사하라. 이 창을 inspector라고 부르는데, 그것의 엔트리를 클릭하면 penguin의 인스턴스 변수들의 값을 비롯해 그것의 슈퍼클래스로부터 상속되는 내용도 모두 볼 수 있다. 객체를 강조하여 Inspect 메뉴 옵션을 이용해 검사할 수도 있다. 이 기법도 사용해보길 권한다.


❏ 인스펙터(Inspector)를 이용해 복합 객체 구조의 여러 계층을 뚫어볼 수 있다. 예를 들어, 다음 객체를 검사해보라:

Array with: P with: 'ice cold' with: -273


그리고 배열의 세 가지 인스턴스 변수를 더블클릭하여 기본이 되는 객체들을 조사해보라.


계단식(Cascading) 메시지

스몰토크는 동일한 객체에게 메시지를 반복 전송함으로써 경제적인 구문을 제공한다. 아래와 같이 작성하는 대신:

SomeObject msg1.
SomeObject msg2.
SomeObject msg3.


아래와 같이 작성함으로써 메시지들을 계단식으로 배열할 수 있다:

SomeObject msg1;
	    msg2;

	    msg3.

계단식(Cascaded) 메시지는-메시지 뒤에 세미콜론이 붙는다-앞의 메시지가 전달된 것과 동일한 객체로 전달된다. 다른 방식으로 생각해보자: 첫 번째 세미콜론 앞에 마지막 메시지를 살펴보라; 그 메시지를 수신하는 객체가 무엇이든 그것이 다른 모든 계단식 메시지들도 수신한다.


❏ 아래를 실행하라:

Transcript cr;
		show: 'If I had a';
		cr;
		show: 'hammer'.

(cr 메시지는 창이 캐리지 리턴을 실행하도록 지시할 뿐이다.)


❏ 아래는 무엇을 리턴하는가:

5 + 2 * 3;
        +7; 
        +9.

이 코드를 표시하여 답을 확인하라.


❏ 간단하지만 손쉬운 메시지 yourself 는 그것이 어느 객체로 전송되든 그 객체를 리턴한다. yourself 를 계단식 메시지와 합치면 at:put: 의 효과를 볼 수 있는 간편한 방법을 제공한다. 아래를 표시해보라:

'salt' copy at: 1 put: $m;
    yourself


메서드 작성하기

❏ 앞서 준비한 동물(animal) 애플리케이션에 애플리케이션 브라우저를 가져오라. (앞에서와 마찬가지로 먼저 Manage Applications 에서 자신의 애플리케이션을 선택하고 마지막으로 Browse Application 을 누른다.)

Animal 클래스를 선택하고 instance/class 버튼이 instance 로 토글되었는지 확인한 후, Methods 메뉴를 내려서 New Method Template 를 선택하라. 아래와 같은 display: 메서드를 작성하라:

display: aString
       "Display aString in the transcript"
       Transcript cr;
             show: aString;
             cr.

Save 메뉴 옵션을 선택해 메서드를 컴파일하라.


❏ 이제 Animal 클래스에 talk 메서드를 작성하여 동물들이 'I have nothing to say' 라고 말하도록 하라. 이 메서드는 방금 작성한 메서드를 사용하며, 아래와 같이 단순하다:

talk
       "Speak tersely"
       self display: 'I have nothing to say'.


❏ 실행하기를 통해 작동하는지 시험하라:

P talk

P 가 여전히 당신의 펭귄이라고 가정할 때 응답은 'I have nothing to say' 가 될 것이다.


❏ Smalltalk/V 동물 계층구조에서와 같이 앵무새(parrot)의 행위를 약간 다르게 만들 것이다. 앵무새는 어휘를 가져야 한다. Parrot 클래스에 vocabulary 인스턴스 변수를 정의하라. 그리고 문자열을 이 인스턴스 변수로 할당하는 setVocabulary: 메서드를 작성하고 컴파일하라. 마지막으로 아래 코드를 이용해 앵무새에 대해 talk 메서드를 작성하라:

talk
       "Repeat my vocabulary"
       self display: vocabulary.


❏ 새 앵무새를 새 전역 변수 P2 로 할당하라. 이 앵무새에게 말을 하도록 요청하라. 이 응답을 이해하는가? 이제 P2 에게 setVocabulary: 메시지를 전송하여 어휘를 ('I want a cracker') 제공하라. P2 에게 말을 하도록 요청하라.


Animal 클래스에 문자열을 name 인스턴스 변수로 할당하는 setName: 메서드를 작성하고 컴파일하라.


MammalHuman 서브클래스를 생성하라. 새 사람(human)을 전역 변수 H 로 할당하라. H 에 이름이 있는가? (응답을 확인하기 위해 H 를 검사하라.) HsetName: 메시지를 전송하여 이름을 ('Claude Monet')부여하라. 마지막으로 talk 메서드를 작성하여 모든 사람이 'My name is _______' 라고 말하도록 하라. H가 자신의 이름을 적절하게 말하는지 확인하라.


특수 변수 self 와 super

❏ 사람에 대해 새 인스턴스 메서드 blab 을 작성하라. 이 메서드의 body는 다음과 같이 단순하다:

self talk.

H blab 의 실행 결과를 예측하고 확인하라. 변수 self 를 변수 super 로 대체하고 blab 을 재컴파일하라. 이제 H blab 을 실행하면 어떤 결과가 나타나는가?

설명하자면 다음과 같다. selfsuper 로 변경함으로써 스몰토크의 메서드 검색 규칙이 변한다. 객체의 클래스에서-이번 예에서는 Human-검색을 시작하는 대신, super 는 스몰토크로 하여금 super 를 포함하는 메서드의 슈퍼클래스에서 검색을 시작하도록 만든다. 이 예제에서 스몰토크는 Human 클래스에 talk 메서드를 우회하여 Mammal 클래스에서 검색을 시작한다. 따라서 super 는 일반적으로 서브클래스 메서드를 오버라이드하여 가리게 될 슈퍼클래스 메서드로 접근하는 방법인 셈이다. superself 모두 동일한 객체를 참조함을 주목하라; 유일한 차이는 메서드 검색의 시작점에 어떻게 영향을 미치는지에 있다.


Human 클래스 내 당신의 talk 메서드에 약간을 추가하여 사람들이 'I have nothing to say' 'My name is ____' 를 모두 말하도록 해보자.

이제 자신만의 코드를 어느 정도 개발했으니 이미지 저장을 통해 작업을 저장해야 한다. 시스템이 충돌할 경우 최근 저장된 이미지는 복구를 간소화하므로 신중하게 습관을 들여야 한다. 4


접근하는 변수

아래와 같은 클래스 계층구조를 고려해보자:

Chapter 04 04.png


Upu 라는 이름의 인스턴스 변수와 U 라는 클래스 변수를 정의하고, Downd 라는 인스턴스 변수와 D 라는 클래스 변수를 정의한다고 가정하자.

Up 의 인스턴스와 Down 의 인스턴스를 그려보라. Down 의 인스턴스 메서드들이 u 를 사용할 수 있는가? Up 의 인스턴스 메서드들은 d 를 사용할 수 있는가?

Down 의 인스턴스 메서드들이 U 를 사용할 수 있는가? Up 의 인스턴스 메서드들이 D 를 사용할 수 있는가?

Down 의 클래스 메서드들이 u 또는 U 를 사용할 수 있는가? Up 의 클래스 메서드들이 d 또는 D 를 사용할 수 있는가?


답이 확실치 않다면 완전히 새로운 애플리케이션을 생성하고 실험을 해보라. (새 애플리케이션을 생성하려면 Application Manager를 사용한다. 메뉴를 열고 Applications > Create 를 선택하라.)

블록 [...]

스몰토크에서는 다른 모든 것과 마찬가지로 블록도 (때로는 컨텍스트라고 부르기도 한다) 객체이다. 이는 대괄호 사이에 코드를 위치시켜 정의된다 [...]. 하지만 블록이 나타내는 코드가 전혀 실행되지 않을 수도 있기 때문에 대부분의 객체보다 신기하다. 한편 블록을 변수로 할당하여 메시지의 argument로서 전달하고, 블록에 메시지를 전송하는 것도 가능하다-짧게 말해 다른 스몰토크 객체처럼 취급이 가능하단 의미다. 블록은 실행에 대한 명시적인 요청을 받을 때에만 실행될 것이다. 예를 들어보겠다.

❏ 아래를 실행하면:

X := [ H talk ]


변수 X 로의 할당이 발생하지만, 말하기에 관한 코드는 실행되지 않는다. 아래와 같은 메시지를 전송할 때에만:

X value

코드가 실제로 실행된다.


❏ 블록은 조건부 메시지에서 자주 발생한다. 아래를 실행해보라:

(H isKindOf: Mammal)
		ifTrue: X


그리고 아래를 실행해보라:

(H isKindOf: Mammal)
		ifTrue: [ P2 talk ]
		ifFalse: [ H talk ]


❏ 루프에서도 발생한다. 아래를 실행해보라:

6 timesRepeat: [ P2 talk ]


❏ 구문을 약간만 비꼬면 블록이 argument를 가질 수도 있다. 예를 들어, 세트(set) 내의 모든 요소들을 곱하기 위해서는 아래를 표시하라 (전역 변수를 선언해야 함을 잊지 말라):

MyProduct := 1.
S := Set with: 5 with: 3 with: 4.
S do: [ :number  MyProduct := MyProduct * number].
MyProduct


do: 메서드는 가장 흔한 루핑 방법 중 하나이다. 이는 S 의 모든 요소를 반복하여 이를 블록 내 변수 number 로 한 번에 하나씩 대체한다. 이 변수의 이름은 임의적이다; :number 로서 :n 또는 :element 라고 불러도 된다.


클래스 일자(Class Date)

Date 객체는 애플리케이션에서 자주 발생한다. Date 클래스는 드물게 발생하는데, 다른 클래스에 비해 인스턴스를 생성하기 위한 클래스 메서드가 훨씬 더 많기 때문이다. 예를 들어, 1부터 366까지 일(day)과 하나의 연도(year)을 알고 있을 경우 newDay:year: 클래스 메서드는 이 일자에 대한 date 인스턴스를 생성할 수 있다. 일, 월, 연으로부터 하루를 알고 있다면 newDay:month:year: 클래스 메서드가 그 일자에 대한 date 인스턴스를 생성할 수 있다.

today 메시지를 Date 로 전송하여 리턴된 객체를 표시하라.

newDay:year: 메서드를 이용해 December 31, 1999를 생성하라; 이를 전역 변수 X 로 할당하라.

newDay:month:year: 클래스 메서드를 이용해 자신의 생년월일을 생성하라; 이를 전역 변수 Y 로 할당하라. 먼저 메서드 내 주석을 읽고 그것의 argument 로 기대되는 형태를 결정하라.

X > Y 가 합리적인 객체를 리턴하는지 확인하라.


어떤 스몰토크 객체든 텍스트로 렌더링하기

printString 메시지는 어떤 객체로부터의 문자열이 그다지 많은 정보를 포함하지 않는다 하더라도 해당 문자열을 리턴하고자 시도한다. 몇 가지 예를 들어보겠다:

❏ 객체를 문자열로 렌더링하는 기능이 얼마나 유용한지 확인하려면 표시하기를 이용해:

'The bird is ' , P2 printString


표시하기를 대조해(contrast)보라:

'The bird is ' , P2


❏ 이와 비슷하게 표시하기를 이용해:

Y printString ,  ' is my birthday'


표시하기를 contrast하라:

Y , ' is my birthday'


많은 스몰토크 구현부(implementations)는 하나의 printString 메서드를 가지는데, 이는 Object 클래스에 위치한다. IBM Smalltalk는 많지는 않으나 여러 개의 printString 구현부를 갖고 있다. (얼마나 많을까?) 또 printString 는 어떻게 모든 종류의 객체를 실제로 의미 있는 텍스트 문자열로 렌더링할만큼 풍부할까? 사실 전혀 풍부하지 않다. 대신 그것은 다수의 클래스에서 개별적으로 구현되는 다른 메서드를 호출한다; 객체들을 문자열로 렌더링하는 작업의 핵심을 실행하는 것은 이렇게 호출하는 메서드이지, printString 이 아니다.

이 메서드를 찾아라. 얼마나 많은 구현부가 있는가? (수백 개가 있을 것이다!)


스몰토크의 디버거(debugger)

디버깅(debugging)

❏ 새 애플리케이션을 생성한 후 (또는 자신의 애플리케이션 중 하나를 사용) Object 의 서브클래스 AAA 를 생성하여, 그것의 인스턴스 변수는 iv 로 하고 그 인스턴스 메서드 m 은 아래를 body 로 하라:

iv := 4.
self badMessage.
Transcript show: 'Done'.


아래를 실행하라:

AAA new m


이 때 나타나는 디버거 창을 walkback 이라 부르는데, 상단 좌측 윈도우패인에 표시되는 리스트는 오류로 이끄는 메서드 호출을 스크롤하거나 "돌아갈(walk back)"수 있도록 해주기 때문에 그렇게 부른다. 각 행은 하나의 클래스와 메서드를 나타낸다. 메서드가 서로를 호출하는 순서대로 되어 있으며, 피호출자(callee) 아래에 호출자(caller)가 위치한다. 언제든 오류가 발생 시 메서드 호출의 frozen stack을 보여준다.

Chapter 04 05.png


AAA>>#m 행은 당신이 작성한 메서드를 나타낸다. 이 행을 선택하면, 위에서 보이는 바와 같이 큰 윈도우패인에 당신의 코드를 표시한다. (AAA(XXX)>>#mmm 표기는 객체가 AAA 의 인스턴스라 할지라도 mmm 메서드는 슈퍼클래스 XXX 로부터 상속됨을 의미한다. 도구의 변덕에 따라 #는 표기에 포함되기도, 빠지기도 한다.)

❏ 중간의 윈도우패인에서 self 를 더블 클릭하여 현재 객체를 (AAA의 인스턴스) 조사하라. 인스펙터가 인스턴스 변수 iv 의 현재 값을 결정하는 곳으로부터 객체를 연다.

❏ 브라우저를 사용할 필요없이 디버거에서 바로 문제를 해결할 수 있다: 큰 윈도우패인에서 오류가 발생한 메시지를 제거하고 저장(재컴파일)을 하라. Walkback 창을 닫고 아래를 이용해 올바르게 실행되는지 확인하라:

AAA new m


단계별로 실행 제어하기

디버거가 실행 스택(stack)을 freeze하는 즉시 당신은 into 버튼 (현재 강조된 메시지로 떨어져 내리는 즉시 중단), over 버튼 (강조된 메시지가 완전히 실행된 후 중단), return 버튼 (현재 메서드를 완료한 후, 그것의 호출 메서드로 리턴하기 직전에 중단), 또는 resume 버튼을 (가능한 한 멀리 실행) 이용해 실행을 계속할 수 있다.

❏ 앞의 예제에서 self badMessageself halt 로 대체한 후 다시 AAA new m 을 실행하라. 이번에는 버튼을 이용해 실험해보라.


도발(provocation)

앨리어싱(Aliasing)

❏ 아래를 표시하거나 검사하라:

|r p|
r := Rectangle origin: 20@20 extent: 10@10.
p := r corner.
"p x: 50; y: 50."
r


이제 인용 부호를 제거하고 다시 표시하거나(display) 검사하라(inspect). r 이 변경될 것으로 예상했는가? pr 의 모서리(corner)를 참조한다-p 는 모서리점의 앨리어스(별명)이다. 따라서 p 를 변경하면 r 자체가 변한다. 스몰토크에서는 변수들이 포인터라는 구문적 신호는 없으나 실제로 포인터이기 때문에 앨리어싱이 발생한다.

❏ 3행을 p := r corner copy 로 교체하고 다시 표시하거나 검사하라. 이번에는 r 이 영향을 받지 않는다. 이것은 p 가 더 이상 모서리점의 앨리어스가 아니라 모서리점의 복사본으로서 구분된 객체이기 때문이다.

명시적인 포인터로 된 언어에서 프로그램에 해를 입히는 포인터 버그로부터 스몰토크는 프로그래머들을 보호하지만 이를 위해선 관습적 언어보다 덜 분명한 앨리어싱을 만들어야 하는 희생을 치뤄야 한다.

간결한 재사용

❏ 아래 body 를 이용해 AAA 클래스에 foobar 메서드를 작성하라:

"... Lots of code ..."
Transcript show: 'To be or not to be'.
"... Lots of code ..."


AAA 의 서브클래스 BBB 를 생성하라. BBB 의 인스턴스들이 'To be or not to be' 대신 'My kingdom for a horse' foobar 메시지에 응답하는 것만 제외하고 AAA 의 인스턴스들과 동일하게 행동하도록 만들어라. 기본 규칙은 코드를 복사하지 않는 것이다-예를 들어, "...Lots of code..." 를 다른 메서드로 복사하면 재사용에 간결한 형태가 아니다. 대신 AAA 에서 foobar 메서드를 수정하고 AAABBB 에 완전히 새로운 메서드를 정의하는 편을 고려해보라.

누구도 코드가 어떻게 사용 또는 재사용될지 보통은 예측할 수 없기 때문에-AAA>>foobar 가 작동된다는 사실을 알기 전까지는 BBB>>foobar 에 대한 요구조건을 알지 못했다-누구도 수많은 시험과 오류 없이는 재사용 가능한 디자인을 생성할 것이라 예측할 수 없다. "재사용을 시도해본 후에야 무엇이 잘못되었는지 발견할 수 있다" [Sarkela 1989].


Notes