SqueakByExample:8.1: Difference between revisions

From 흡혈양파의 번역工房
Jump to navigation Jump to search
(SBE 오브젝트 페이지 추가)
 
(용어수정)
 
(35 intermediate revisions by the same user not shown)
Line 1: Line 1:
==오브젝트 (Object)==
==Object==


'''오브젝트'''는 모든 의도와 목적에 있어 상속 계층도(the inheritance hierarchy)의 근간(root)입니다. 실제로 스몰토크에서, 상속도의 참된 근간은(the true root) ProtoObject이며, 이것은 오브젝트로서 가장된 최소한의 엔티티들(minimal entities)을 정의하기 위해 사용되지만, 우리는 지금 이러한 기능을 무시할 수 있습니다.
어떠한 의도 또는 목적에 대해서도 '''Object''' 는 상속 계층의 뿌리<sup>root</sup>(또는 최하위 계층) 가 됩니다. 실제로 스몰토크에서, 상속도의 진짜 최하위 계층은 ProtoObject 이며, ProtoObject 는 객체로서 가장된 최소한의 엔티티를 정의하기 위해 사용되지만, 지금의 스몰토크는 이런 ProtoObject 에 대한 내용을 크게 신경쓰지 않아도 됩니다.


오브젝트는 커널-오브젝트 카테고리(the Kernel-Objects category)에서 발견될 수 있습니다. 놀랍게도, 여기서 발견될 (확장포함) 400개의 메소드가 있습니다. 다른 말로 하면, 여러분이 정의하는 모든 클래스는 여러분이 그 메소드들을 아는 여부와 상관 없이 400개의 메소드를 자동으로 제공한다는 것입니다. 이 메소드들의 몇몇은 반드시 제거되야 하며 스퀵의 새로운 버전은 아마도 더 이상 필요하지 않은 메소드들을 제거할 수도 있습니다.
Obejct 는 kernel-Object 카테고리에서 발견할 수 있습니다. 놀랍게도, 여기에서는 400개의 메서드(확장을 포함한)를 발견할 수 있습니다. 달리 말한다면, 스몰토크에서 사용자가 정의하는 모든 클래스는 사용자가 이런 메서드들을 아는지와는 상관없이 400개의 메서드를 자동으로 제공한다는 의미가 됩니다. 몇몇메서드가 없을수도 있는데 새버전의 Squeak 에서는 몇몇 필요없는 메서드를 제거할 수도 있기 때문이라는 것에 주의해야 합니다.




오브젝트 상태(the object states)에 대한 클래스 주석:<br>
''오브젝트는 클래스 계층도에 있는 거의 모든 다른 클래스들을 위한 루트 클래스(the root class)입니다. 예외는 {{Template:HighlightBold|ProtoObject}}(오브젝트의 superclass)와 그것의 서브클래스들(subclases)입니다. 클래스 {{Template:HighlightBold|오브젝트(Object)}}는 접근(access), 복사(copying), 비교(comparison), 에러 헨들링(error handling), 메시지 발송(message sending), 그리고 반영(reflection)과 같은 모든 보통 {{Template:HighlightBold|오브젝트(Object)}}에 공통인 디폴트 동작(default behaviour)을 제공합니다. 또한 모든 오브젝트들이 응답해야할 유틸리티 메시지들(utility messages)은 이곳에서 정의됩니다. 오브젝트는 인스턴스 변수를 갖고 있지 않을 뿐만 아니라, 어떤 것도 그 자체에 추가될 수 없습니다. 이러한 현상은 오브젝트의 여러 클래스들이 특별한 실행(예를 들면 {{Template:HighlightBold|smallInteger}} 와 {{Template:HighlightBold|UndefinedObject}})을 갖는 오브젝트로부터 상속되었기 때문이거나 또는 VM이 특정한 표준 클래스(standard classes)의 구조와 레이아웃에 관해 알거나 기초해 있기 때문입니다.''


Object 클래스에 적혀있는 주석:


만약 우리가 오브젝트의 인스턴스 사이드(the instance side)에서 메소드 카테고리 검색을 시작한다면, 그것이 제공하는 매우 중요한 동작의 몇 가지들을 보기 시작할 것입니다.  
''Object 는 클래스 계층도에 속한 대부분의 다른 클래스들을 위한 루트 클래스<sup>the root class</sup>입니다. 예외가 있다면 {{Template:HighlightBold|ProtoObject}}(Object 의 superclass)와 그것의 서브클래스들<sup>subclases</sup>입니다. {{Template:HighlightBold|Object}} 클래스는 접근, 복사, 비교, 오류 처리, 메시지 전송, 그리고 반영과 같은 모든 일반적인 객체에 공통으로 적용되는 기본 동작을 제공합니다. 또한 모든 객체들이 응답해야할 유틸리티 메시지는 이 곳에서 정의됩니다. Object 는 인스턴스 변수를 가지고 있지 않으며 그것(인스턴스변수)를 추가해야할 필요도 없습니다. 이런 특성은 Object 의 여러 클래스들이 특별한 실행(예를 들면 {{Template:HighlightBold|smallInteger}} 와 {{Template:HighlightBold|UndefinedObject}})을 가지는 Object 부터 상속되었기 때문이거나, 또는 VM 이 특정한 표준 클래스의 구조와 레이아웃에 관해 알고있거나 그것들을 기반으로 하기 때문입니다.''


===인쇄(Printing)===


스몰토크에서 모든 오브젝트들은 그 자체의 인쇄된 폼(a printed form)을 리턴할 있습니다. 여러분은 워크스페이스에서 모든 표현식(expression)을 선택할 수 있고 {{Template:HighlightGray|print it}} 메뉴를 선택할 수 있습니다: 이 동작은 표현식을 실행하고 리턴된 오브젝트에게 그 자체를 인쇄하도록 요청합니다. 사실 이 동작은 리턴된 오브젝트에 메시지 printString을 발송합니다. 메소드 printString은 템플릿 메소드(a template method)이며 그 자체 코어는 메시지 printOn:을 그 자신의 수신자(receiver)에 발송합니다. 메시지 printOn:은 특화될 수 있는 hook입니다.  
만약 Object 의 instance side 에서 메서드 카테고리 탐색을 시작하면, 여기서 제공되는 몇가지 중요한 동작을 발견하기 시작할 있을겁니다.


'''Object»printOn:'''은 여러분이 가장 빈번하게 재지정(override)할 수 있을만한 메소드들 중의 하나일 가능성이 높습니다. 이 메소드는 오브젝트의 문자열 표현이 작성될 그 메소드의 인수로서 a Stream을 취합니다. 디폴트 실행(the default implementation)은 단순히 “a” 또는 “an” 뒤에 클래스 이름을 적습니다. Object»printString은 작성된 문자열을 리턴 합니다.


예를 들면, 클래스 브라우저(the class Browser)는 메소드 printOn:을 재정의 하지(redefine) 않으며 메시지 printSting을 오브젝트에서 정의된 메소드들을 실행하는 인스턴스에 발송합니다.
===출력(Printing)===


스몰토크에서 모든 객체들은 그 자체의 출력 폼을 반환할 수 있습니다. 사용자는 워크스페이스에서 모든 표현식을 선택할 수 있고 {{Template:HighlightGray|print it}} 메뉴를 선택할 수 있습니다: 이런 동작들은 프로그램식을 실행하고 반환된 객체에게 그 자체를 출력하도록 요청합니다. 사실 출력에 대한 동작은 반환된 객체에 printString 메시지을 전송합니다. 메서드 printString 은 템플릿 메서드이며 그 핵심은 메시지 printOn: 을 그 자신의 수신자(receiver)에 발송하는것에 있습니다. printOn: 은 hook 메서드 로서 특별하게 취급됩니다.
'''Object>>printOn:''' 은 사용자가 가장 빈번하게 오버라이딩 하게되는 메서드중 하나가 될겁니다. printOn 메서드는 인자로 Stream 을 받은후, 받은 인자를 이용해서 String 으로 객체의 이름을 표현하는것을 만들어냅니다. printOn 의 기본적인 구현은 "a" 또는 "an" 뒤에 클래스 이름을 사용하는것 밖에 없습니다. Object>>printString 은 이렇게 만들어진 문자열을 반환 합니다.<ref name="역자주1">http://ta.onionmixer.net/wordpress/?p=215 페이지를 참고해주세요</ref>
예를 들어보면, 클래스 브라우저는 printOn: 메서드를 재정의 하지 않으며, printSting 메시지를 Object 에서 정의된 메서드들을 실행하는 인스턴스로 전송합니다.


