SqueakByExample:8.1: Difference between revisions

From 흡혈양파의 번역工房
Jump to navigation Jump to search
(번역수정)
(용어수정)
 
(12 intermediate revisions by the same user not shown)
Line 9: Line 9:
Object 클래스에 적혀있는 주석:
Object 클래스에 적혀있는 주석:


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




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




===출력(Printing)===
===출력(Printing)===


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


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




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




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


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




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


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




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


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




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




메서드 8.2: Point의 자체 처리
메서드 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 99: Line 97:
메서드 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 107: Line 105:
   aStream nextPut: $)
   aStream nextPut: $)
</syntaxhighlight>
</syntaxhighlight>


<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Line 112: Line 111:
</syntaxhighlight>
</syntaxhighlight>


===정체성과 동일성===


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


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


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




메서드 8.4: 오브젝트 동일성
객체 동일성 비교에 대한 기본구현은 객체의 정체성에 대한 테스트입니다.
 
메서드 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 129: Line 129:




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


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


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




메서드 8.5: 복소수를 위한 동일성
메서드 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 152: Line 150:




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


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




만약 여러분이 =를 재지정하면, 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 170: Line 167:




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


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




Line 183: Line 181:
===클래스 멤버십===
===클래스 멤버십===


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


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




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




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


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


'''isKindOf:'''  Object»isKindOf:는 수신자의 클래스를 동일한 것 또는 인수 클래스의 하위 클래스로서 답변합니다.
{| style="border: 1px solid black;"
{| style="border: 1px solid black;"
|-
|-
Line 225: Line 224:
|}
|}


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




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


'''respondsTo:''' Object»respondsTo:은 인수로서 주어진 메시지 선택자를 수신자가 이해하는지의 여부로 답변합니다.
<syntaxhighlight lang="text">
<syntaxhighlight lang="text">
1 respondsTo: #, ⇒  false
1 respondsTo: #, ⇒  false
Line 235: Line 235:




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




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


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


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




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




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




'''Object»copyTwoLevel'''은 단순한 얕은 복사가 만족시키지 못할 때, 명확한 작업을 수행합니다:
'''Object>>copyTwoLevel'''은 간단한 얕은 복사로 잘 되지 않을때, 정확한 작업을 진행합니다:
 
{| style="border: 1px solid black;"
{| style="border: 1px solid black;"
|-
|-
Line 282: Line 283:




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




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




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


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


메서드 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 324: Line 328:


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


===디버깅===
===디버깅===


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


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




메서드 8.8: 전제조건 점검
메서드 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 341: Line 348:




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


===오류 처리===
===오류 처리===


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


만약, deprecation을 기본 설정 브라우저(Preference browser)의 디버그 프로토콜에서 활성화했다면, 현재 메서드인 self deprecated: anExplanationString 신호 전송은 더 이상 사용해서는 안됩니다. 문자열 인자는 반드시 대안을 제공해야 합니다.
설정 브라우저(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: 재지정이 유용할 수 있습니다.  
'''doesNotUnderstand:''' 는 메시지 lookup 에 실패할 때마다 전송됩니다. 기본구현은 그렇습니다만, 예를 들자면 Object>>doesNotUnderstand: 시점에서 디버거를 열게됩니다. 이 외에 다른 동작을 제공하려면 doestNotUnderstand: 재지정 하는것이 좋습니다.


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


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




메서드 8.9: 추상 메서드임을 신호로 알리기.
메서드 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 372: Line 381:




매그니튜드(혹은 등급, Magnitude), 숫자 그리고 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 378: Line 388:




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


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


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


testing 메서드는 SUnit 테스트와는 아무 관계가 없습니다. testing 메서드는 여러분에게 수신자(receiver)의 상태에 관한 질문을 하도록 해주고 Boolean(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/>을 보내서 객체로 하여금, 그 클래스를 어떻게 제어할지 결정하도록 해야합니다.


여러 개의 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> 디자인 패턴을 사용하면 이런 메서드가 필요한 경우를 없앨 수 있습니다.)


그럼에도 불구하고, 이 테스트 메서드중 일부는 부인할 수 없을 만큼 유용합니다. 가장 유용한 것은 아마도 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===


ProtoObject에 있는 오브젝트에서 발생하지 않는 최종 키 메서드는 initialize입니다.
Object 가 아닌 ProtoObject 에 잇는 마지막 중요한 메서드는 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 404: Line 418:




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


메서드 8.11: 클래스-사이드 템플릿 메서드로서의 '''new '''
메서드 8.11: 클래스-사이드 템플릿 메서드로서의 '''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 417: Line 430:




이 의미는 initialize hook 메서드를 단순히 재지정하여 여러분의 클래스의 인스턴스들이 자동으로 초기화된다는 것입니다. initialize 메서드는 반드시 모든 상속된 인스턴스 변수들을 위한 클래스 불변성을 확보하기 위해 보통 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