<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Line 26: Line 28:




클래스 TTCFont는 PrintOn: 특별화(specialization)의 예시를 보여드립니다.  
TTCFont 클래스에서 PrintOn: 특수화에 대한 좋은 예를 확인할 수 있습니다. 아래의 코드에서 볼 수 있듯이 클래스의 인스턴스를 출력할때, 폰트의 모음 이름, 크기 그리고 종속 모음 이름이 클래스 이름의 뒤에 같이 출력됩니다.
클래스의 인스턴스를 인쇄하는 아래의 코드에 보이는 것처럼, 폰트의 가족 이름(the family name), 크기(the size) 그리고 종속 가족 이름(subfamily name)이 클래스 이름 뒤를 잇습니다.  
   
   


메소드 8.1: printOn: 재정의
메서드 8.1: printOn: 메서드의 재정의
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
TTCFont»printOn: aStream
TTCFont>>printOn: aStream
   aStream nextPutAll: 'TTCFont(';
   aStream nextPutAll: 'TTCFont(';
   nextPutAll: self familyName; space;
   nextPutAll: self familyName; space;
Line 39: Line 40:
   nextPut: $)
   nextPut: $)
</syntaxhighlight>
</syntaxhighlight>


<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Line 44: Line 46:
</syntaxhighlight>
</syntaxhighlight>


메시지 PrintOn:은 storeOn:과 동일하지 않다는 것에 주의합니다. 메시지 StoreOn은 수신자(the receiver)를 다시 만들기 위해 사용될 수 있는 표현식을 그 자체의(Storeon의)인수에 넣습니다. 이 표현식은 메시지 readFrom을 사용하여 읽힐 때 평가됩니다. pringOn:은 단지 수신자의 원문의 버전(a textual version)을 리턴합니다. 물론 원문의 표현(textual representation)이 자체 평가 표현식(a self-evaluating expression)으로서 수신자를 나타내는 일이 생길 수 있습니다.


메시지 PrintOn: 은 storeOn: 과 같지 않다는것에 주의해주세요. storeOn: 메시지는 수신자를 재생성하는데 사용할 수 있는 프로그램식을 그 자체의(storeOn의)인자에 stream 으로 대입합니다. 이렇게 만들어진 프로그램식은 readFrom 메시지를 사용해서 읽어들일때 처리됩니다. pringOn: 은 단지 수신자의 텍스트 버전을 반환할 뿐입니다. 물론 텍스트로 만들어진 프로그램식이 수신자를 표현하는 자체 처리 프로그램식이 될 수도 있습니다.


'''표현과 자체 평가 표현에(representation and self-evaluating representation) 관한 단어''' 기능 프로그래밍에서, 표현식은 실행될 때 값을 리턴합니다. 스몰토크에서, 메시지들은 (표현식) 오브젝트(값)을 리턴합니다. 몇몇 오브젝트는 값이 그 자체들인 훌륭한 속성(properties)들을 갖고 있습니다. 예를 들면, 오브젝트 true의 값은 예컨데 오브젝트 true인 자체입니다. 우리는 이러한 오브젝트를 자체 평가 오브젝트(self-evaluating object)라 부릅니다. 여러분은 워크스페이스에서 오브젝트를 인쇄할 오브젝트의 인쇄된 버전을 볼 수 있습니다. 여기에 이러한 자체 평가 표현식의 예시가 있습니다.
 
'''표현식과 자체평가표현식 이라는 단어에 대해서''' 함수 프로그래밍에서, 프로그램식은 실행이 될 때 값을 반환합니다. 스몰토크에서, 메시지(프로그램식)들은  객체(값<sup>value</sup>)를 반환합니다. 그 자신이 스스로 값으로 인식되는 좋은 속성들을 가지는 객체들이 있습니다. 예를 들면, 객체 true 의 값은 그 자신으로서 true 라는 객체가 됩니다. 이러한 객체들을 자체평가 객체<sup>self-evaluating objects</sup> 라 부릅니다. 워크스페이스에서 객체를 출력(print it)할 객체의 출력용 버전을 볼 수 있습니다. 여기에 위에서 설명한 자체평가 프로그램식의 예제가 있습니다.


{| style="border: 1px solid black;"
{| style="border: 1px solid black;"
Line 61: Line 64:




배열(arrays)과 같은 몇몇 오브젝트는 자체 평가이거나 또는 그것들이 포함하고 있는 오브젝트에 의존하지 않는 다는 사실에 주의합니다. 예를 들면, 불리언(Booleans)배열은 자체평가인 반면 persons의 배열은 그렇지 않습니다. 스퀵 3.9에서 메커니즘은 가능한 많이 자체 평가 폼(self-evaluating forms)속에 컬렉션들(collections)을 인쇄하기 위해 소개되었고, 이는 특별히 중괄호 배열에 있어 참입니다. 다음 예시는 동적 배열의 구성요소들일 경우에만, 동적 배열이 자체평가가 되는 것을 보여드립니다.
배열과 같은 몇몇 객체는 자체평가 또는 포함하고 있는 객체에 의존하지 않는다는 사실을 주의해주세요. 예를 들면, Boolean 으로 만들어진 배열은 자체평가가 되지면 persons 의 배열은 그렇지 않다는거죠. 스퀵 3.9에서 이러한 동작방식은 가능한 많이 (isSelfEvaluating 메시지처럼) 자체평가 형태로 컬렉션을 출력하기 위해 도입되었으며, 이는 특별히 중괄호 배열에 있어 true 가 됩니다. 다음 예제는 동적 배열의 구성요소들일 경우에만, 동적 배열이 자체 처리됨을 보여드립니다. 다음 예제는 동적배열의 구성요소가 자체평가형인 경우 동적배열이 자체평가로 처리됨을 보여줍니다:
 


{| style="border: 1px solid black;"
{| style="border: 1px solid black;"
Line 72: Line 74:




리터럴 배열(literal arrays)들이 오직 리터럴만(literal) 포함한다는 사실을 기억하십시오. 그러므로 다음 배열은 두 개의 점(two points)를 포함하지 않으며 여섯개의 리터럴 구성요소만을 포함합니다.
리터럴 배열은 오직 리터럴만 포함할 수 있다는걸 기억해주세요. 그렇기 때문에 다음의 배열은 두 개의 점(point)을 가지지 않으며 여섯개의 리터럴 요소만을 가지게 됩니다.
 


{| style="border: 1px solid black;"
{| style="border: 1px solid black;"
Line 81: Line 82:




많은 printOn: 메소드 특별화들은(method specializations)는 자체 평가 동작을 실행합니다. Point»printOn:와 Interval»printOn:의 실행은 자체 평가입니다
printOn: 메서드의 대부분은 자체평가를 구현하는 특수화를 진행하게 됩니다. Point>>printOn: 과 Interval>>printOn: 의 구현은 자체평가방식이 됩니다.




메소드 8.2: Point의 자체 평가(Self-evaluation)
메서드 8.2: Point의 자체평가
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Point»printOn: aStream
Point>>printOn: aStream
   "The receiver prints on aStream in terms of infix notation."
   "The receiver prints on aStream in terms of infix notation."
   x printOn: aStream.
   x printOn: aStream.
Line 94: Line 95:




메소드 8.3:인터벌(Interval)의 자체평가(Self-evaluation)
메서드 8.3:인터벌(Interval)의 자체평가(Self-evaluation)
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Interval»printOn: aStream
Interval>>printOn: aStream
   aStream nextPut: $(;
   aStream nextPut: $(;
     print: start;
     print: start;
Line 104: Line 105:
   aStream nextPut: $)
   aStream nextPut: $)
</syntaxhighlight>
</syntaxhighlight>


<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Line 110: Line 112:




===정체성(identity)과 동등성(equality)===


스몰토크에서, 메시지=테스트 오브젝트의 동등성(예컨데, 두 개의 오브젝트가 동일한 값을 나타내는 것과 상관없이)이며, 반면에 ==는 오브젝트 정체성을 테스트합니다. (예컨데 두 개의 표현식이 동일한 오브젝트를 나타내는 것과 상관없이)
===정체성(Identity)과 동일성(equality)===


스몰토크에서 '''=''' 메시지는 객체가 같은지(동일성)를 테스트하며(예: 두개의 객체가 같은값을 가지는지에 대해), '''==''' 메시지는 객체의 정체성(identity)을 테스트 합니다(예: 두개의 프로그램식이 같은 객체를 나타내는지에 대한 부분)


오브젝트 동등성의 디폴트 실행은 오브젝트 정체성을 위한 테스트를 시행하는 것입니다.


객체 동일성 비교에 대한 기본구현은 객체의 정체성에 대한 테스트입니다.


메소드 8.4: 오브젝트 동등성(Object equality)
메서드 8.4: 객체의 동일성
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Object»= anObject
Object>>= anObject
   "Answer whether the receiver and the argument represent the same object.
   "Answer whether the receiver and the argument represent the same object.
   If = is redefined in any subclass, consider also redefining the message hash."
   If = is redefined in any subclass, consider also redefining the message hash."
Line 127: Line 129:




이것은 여러분이 빈번하게 재지정(override)하기를 원하는 메소드 입니다. 복합적인 숫자들(numbers)의 사례를 고려해 보십시오:
아래에 보이는것은 자주 오버라이드 하게될 메서드 입니다. 복소수의 경우를 생각해주세요:
 


{| style="border: 1px solid black;"
{| style="border: 1px solid black;"
Line 137: Line 138:
|}
|}


 
이렇게 동작하는 이유는, Complex 가 '''=''' 를 다음과 같이 오버라이드(override) 하고 있기 때문입니다.
이것은 작동합니다. 그 이유는 복합적인 재지정(complex override)이 다음과 같기 때문입니다




메소드 8.5: 복합 숫자들(complex numbers)를 위한 동등성(Equlity)
메서드 8.5: 복소수를 위한 동일성
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Complex»= anObject
Complex>>= anObject
   anObject isComplex
   anObject isComplex
     ifTrue: [↑ (real = anObject real) & (imaginary = anObject imaginary)]
     ifTrue: [↑ (real = anObject real) & (imaginary = anObject imaginary)]
Line 150: Line 150:




Object»~=의 실행은 단순히 Object»=를 무효로 하며, 일반적인 상황에서 변경될 필요가 없습니다.  
'''Object>>∼=''' 기본구현은 '''Object>>=''' 을 부정하는 의미가 됩니다. 일반적으로 변경해야할 필요는 없습니다.


<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Line 157: Line 157:




만약 여러분이 =를 재지정하면, hash 재지정을 고려해야만 합니다. 만약 여러분의 클래스의 인스턴스가 딕셔너리에서 키들(keys)로 사용된 적이 없다면, 인스턴스를 동일한 hash 값을 갖는 것과 동등하게 고려가되도록 제작해야만 합니다:
만약 '''=''' 을 오버라이드 한다면, hash 를 사용한 부분 또한 오버라이드 하지 않으면 안됩니다. 클래스의 인스턴스가 Dictionary의 키로서 사용되었을 경우에, "같다"고 보이는 인스턴스는, hash값도 같아야 합니다:
 


메소드 8.6: hash는 반드시 복합 숫자들을 위해 재실행되어야 합니다.
메서드 8.6: hash는 반드시 복소수등을 위해 재실행되어야 합니다.
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Complex»hash
Complex>>hash
   "Hash is reimplemented because = is implemented."
   "Hash is reimplemented because = is implemented."
   ↑ real hash bitXor: imaginary hash.
   ↑ real hash bitXor: imaginary hash.
Line 168: Line 167:




비록 여러분이 =과 hash를 함께 재지정 해야만 할지라도, ==는 결코 재지정하면 안됩니다. (오브젝트 정체성의 의미론은 모든 클래스에 동일합니다) ==은 ProtoObject의 프리미티브 메소드(primitive method)입니다.
비록 '''=''' bash 를 함께 오버라이드 해야한다고 해도, '''==''' 를 재지정하면 안됩니다. (객체의 identity 에 대한 의미는 모든 클래스에 동일합니다) '''==''' 기호는 ProtoObject 의 프리미티브 메서드 이기 때문입니다.


스퀵이 다른 스몰토크와 비교하여 몇 가지 이상한 동작(behaviour)를 갖고 있는 것에 주목해 주십시오: 예를 들면, 심볼(symbol)문자열(string)동등해 질 수 없습니다 [우리는 이것을 bug로 고려하며, 특성(feature)으로 고려하지 않습니다]
 
스퀵이 다른 스몰토크 구현과는 다른 몇가지 익숙하지 않은 동작을 한다는것에 주의해주세요: 예를들면 symbol 과 string 은 동일하지 않습니다.(이건 기능이 아니라 버그가 아닌가 라는 생각을 하고있습니다)




Line 179: Line 179:




===클래스 멤버십(Class membership)===
===클래스 멤버십===


여러 개의 메소드는 여러분으로 하여금 오브젝트의 클래스를 조회(query)할 있도록 해드립니다.
몇몇의 메서드를 이용하면 사용자는 객체의 클래스를 조회할 있습니다.


'''클래스.''' 여러분은 메시지 클래스(the message class)를 사용하여 오브텍트의 클래스에 관해 모든 객체들에게 요청을 할 수 있습니다.
'''class.''' class 라는 메시지를 사용하면 모든 객체에 대해 객체의 클래스가 무엇인지를 요청할 수 있습니다.
<syntaxhighlight lang="text">
<syntaxhighlight lang="text">
1 class    ⇒    SmallInteger
1 class    ⇒    SmallInteger
Line 189: Line 189:




반대로, 여러분은 오브젝트가 특정 클래스의 인스턴스 인지의 여부에 대한 확인을 요청할 수도 있습니다:
이와는 반대로, 객체가 특정 클래스의 인스턴스가 맞는지의 여부에 대한 확인을 요청할 수 있는 방법도 있습니다:
{| style="border: 1px solid black;"
{| style="border: 1px solid black;"
|-
|-
Line 202: Line 202:




스몰토크가 그 자신에게서 작성되었기 때문에, 여러분은 superclass와 클래스 메시지(12장을 보십시오)의 올바른 조합을 사용하여 스몰토크의 구조 전체를 실제로 탐색할 수 있습니다.  
스몰토크는 스몰토크 스스로를 이용해서 만들어져 있기 때문에, 사용자는 상위클래스와 메시지(12장을 참고해주세요)의 정상적인 조합을 사용해서 스몰토크의 구조 전체를 탐색할 수 있습니다.




'''isKindOf:'''  Object»isKindOf:는 수신자(the receiver)의 클래스를 동일한 것 또는 인수 클래스 의 서브클래스로서 답변합니다.  
'''isKindOf:'''  Object>>isKindOf:는 수신자의 클래스가 인자클래스와 동일한지, 또는 서브클래스인지등을 반환합니다.
 
{| style="border: 1px solid black;"
{| style="border: 1px solid black;"
|-
|-
Line 223: Line 224:
|}
|}


1/3은 분수인 동시에 숫자의 한 종류이며, 클래스 숫자가 클래스 분수의 superclass이지만 1/3은 정수가 아닙니다.


1/3 은 분수인 동시에 숫자의 한 종류이며, Integer 클래스는 분수 클래스의 super클래스이지만, 1/3 이 정수가 되는것은 아닙니다.




'''respondsTo:''' Object»respondsTo:은 인수로서 주어진 메시지 선택자(the message selector)를 수신자(the receiver)가 이해하는지의 여부로 답변합니다.
'''respondsTo:''' Object>>respondsTo: 는 수신자의 클래스가 인자의 클래스와 동일한지, 또는 서브클래스인지에 대한 정보등을 반환합니다.
 
<syntaxhighlight lang="text">
<syntaxhighlight lang="text">
1 respondsTo: #, ⇒  false
1 respondsTo: #, ⇒  false
Line 233: Line 235:




보통 오브젝트의 클래스를 위해 오브젝트를 조회(query)하거나 오브젝트가 어떤 메시지를 이해하는지를 오브젝트에게 요청하는 것은 좋지 않은 생각입니다. 여러분은 오브젝트의 클래스에 기초하여 결정을 하는 대신에, 단순히 오브젝트에 메시지를 발송하여야만 하며, 그 오브젝트가 어떻게 작동해야만 할지를 스스로 결정하도록(예컨데 오브젝트의 클래스에 기초하여) 해야 합니다.  
일반적으로 객체에 클래스를 질의하거나, 객체가 어떤 메시지를 이해할 수 있는지에 대해서 객체에게 요청하는 것은 좋은 방법이 아닙니다. 객체의 클래스에 근거해서 판단하는 대신, 객체에 메시지만을 보낸후 수신한 객체가 어떤 행동을 할지는 객체의 결정(예를들자면 객체의 클래스에 기반한)에 맡겨야 합니다.
    
    




===복사(Copying)===
===객체의 복사===
 
오브젝트를 복사하는 것은 몇 가지 미묘한 문제들을 낳습니다. 인스턴스 변수들이 참조(reference)에 의해 접근되기 때문에, 오브젝트의 오브젝트의 얕은 복사(shallow copy)는 인스턴스 변수에 대한 그 오브젝트의 참조를 오리지널 오브젝트와 공유합니다:


객체를 복사하는 것은 몇 가지 미묘한 문제들을 발생시킵니다. 인스턴스 변수들은 참조에 의해 접근되기 때문에, 객체의 얕은 복사는 인스턴스 변수에 대한 그 객체의 참조를 원본 객체와 공유합니다:


{| style="border: 1px solid black;"
{| style="border: 1px solid black;"
Line 260: Line 261:




'''Object»shallowCopy''' 는 오브젝트의 얕은 복사(shallow copy)를 만드는 프리미티브 메소드(primitive method)입니다. a2가 a1의 유일한 얕은 복사이므로, 두 개의 배열은 그것들이 포함하고 있는 상주하는 배열(the nested array)에 대한 참조를 공유합니다.
'''Object>>shallowCopy''' 는 객체의 얕은 복사를 만드는 프리미티브 메서드입니다. a2 가 a1 의 유일한 얕은 복사이기 때문에, 두 개의 배열은 공통으로 겹치는 배열에 대한 참조를 공유합니다.
 


'''Object>>shallowCopy''' 는 Object>>copy 에 대한 "공용 인터페이스" 이며 만약 인스턴스들이 고유한 경우 반드시 재지정(override) 해야만 합니다. 예를 들면  Boolean , Character, Smallinteger, Symbol 클래스, UndefinedObject 등이 이 경우에 해당됩니다.


'''Object»shallowCopy''' 는 Object»copy에 대한 “public interface(공용 인터페이스)”이며 만약 인스턴스들이 고유한(unique)것들일 경우 반드시 재지정(overridden) 되어야만 합니다. 이것은 케이스(case)입니다. 예를 들면 클래스 불리언(Boolean), 문자(Character), Smallinteger, 심볼(Symbol) 그리고 UndefinedObject와 함께있는경우,


'''Object>>copyTwoLevel'''은 간단한 얕은 복사로 잘 되지 않을때, 정확한 작업을 진행합니다:


'''Object»copyTwoLevel'''은 단순한 얕은 복사(simple shallow copy)가 만족시키지 못할 때, 명확한 작업을 수행합니다:
{| style="border: 1px solid black;"
{| style="border: 1px solid black;"
|-
|-
Line 281: Line 283:




'''Object»deepCopy''' 는 임의로 오브젝트의 깊은 복사(deep copy)를 만듭니다.
'''Object>>deepCopy''' 는 임의로 객체의 깊은 복사를 만듭니다.
 
{| style="border: 1px solid black;"
{| style="border: 1px solid black;"
|-
|-
Line 296: Line 299:




깊은복사(deepCopy)의 문제는 상호간의 귀납적인 구조(mutually recursive structure)에 적용될 때, 종료되지 않는다는 것입니다:
깊은 복사의 문제는 상호 재귀적 구조에 사용되는경우, 종료되지 않는다는 것입니다:
 
{| style="border: 1px solid black;"
{| style="border: 1px solid black;"
|-
|-
Line 309: Line 313:




비록, 깊은 복사(deepCopy) 재지정이 바른 작업을 하는 것이 가능하지만, Object»는 더 나은 솔루션을 우리에게 제공합니다:
올바른 작동을 위해 deepCopy 의 재지정(override)을 할 수도 있지만, 보통 Object>>copy 가 더 나은 방법이라고 할 수 있습니다:




메소드 8.7: 템플릿 메소드(template method)로서 오브젝트를 복사하기
메서드 8.7: 템플릿 메서드로 객체를 복사하기
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Object»copy
Object>>copy
   "Answer another instance just like the receiver. Subclasses typically override
   "Answer another instance just like the receiver. Subclasses typically override
     postCopy;
     postCopy;
Line 322: Line 327:




공유되지 않아야 할 모든 인스턴스 변수들을 복사하기 위해 여러분은 반드시 postCopy를 재지정(override)해야만 합니다. posetCopy는 반드시 super postCopy를 실행해야만 합니다.
공유되지 않아야 할 모든 인스턴스 변수들을 복사하기 위해 여러분은 반드시 postCopy를 재지정해야 합니다. posetCopy는 반드시 super postCopy를 실행해야 합니다.






===디버깅(Debugging)===
===디버깅===


여기서 가장 중요한 메소드는 halt입니다. 메소드에서 breakpoint를 설정하기 위해, 메소드의 바디에 있는 몇몇 점(point)에 메시지 발송(message send) self halt”를 삽입합니다. 메시지가 발송될 때, 실행은 간섭되며, 디버거는 여러분의 프로그램에서 이 지점(point)에 열릴 것입니다. (디버거에 관한 좀더 상세한 내용은 6장을 보십시오.)
이 부분에서 가장 중요한 메서드는 halt 입니다. 메서드에서 중단점(breakpoint)을 설정하기 위해, 메서드의 내용중 원하는 부분에 메세지를 보내는 "self halt" 를 삽입합니다. 메시지가 보내질 때, 실행은 중단되며, 디버거는 프로그램내의 중단된 지점에서 열리게 됩니다. (디버거에 관한 좀 더 자세한 내용은 6장을 보십시오.)


다음 중요한 메시지는 assert:이며, 이 메시지는 그 자체의 인수로서 블록(block)을 취합니다 만약 블록이 true를 리턴하면 실행이 계속됩니다. 그렇지 않을 경우, AssertionFailure 예외(exception)가 제기될 것입니다. 만약 이 예외가 별도로 잡히지 않으면, 디버거는 실행시 이 지점(point)에서 열릴 것입니다. assert:는 계약(contract)에 의해 디자인을 지원하는 작업에 특별히 유용합니다. 가장 전형적인 용례는 오브젝트의 공용 메소드(public methods)에 대한 사소하지 않은 전제조건(non-trivial pre-conditions)을 점검하는 작업에 쓰이는 것입니다. Stack»pop는 다음과 같이 쉽게 실행될 수 있습니다.
다음으로 중요한 메시지는 assert:이며, 이 메시지는 블록을 인자로 요구합니다. 만약 블록이 true 를 반환하면 계속 실행됩니다. true 가 아닌경우, AssertionFailure 예외가 발생됩니다. 만약 이 예외를 인지하지 못하면, 예외가 발생한 지점에서 디버거가 열립니다. assert: 메시지는 명세에 의한 설계를 진행하는 작업을 지원해야하는 작업등에 유용합니다. 객체의 공용 메서드에 대한 중요한 사전조건을 점검하는 작업등에서 사용되는경우가 일반적입니다. Stack>>pop는 다음처럼 쉽게 구현되어 있습니다:




메소드 8.8: 전제조건 점검 (Checking a pre-condition)
메서드 8.8: 전제조건 점검
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Stack»pop
Stack>>pop
   "Return the first element and remove it from the stack."
   "Return the first element and remove it from the stack."
   self assert: [ self isEmpty not ].
   self assert: [ self isEmpty not ].
Line 342: Line 348:




TestCase»assert:로 Object»assert:을 혼돈하지 마십시오, 이것은 SUnit 테스트 프레임워크에서 발생합니다. (7장을 보십시오) 전자가 그것의 인수<ref name="주석8-1">실제로, 이것은 불리언을 포함한 값을 이해하는 모든 인수를 취할 것입니다.</ref>로서 블록을 기대하는 반면에, 후자는 불리언(Boolean)기대합니다. 비록 두 개 모두 디버깅에 유용하지만, 매우 다른 용도로 쓰입니다.  
TestCase>>assert: 와 Object>>assert: 을 혼돈하지 마십시오, Object>>assert 은 SUnit 테스트 프레임워크에서 발견할 수 있습니다(7장을 보십시오). 전자가 인자<ref name="주석8-1">실제로, 이것은 Boolean을 포함한 값을 이해하는 모든 인자를 취할 것입니다.</ref>로서 블록을 필요로하는 반면에, 후자는 Boolean 을 인자로서 받습니다. 하지만, 두가지 모두 디버깅에 유용합니다만, 사용목적은 크게 다릅니다.






===에러 핸들링(Error handling)===
===오류 처리===


이 프로토콜은 런타임 에러에 유용한 여러 개의 메소드들을 포함하고 있습니다.
이 프로토콜은 런타임 오류를 나타내는데에 유용한 여러 개의 메서드들을 포함하고 있습니다.


만약, deprecation이 프리퍼런스 브라우저(Preference browser)의 디버그 프로토콜에서 켜진다면, 현재 메소드인 self deprecated: anExplanationString 신호 발송은 더 이상 사용되어서는 안됩니다. 문자열 인수(the String argument)는 반드시 대안(alternative)를 제공해야만 합니다.
설정 브라우저(Preference browser) 에서 deprecation 을 활성한 경우에 self deprecated: anExplanationString 신호를 보내면 현재 메서드는 더이상 사용되지 않는것이 되며, String 으로 반환되는 인자는 대안을 제시하게 됩니다.


<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
1 doIfNotNil: [ :arg | arg printString, ' is not nil' ]
1 doIfNotNil: [ :arg | arg printString, ' is not nil' ]
   ⇒    SmallInteger(Object)»doIfNotNil: has been deprecated. use ifNotNilDo:
   ⇒    SmallInteger(Object)>>doIfNotNil: has been deprecated. use ifNotNilDo:
</syntaxhighlight>
</syntaxhighlight>




'''doesNotUnderstand:''' 는 메시지 lookup이 실패할 때마다 발송됩니다. 디폴트 실행은 예컨대, Object»doesNotUnderstand:지점에서 디버거를 트리거 하게 될 것입니다. 몇 가지 다른 동작을 제공하기 위해 doestNotUnderstand:재지정(override)하는 것은 유용할 수 있습니다.  
'''doesNotUnderstand:''' 는 메시지 lookup 에 실패할 때마다 전송됩니다. 기본구현은 그렇습니다만, 예를 들자면 Object>>doesNotUnderstand: 시점에서 디버거를 열게됩니다. 이 외에 다른 동작을 제공하려면 doestNotUnderstand: 재지정 하는것이 좋습니다.


Object»error Object»error:는 예외들(exceptions)를 제기하기 위해 사용될 수 있는 포괄적인 메소드들(generic methods)입니다. 일반적으로, 여러분 자신의 사용자화된 예외들을 제기하는 것이 나으므로, 여러분의 코드에서 생긴 에러들과 커널 클래스들(kernel clases)에서 생긴 에러들을 구별할 있을 것입니다.
Object>>error Object>>error: 는 예외를 발생시키기 위해 사용될 수 있는 범용적인 메서드입니다. (하지만 대부분의 경우 kernel 클래스와 코드에서 발생하는 오류를 구분할 있도록 사용자가 별도로 예외를 발생시키는것이 좋습니다.)


스몰토크에서 추상 메소드(Abstract methods)는 바디 '''self subclassResponsibility''' 와 함께 관례(convention)에 의해 실행됩니다. 추상 클래스(abstract class)는 우연히 예시되어야만 하며, 그 다음 추상 메소드에 대한 콜은 평가중인 Object»subclassResponsibility를 결과물로 내놓을 것입니다. 
스몰토크에서 추상 메서드는 규칙에 따라 '''self subclassResponsibility''' 의 본문에 구현되어 있습니다. 따라서 추상클래스가 우연히(실수로) 인스턴스화 되어 호출되어도 Object>>subclassResponsibility 가 실행됩니다.




메소드 8.9: 메소드인 시그널링(signaling)은 추상 메소드입니다.
메서드 8.9: 추상 메서드임을 신호로 알리기.
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Object»subclassResponsibility
Object>>subclassResponsibility
   "This message sets up a framework for the behavior of the class' subclasses.
   "This message sets up a framework for the behavior of the class' subclasses.
   Announce that the subclass should have implemented this message."
   Announce that the subclass should have implemented this message."
Line 375: Line 381:




매그니튜드(혹은 등급, Magnitude), 숫자(Number) 그리고 불리언(Boolean)우리가 이번 장에서 잠깐 살펴볼 추상 클래스들의 고전적인 예들입니다.
Magnitude, Number, Boolean 은 장에서 잠깐 살펴볼 추상 클래스들의 고전적인 예입니다.
 
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Number new + 1    ⇒    ''Error: My subclass should have overridden #+''
Number new + 1    ⇒    ''Error: My subclass should have overridden #+''
Line 381: Line 388:




'''self ShouldNotImplement''는 관례에(convention) 의해 상속된 메소드인 시그널에 발송되었고, 그 발송은 이 서브클래스에 적합하지 않습니다. 이것은 일반적으로 클래스 계층도의 디자인과 꽤 잘맞지 않는 어떤 신호(sign)입니다. 그럼에도 불구하고 단일 상속의 한계 때문에, 때때로 이러한 예비수단(workarounds)을 피하는 것이 매우 어렵습니다.
관례에 따라 서브클래스가 상속해서는 안되는 메서드를 알려주려먼 서브클래스쪽에서 '''self ShouldNotImplement''' 를 보냅니다. 이건 일반적인 경우 클래스계층의 설계가 잘못된 경우의 신호(signal)입니다. 그렇지만 단일상속에서 오는 한계때문에, 가끔 이런 예비수단을 사용해야만 하죠.
 
대표적인 예로서, 딕셔너리에 의해 상속되었지만 실행된 것으로 알려지지 않은 Collection>>remove: 가 있습니다. (딕셔너리는 removeKey:instead를 제공합니다.)
 


전형적인 예는 딕셔너리에 의해 상속되었지만 실행된 것으로 알려지지 않은 Collection»remove:입니다. (딕셔너리는 removeKey:instead를 제공합니다.)


===Testing===
===Testing===


testing 메소드는 SUnit 테스트와는 아무 관계가 없습니다. testing 메소드는 여러분에게 수신자(receiver)의 상태에 관한 질문을 하도록 해주고 불리언(Boolean)을 리턴하는 메소드입니다.
testing 메서드는 SUnit 테스트와는 아무 관계가 없습니다. testing 메서드는 여러분에게 수신자<sup>receiver</sup>의 상태에 대한 질문을 하고 Boolean(Boolean)을 반환하는 메서드입니다.
 
'''Object''' 는 여러개의 testing 메서드를 제공합니다. isComplex 는 이미 확인되었습니다. 그 외에도  '''isArray''', '''isBoolean''', '''isBlock''', '''isCollection''' 등이 있습니다. 일반적으로 이러한 메서드들은 가능하면 사용하지 말아야 합니다. 그 이유는 메서드의 클래스를 위해 객체를 조회<sup>quering</sup>하는 작업은 캡슐화<sup>encapsulation</sup>에 위배<sup>a form of violation<sup/>되기 때문입니다. 사용자는 객체의 클래스를 테스트 하는 대신에, 요청<sup>request<sup/>을 보내서 객체로 하여금, 그 클래스를 어떻게 제어할지 결정하도록 해야합니다.
 
그렇지만, 이 테스트 메서드중 일부는 정말로 유용합니다. 가장 유용한 것은 아마도 ProtoObject>>isNil 과 Object>>notNil 일 것입니다.(Null 객체<ref name="주석8-2">BobbyWoolf, Null Object. In Robert Martin, Dirk Riehle and Frank Buschmann, editors, Pattern Languages of Program Design 3. Addison Wesley, 1998</ref> 의 디자인 패턴을 사용하면 이런 메서드가 필요한 경우를 없앨 수 있습니다.)


여러 개의 testing 메소드가 '''오브젝트'''에 의해 제공됩니다 우리는 이미 isComplex를 보았습니다. 다른 것들은 '''isArray''', '''isBoolean''', '''isBlock''', '''isCollection''' 등을 포함합니다. 일반적으로 이러한 메소드들은 회피되어야 합니다. 그 이유는 메소드의 클래스를 위해 오브젝트를 조회(quering)하는 작업은 인켑슐레이션(encapsulation)의 위배 폼(a form of violation)입니다. 사용자는 오브젝트의 클래스를 테스트 하는 대신에, 요청(request)을 반드시 보내어 오브젝트로 하여금, 어떻게 그 클래스를 핸들 할지를 결정하도록 만들어야 합니다.


그럼에도 불구하고, 이 테스트 메소드들의 몇몇은 부인할 수 없을 만큼 유용합니다. 가장 유용한 것은 아마도 ProtoObject»isNil 과 Object»notNil 입니다 (비록 Null 오브젝트<ref name="주석8-2">BobbyWoolf, Null Object. In Robert Martin, Dirk Riehle and Frank Buschmann, editors, Pattern Languages of Program Design 3. Addison Wesley, 1998</ref> 디자인 패턴(Object design pattern)이 심지어 이 메소드들을 위한 필요를 제거할 수 있기 때문입니다.


===initialize release===


===릴리즈 초기화 하기 (Initialize release)===
Object 가 아닌 ProtoObject 에 잇는 마지막 중요한 메서드는 initialize 입니다.


ProtoObject에 있는 오브젝트에서 발생하지 않는 최종 키 메소드(final key method)는 initialize(초기화)입니다.


메서드 8.10: 빈 hook 메서드로서의 initialize


메소드 8.10: 빈 hook 메소드로서의 initialize(초기화)
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
ProtoObject»initialize
ProtoObject>>initialize
   "Subclasses should redefine this method to perform initializations on instance
   "Subclasses should redefine this method to perform initializations on instance
   creation"
   creation"
Line 407: Line 418:




이것이 중요한 이유는 버전 3.9로서의 스퀵에서, 시스템에 있는 모든 클래스들을 위해 정의된 디폴트 새 메소드는(default new method) 새롭게 만든 인스턴스에 초기화(initialize)발송할 것이기 때문입니다.  
squeak 3.9 버전에서 이게 중요한 이유는, 기본적으로 시스템의 모든 클래스에서 new 메서드는 새로 생성된 인스턴스에 initialize 를 보내게 되기 때문입니다.


 
메서드 8.11: 클래스-사이드 템플릿 메서드로서의 '''new '''
메소드 8.11: 클래스-사이드 템플릿 메소드(class-side template method)로서의 '''new '''
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Behavior»new
Behavior>>new
   "Answer a new initialized instance of the receiver (which is a class) with no
   "Answer a new initialized instance of the receiver (which is a class) with no
     indexable
     indexable
Line 420: Line 430:




이 의미는 initialize hook 메소드를 단순히 재지정하여 여러분의 클래스의 인스턴스들이 자동으로 초기화(initialized)된다는 것입니다. initialize 메소드는 반드시 모든 상속된 인스턴스 변수들을 위한 클래스 불변성(the class invariant)를 구축하기 위해 보통 super initialize를 수행합니다. (이것이 다른 스몰토크들의 표준 동작이 아닌 것에 주의해야 합니다)
위의 내용은 initialize hook 메서드를 단순히 재지정하면,  클래스의 인스턴스가 자동으로 초기화된다는 의미입니다.  
 
 
일반적으로 initialize 메서드는 상속된 모든 인스턴스 변수들을 위한 클래스 불변성을 확보하기 위해 super initialize를 수행합니다.(이런 동작이 다른 스몰토크에서 표준은 아니라는것을 주의해주세요)
 





Latest revision as of 02:23, 17 September 2013

Object

어떠한 의도 또는 목적에 대해서도 Object 는 상속 계층의 뿌리root(또는 최하위 계층) 가 됩니다. 실제로 스몰토크에서, 상속도의 진짜 최하위 계층은 ProtoObject 이며, ProtoObject 는 객체로서 가장된 최소한의 엔티티를 정의하기 위해 사용되지만, 지금의 스몰토크는 이런 ProtoObject 에 대한 내용을 크게 신경쓰지 않아도 됩니다.

Obejct 는 kernel-Object 카테고리에서 발견할 수 있습니다. 놀랍게도, 여기에서는 400개의 메서드(확장을 포함한)를 발견할 수 있습니다. 달리 말한다면, 스몰토크에서 사용자가 정의하는 모든 클래스는 사용자가 이런 메서드들을 아는지와는 상관없이 400개의 메서드를 자동으로 제공한다는 의미가 됩니다. 몇몇메서드가 없을수도 있는데 새버전의 Squeak 에서는 몇몇 필요없는 메서드를 제거할 수도 있기 때문이라는 것에 주의해야 합니다.


Object 클래스에 적혀있는 주석:

Object 는 클래스 계층도에 속한 대부분의 다른 클래스들을 위한 루트 클래스the root class입니다. 예외가 있다면 ProtoObject(Object 의 superclass)와 그것의 서브클래스들subclases입니다. Object 클래스는 접근, 복사, 비교, 오류 처리, 메시지 전송, 그리고 반영과 같은 모든 일반적인 객체에 공통으로 적용되는 기본 동작을 제공합니다. 또한 모든 객체들이 응답해야할 유틸리티 메시지는 이 곳에서 정의됩니다. Object 는 인스턴스 변수를 가지고 있지 않으며 그것(인스턴스변수)를 추가해야할 필요도 없습니다. 이런 특성은 Object 의 여러 클래스들이 특별한 실행(예를 들면 smallIntegerUndefinedObject)을 가지는 Object 부터 상속되었기 때문이거나, 또는 VM 이 특정한 표준 클래스의 구조와 레이아웃에 관해 알고있거나 그것들을 기반으로 하기 때문입니다.


만약 Object 의 instance side 에서 메서드 카테고리 탐색을 시작하면, 여기서 제공되는 몇가지 중요한 동작을 발견하기 시작할 수 있을겁니다.


출력(Printing)

스몰토크에서 모든 객체들은 그 자체의 출력 폼을 반환할 수 있습니다. 사용자는 워크스페이스에서 모든 표현식을 선택할 수 있고 print it 메뉴를 선택할 수 있습니다: 이런 동작들은 프로그램식을 실행하고 반환된 객체에게 그 자체를 출력하도록 요청합니다. 사실 출력에 대한 동작은 반환된 객체에 printString 메시지을 전송합니다. 메서드 printString 은 템플릿 메서드이며 그 핵심은 메시지 printOn: 을 그 자신의 수신자(receiver)에 발송하는것에 있습니다. printOn: 은 hook 메서드 로서 특별하게 취급됩니다.

Object>>printOn: 은 사용자가 가장 빈번하게 오버라이딩 하게되는 메서드중 하나가 될겁니다. printOn 메서드는 인자로 Stream 을 받은후, 받은 인자를 이용해서 String 으로 객체의 이름을 표현하는것을 만들어냅니다. printOn 의 기본적인 구현은 "a" 또는 "an" 뒤에 클래스 이름을 사용하는것 밖에 없습니다. Object>>printString 은 이렇게 만들어진 문자열을 반환 합니다.[1]

예를 들어보면, 클래스 브라우저는 printOn: 메서드를 재정의 하지 않으며, printSting 메시지를 Object 에서 정의된 메서드들을 실행하는 인스턴스로 전송합니다.

Browser new printString  'a Browser'


TTCFont 클래스에서 PrintOn: 특수화에 대한 좋은 예를 확인할 수 있습니다. 아래의 코드에서 볼 수 있듯이 클래스의 인스턴스를 출력할때, 폰트의 모음 이름, 크기 그리고 종속 모음 이름이 클래스 이름의 뒤에 같이 출력됩니다.


메서드 8.1: printOn: 메서드의 재정의

TTCFont>>printOn: aStream
  aStream nextPutAll: 'TTCFont(';
  nextPutAll: self familyName; space;
  print: self pointSize; space;
  nextPutAll: self subfamilyName;
  nextPut: $)


TTCFont allInstances anyOne printString       'TTCFont(BitstreamVeraSans 6 Bold)'


메시지 PrintOn: 은 storeOn: 과 같지 않다는것에 주의해주세요. storeOn: 메시지는 수신자를 재생성하는데 사용할 수 있는 프로그램식을 그 자체의(storeOn의)인자에 stream 으로 대입합니다. 이렇게 만들어진 프로그램식은 readFrom 메시지를 사용해서 읽어들일때 처리됩니다. pringOn: 은 단지 수신자의 텍스트 버전을 반환할 뿐입니다. 물론 텍스트로 만들어진 프로그램식이 수신자를 표현하는 자체 처리 프로그램식이 될 수도 있습니다.


표현식과 자체평가표현식 이라는 단어에 대해서 함수 프로그래밍에서, 프로그램식은 실행이 될 때 값을 반환합니다. 스몰토크에서, 메시지(프로그램식)들은 객체(값value)를 반환합니다. 그 자신이 스스로 값으로 인식되는 좋은 속성들을 가지는 객체들이 있습니다. 예를 들면, 객체 true 의 값은 그 자신으로서 true 라는 객체가 됩니다. 이러한 객체들을 자체평가 객체self-evaluating objects 라 부릅니다. 워크스페이스에서 객체를 출력(print it)할 때 객체의 출력용 버전을 볼 수 있습니다. 여기에 위에서 설명한 자체평가 프로그램식의 예제가 있습니다.

true ⇒ true
3@4 ⇒ 3@4
$a ⇒ $a
#(1 2 3) ⇒ #(1 2 3)


배열과 같은 몇몇 객체는 자체평가 또는 포함하고 있는 객체에 의존하지 않는다는 사실을 주의해주세요. 예를 들면, Boolean 으로 만들어진 배열은 자체평가가 되지면 persons 의 배열은 그렇지 않다는거죠. 스퀵 3.9에서 이러한 동작방식은 가능한 많이 (isSelfEvaluating 메시지처럼) 자체평가 형태로 컬렉션을 출력하기 위해 도입되었으며, 이는 특별히 중괄호 배열에 있어 true 가 됩니다. 다음 예제는 동적 배열의 구성요소들일 경우에만, 동적 배열이 자체 처리됨을 보여드립니다. 다음 예제는 동적배열의 구성요소가 자체평가형인 경우 동적배열이 자체평가로 처리됨을 보여줍니다:

{10@10 . 100@100} ⇒ {10@10 . 100@100}
{Browser new . 100@100} ⇒ an Array(a Browser 100@100)


리터럴 배열은 오직 리터럴만 포함할 수 있다는걸 기억해주세요. 그렇기 때문에 다음의 배열은 두 개의 점(point)을 가지지 않으며 여섯개의 리터럴 요소만을 가지게 됩니다.

#(10@10 100@100) ⇒ #(10 #@ 10 100 #@ 100)


printOn: 메서드의 대부분은 자체평가를 구현하는 특수화를 진행하게 됩니다. Point>>printOn: 과 Interval>>printOn: 의 구현은 자체평가방식이 됩니다.


메서드 8.2: Point의 자체평가

Point>>printOn: aStream
  "The receiver prints on aStream in terms of infix notation."
  x printOn: aStream.
  aStream nextPut: $@.
  y printOn: aStream


메서드 8.3:인터벌(Interval)의 자체평가(Self-evaluation)

Interval>>printOn: aStream
  aStream nextPut: $(;
    print: start;
    nextPutAll: ' to: ';
    print: stop.
  step = 1 ifTrue: [aStream nextPutAll: ' by: '; print: step].
  aStream nextPut: $)


1 to: 10  (1 to: 10)  "intervals are self--evaluating"


정체성(Identity)과 동일성(equality)

스몰토크에서 = 메시지는 객체가 같은지(동일성)를 테스트하며(예: 두개의 객체가 같은값을 가지는지에 대해), == 메시지는 객체의 정체성(identity)을 테스트 합니다(예: 두개의 프로그램식이 같은 객체를 나타내는지에 대한 부분)


객체 동일성 비교에 대한 기본구현은 객체의 정체성에 대한 테스트입니다.

메서드 8.4: 객체의 동일성

Object>>= anObject
  "Answer whether the receiver and the argument represent the same object.
  If = is redefined in any subclass, consider also redefining the message hash."
   self == anObject


아래에 보이는것은 자주 오버라이드 하게될 메서드 입니다. 복소수의 경우를 생각해주세요:

(1 + 2 i) = (1 + 2 i) ⇒ true "same value"
(1 + 2 i) == (1 + 2 i) ⇒ false "but different objects"

이렇게 동작하는 이유는, Complex 가 = 를 다음과 같이 오버라이드(override) 하고 있기 때문입니다.


메서드 8.5: 복소수를 위한 동일성

Complex>>= anObject
  anObject isComplex
    ifTrue: [ (real = anObject real) & (imaginary = anObject imaginary)]
    ifFalse: [ anObject adaptToComplex: self andSend: #=]


Object>>∼= 의 기본구현은 Object>>= 을 부정하는 의미가 됩니다. 일반적으로 변경해야할 필요는 없습니다.

(1 + 2 i) = (1 + 4 i)  true


만약 = 을 오버라이드 한다면, hash 를 사용한 부분 또한 오버라이드 하지 않으면 안됩니다. 클래스의 인스턴스가 Dictionary의 키로서 사용되었을 경우에, "같다"고 보이는 인스턴스는, hash값도 같아야 합니다:

메서드 8.6: hash는 반드시 복소수등을 위해 재실행되어야 합니다.

Complex>>hash
  "Hash is reimplemented because = is implemented."
   real hash bitXor: imaginary hash.


비록 = 과 bash 를 함께 오버라이드 해야한다고 해도, == 를 재지정하면 안됩니다. (객체의 identity 에 대한 의미는 모든 클래스에 동일합니다) == 기호는 ProtoObject 의 프리미티브 메서드 이기 때문입니다.


스퀵이 다른 스몰토크 구현과는 다른 몇가지 익숙하지 않은 동작을 한다는것에 주의해주세요: 예를들면 symbol 과 string 은 동일하지 않습니다.(이건 기능이 아니라 버그가 아닌가 라는 생각을 하고있습니다)


#'lulu' = 'lulu'    true
'lulu' = #'lulu'    true


클래스 멤버십

몇몇의 메서드를 이용하면 사용자는 객체의 클래스를 조회할 수 있습니다.

class. class 라는 메시지를 사용하면 모든 객체에 대해 객체의 클래스가 무엇인지를 요청할 수 있습니다.

1 class    ⇒    SmallInteger


이와는 반대로, 객체가 특정 클래스의 인스턴스가 맞는지의 여부에 대한 확인을 요청할 수 있는 방법도 있습니다:

1 isMemberOf: SmallInteger ⇒ true "must be precisely this class"
1 isMemberOf: Integer ⇒ false
1 isMemberOf: Number ⇒ false
1 isMemberOf: Object ⇒ false


스몰토크는 스몰토크 스스로를 이용해서 만들어져 있기 때문에, 사용자는 상위클래스와 메시지(12장을 참고해주세요)의 정상적인 조합을 사용해서 스몰토크의 구조 전체를 탐색할 수 있습니다.


isKindOf: Object>>isKindOf:는 수신자의 클래스가 인자클래스와 동일한지, 또는 서브클래스인지등을 반환합니다.

1 isKindOf: SmallInteger ⇒ true
1 isKindOf: Integer ⇒ true
1 isKindOf: Number ⇒ true
1 isKindOf: Object ⇒ true
1 isKindOf: String ⇒ false
1/3 isKindOf: Number ⇒ true
1/3 isKindOf: Integer ⇒ false


1/3 은 분수인 동시에 숫자의 한 종류이며, Integer 클래스는 분수 클래스의 super클래스이지만, 1/3 이 정수가 되는것은 아닙니다.


respondsTo: Object>>respondsTo: 는 수신자의 클래스가 인자의 클래스와 동일한지, 또는 서브클래스인지에 대한 정보등을 반환합니다.

1 respondsTo: #, ⇒  false


일반적으로 객체에 클래스를 질의하거나, 객체가 어떤 메시지를 이해할 수 있는지에 대해서 객체에게 요청하는 것은 좋은 방법이 아닙니다. 객체의 클래스에 근거해서 판단하는 대신, 객체에 메시지만을 보낸후 수신한 객체가 어떤 행동을 할지는 객체의 결정(예를들자면 객체의 클래스에 기반한)에 맡겨야 합니다.


객체의 복사

객체를 복사하는 것은 몇 가지 미묘한 문제들을 발생시킵니다. 인스턴스 변수들은 참조에 의해 접근되기 때문에, 객체의 얕은 복사는 인스턴스 변수에 대한 그 객체의 참조를 원본 객체와 공유합니다:

a1 := { { 'harry' } }.  
a1 ⇒ #(#('harry'))
a2 := a1 shallowCopy.  
a2 ⇒ #(#('harry'))
(a1 at: 1) at: 1 put: 'sally'.  
a1 ⇒ #(#('sally'))
a2 ⇒ #(#('sally')) "the subarray is shared"


Object>>shallowCopy 는 객체의 얕은 복사를 만드는 프리미티브 메서드입니다. a2 가 a1 의 유일한 얕은 복사이기 때문에, 두 개의 배열은 공통으로 겹치는 배열에 대한 참조를 공유합니다.


Object>>shallowCopy 는 Object>>copy 에 대한 "공용 인터페이스" 이며 만약 인스턴스들이 고유한 경우 반드시 재지정(override) 해야만 합니다. 예를 들면 Boolean , Character, Smallinteger, Symbol 클래스, UndefinedObject 등이 이 경우에 해당됩니다.


Object>>copyTwoLevel은 간단한 얕은 복사로 잘 되지 않을때, 정확한 작업을 진행합니다:

a1 := { { 'harry' } } .  
a2 := a1 copyTwoLevel.  
(a1 at: 1) at: 1 put: 'sally'.  
a1 ⇒ #(#('sally'))
a2 ⇒ #(#('harry')) "fully independent state"


Object>>deepCopy 는 임의로 객체의 깊은 복사를 만듭니다.

a1 := { { { 'harry' } } } .  
a2 := a1 deepCopy.  
(a1 at: 1) at: 1 put: 'sally'.  
a1 ⇒ #(#('sally'))
a2 ⇒ #(#(#('harry')))


깊은 복사의 문제는 상호 재귀적 구조에 사용되는경우, 종료되지 않는다는 것입니다:

a1 := { 'harry' }.  
a2 := { a1 }.  
a1 at: 1 put: a2.  
a1 deepCopy ⇒ ...does not terminate!


올바른 작동을 위해 deepCopy 의 재지정(override)을 할 수도 있지만, 보통 Object>>copy 가 더 나은 방법이라고 할 수 있습니다:


메서드 8.7: 템플릿 메서드로 객체를 복사하기

Object>>copy
  "Answer another instance just like the receiver. Subclasses typically override
    postCopy;
  they typically do not override shallowCopy."
  self shallowCopy postCopy


공유되지 않아야 할 모든 인스턴스 변수들을 복사하기 위해 여러분은 반드시 postCopy를 재지정해야 합니다. posetCopy는 반드시 super postCopy를 실행해야 합니다.


디버깅

이 부분에서 가장 중요한 메서드는 halt 입니다. 메서드에서 중단점(breakpoint)을 설정하기 위해, 메서드의 내용중 원하는 부분에 메세지를 보내는 "self halt" 를 삽입합니다. 메시지가 보내질 때, 실행은 중단되며, 디버거는 프로그램내의 중단된 지점에서 열리게 됩니다. (디버거에 관한 좀 더 자세한 내용은 6장을 보십시오.)

다음으로 중요한 메시지는 assert:이며, 이 메시지는 블록을 인자로 요구합니다. 만약 블록이 true 를 반환하면 계속 실행됩니다. true 가 아닌경우, AssertionFailure 예외가 발생됩니다. 만약 이 예외를 인지하지 못하면, 예외가 발생한 지점에서 디버거가 열립니다. assert: 메시지는 명세에 의한 설계를 진행하는 작업을 지원해야하는 작업등에 유용합니다. 객체의 공용 메서드에 대한 중요한 사전조건을 점검하는 작업등에서 사용되는경우가 일반적입니다. Stack>>pop는 다음처럼 쉽게 구현되어 있습니다:


메서드 8.8: 전제조건 점검

Stack>>pop
  "Return the first element and remove it from the stack."
  self assert: [ self isEmpty not ].
  self linkedList removeFirst element


TestCase>>assert: 와 Object>>assert: 을 혼돈하지 마십시오, Object>>assert 은 SUnit 테스트 프레임워크에서 발견할 수 있습니다(7장을 보십시오). 전자가 인자[2]로서 블록을 필요로하는 반면에, 후자는 Boolean 을 인자로서 받습니다. 하지만, 두가지 모두 디버깅에 유용합니다만, 사용목적은 크게 다릅니다.


오류 처리

이 프로토콜은 런타임 오류를 나타내는데에 유용한 여러 개의 메서드들을 포함하고 있습니다.

설정 브라우저(Preference browser) 에서 deprecation 을 활성한 경우에 self deprecated: anExplanationString 신호를 보내면 현재 메서드는 더이상 사용되지 않는것이 되며, String 으로 반환되는 인자는 대안을 제시하게 됩니다.

1 doIfNotNil: [ :arg | arg printString, ' is not nil' ]
       SmallInteger(Object)>>doIfNotNil: has been deprecated. use ifNotNilDo:


doesNotUnderstand: 는 메시지 lookup 에 실패할 때마다 전송됩니다. 기본구현은 그렇습니다만, 예를 들자면 Object>>doesNotUnderstand: 는 이 시점에서 디버거를 열게됩니다. 이 외에 다른 동작을 제공하려면 doestNotUnderstand: 재지정 하는것이 좋습니다.

Object>>error 와 Object>>error: 는 예외를 발생시키기 위해 사용될 수 있는 범용적인 메서드입니다. (하지만 대부분의 경우 kernel 클래스와 코드에서 발생하는 오류를 구분할 수 있도록 사용자가 별도로 예외를 발생시키는것이 좋습니다.)

스몰토크에서 추상 메서드는 규칙에 따라 self subclassResponsibility 의 본문에 구현되어 있습니다. 따라서 추상클래스가 우연히(실수로) 인스턴스화 되어 호출되어도 Object>>subclassResponsibility 가 실행됩니다.


메서드 8.9: 추상 메서드임을 신호로 알리기.

Object>>subclassResponsibility
  "This message sets up a framework for the behavior of the class' subclasses.
  Announce that the subclass should have implemented this message."
  self error: 'My subclass should have overridden ', thisContext sender selector
    printString


Magnitude, Number, Boolean 은 이 장에서 잠깐 살펴볼 추상 클래스들의 고전적인 예입니다.

Number new + 1        ''Error: My subclass should have overridden #+''


관례에 따라 서브클래스가 상속해서는 안되는 메서드를 알려주려먼 서브클래스쪽에서 self ShouldNotImplement 를 보냅니다. 이건 일반적인 경우 클래스계층의 설계가 잘못된 경우의 신호(signal)입니다. 그렇지만 단일상속에서 오는 한계때문에, 가끔 이런 예비수단을 사용해야만 하죠.

대표적인 예로서, 딕셔너리에 의해 상속되었지만 실행된 것으로 알려지지 않은 Collection>>remove: 가 있습니다. (딕셔너리는 removeKey:instead를 제공합니다.)


Testing

testing 메서드는 SUnit 테스트와는 아무 관계가 없습니다. testing 메서드는 여러분에게 수신자receiver의 상태에 대한 질문을 하고 Boolean(Boolean)을 반환하는 메서드입니다.

Object 는 여러개의 testing 메서드를 제공합니다. isComplex 는 이미 확인되었습니다. 그 외에도 isArray, isBoolean, isBlock, isCollection 등이 있습니다. 일반적으로 이러한 메서드들은 가능하면 사용하지 말아야 합니다. 그 이유는 메서드의 클래스를 위해 객체를 조회quering하는 작업은 캡슐화encapsulation에 위배a form of violation되기 때문입니다. 사용자는 객체의 클래스를 테스트 하는 대신에, 요청request을 보내서 객체로 하여금, 그 클래스를 어떻게 제어할지 결정하도록 해야합니다.

그렇지만, 이 테스트 메서드중 일부는 정말로 유용합니다. 가장 유용한 것은 아마도 ProtoObject>>isNil 과 Object>>notNil 일 것입니다.(Null 객체[3] 의 디자인 패턴을 사용하면 이런 메서드가 필요한 경우를 없앨 수 있습니다.)


initialize release

Object 가 아닌 ProtoObject 에 잇는 마지막 중요한 메서드는 initialize 입니다.


메서드 8.10: 빈 hook 메서드로서의 initialize

ProtoObject>>initialize
  "Subclasses should redefine this method to perform initializations on instance
  creation"


squeak 3.9 버전에서 이게 중요한 이유는, 기본적으로 시스템의 모든 클래스에서 new 메서드는 새로 생성된 인스턴스에 initialize 를 보내게 되기 때문입니다.

메서드 8.11: 클래스-사이드 템플릿 메서드로서의 new

Behavior>>new
  "Answer a new initialized instance of the receiver (which is a class) with no
    indexable
  variables. Fail if the class is indexable."
   self basicNew initialize


위의 내용은 initialize hook 메서드를 단순히 재지정하면, 클래스의 인스턴스가 자동으로 초기화된다는 의미입니다.


일반적으로 initialize 메서드는 상속된 모든 인스턴스 변수들을 위한 클래스 불변성을 확보하기 위해 super initialize를 수행합니다.(이런 동작이 다른 스몰토크에서 표준은 아니라는것을 주의해주세요)


Notes

  1. http://ta.onionmixer.net/wordpress/?p=215 페이지를 참고해주세요
  2. 실제로, 이것은 Boolean을 포함한 값을 이해하는 모든 인자를 취할 것입니다.
  3. BobbyWoolf, Null Object. In Robert Martin, Dirk Riehle and Frank Buschmann, editors, Pattern Languages of Program Design 3. Addison Wesley, 1998