SqueakByExample:5.6: Difference between revisions
Onionmixer (talk | contribs) mNo edit summary |
Onionmixer (talk | contribs) (본문수정 및 스타일 수정) |
||
(4 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
==메서드 | ==메서드 찾기는 상속 관계를 따른다== | ||
오브젝트가 메시지를 수신할 때 정확히 어떤 일이 생깁니까? | 오브젝트가 메시지를 수신할 때 정확히 어떤 일이 생깁니까? | ||
프로세스는 꽤 단순합니다: 수신자의 클래스는 메시지를 | 프로세스는 꽤 단순합니다: 수신자의 클래스는 메시지를 취급하기 위해 사용할 메서드를 검색합니다. 만약 이 클래스가 메서드를 갖고 있지 안다면, 클래스는 그것의 상위 클래스와 다른 것을 상속 관계를 거슬러 올라가 찾습니다. 메서드를 발견 했을 때, 인자는 그 메서드의 파라미터에 묶이며, 가상머신은 그 인수를 실행합니다. | ||
이 작업은 본래 이렇게 단순한 것입니다. 그럼에도 불구하고, 몇몇 분들이 궁금해 할 질문들에 대해 답변해드려야 할 몇 가지 내용이 존재합니다. | 이 작업은 본래 이렇게 단순한 것입니다. 그럼에도 불구하고, 몇몇 분들이 궁금해 할 질문들에 대해 답변해드려야 할 몇 가지 내용이 존재합니다. | ||
* 메서드가 명확하게 값을 리턴하지 못할 때 어떤 현상이 생기게 됩니까? | * 메서드가 명확하게 값을 리턴하지 못할 때 어떤 현상이 생기게 됩니까? | ||
* 클래스가 | * 클래스가 상위 클래스 메서드를 다시 실행할 때 어떤 현상이 생깁니까? | ||
* Self와 | * Self와 상위 발송의 차이점은 무엇입니까? | ||
* 어떤 메서드도 발견되지 않으면 무슨 일이 발생합니까? | * 어떤 메서드도 발견되지 않으면 무슨 일이 발생합니까? | ||
우리가 여기서 제시한 메서드 보기의 규칙들은 개념적입니다: 가상 머신 | 우리가 여기서 제시한 메서드 보기의 규칙들은 개념적입니다: 가상 머신 실행자는 메서드 검색 속도를 늘리기 위해 모든 종류의 기술과 최적화를 사용합니다. 그것이 가상 머신 실행자의 임무지만, 여러분은 우리의 규칙들과 임무의 다른 점을 발견하지 못하실 것입니다. | ||
먼저, 기본 검색 기술들을 살펴보고, 그 다음 이 부가적인 질문들을 고려해 봅시다. | 먼저, 기본 검색 기술들을 살펴보고, 그 다음 이 부가적인 질문들을 고려해 봅시다. | ||
===메서드 검색=== | ===메서드 검색=== | ||
Line 43: | Line 45: | ||
반대로, 만약 우리가 메시지 openInWorld를 Ellipse에 보낸다면, 메서드는 즉시 발견되지 않습니다, 그 이유는 클래스 EllipseMorph가 openInWorld를 즉시 실행하지 않기 때문입니다. 그러므로 검색은, openInWorld 메서드가 클래스 Morph에서 발견될 때까지, | 반대로, 만약 우리가 메시지 openInWorld를 Ellipse에 보낸다면, 메서드는 즉시 발견되지 않습니다, 그 이유는 클래스 EllipseMorph가 openInWorld를 즉시 실행하지 않기 때문입니다. 그러므로 검색은, openInWorld 메서드가 클래스 Morph에서 발견될 때까지, 상위 클래스 BorderedMorph 그리고 다른 것에서 계속됩니다. (그림 5.2를 보십시오) | ||
Line 56: | Line 58: | ||
[[image:openInWorldLookup.png|none| | [[image:openInWorldLookup.png|none|800px|thumb|그림5.2: 메서드 lookup 은 상속받은 상속관계를 따릅니다]] | ||
===self 리턴하기=== | ===self 리턴하기=== | ||
EllipseMorph»defaultColor (메서드 5.14)는 명확하게 Color yellow를 리턴하고, 반면에, Morph»openInWorld (메서드 5.15)는 어떤 것도 리턴 하지 | EllipseMorph»defaultColor (메서드 5.14)는 명확하게 Color yellow를 리턴하고, 반면에, Morph»openInWorld (메서드 5.15)는 어떤 것도 리턴 하지 않음을 나타납니다. | ||
실제로, 한 개의 메서드는 항상 값과 함께 메시지를 답으로 내놓으며-그것은 물론, 오브젝트입니다. 답은 아마도 메서드에서↑construct 로 정의될 수 있지만, 만약 실행이 ↑를 실행하지 않고, 메서드의 끝부분에 도달한다면, 메서드는 여전히 값을 내놓게 됩니다: 메서드는 메시지를 수신한 오브젝트를 답변으로 내놓습니다. 우리는 보통 메서드는 “self를 답변한다”라고 말합니다, 그 이유는 스몰토크에서, | 실제로, 한 개의 메서드는 항상 값과 함께 메시지를 답으로 내놓으며-그것은 물론, 오브젝트입니다. 답은 아마도 메서드에서↑construct 로 정의될 수 있지만, 만약 실행이 ↑를 실행하지 않고, 메서드의 끝부분에 도달한다면, 메서드는 여전히 값을 내놓게 됩니다: 메서드는 메시지를 수신한 오브젝트를 답변으로 내놓습니다. 우리는 보통 메서드는 “self를 답변한다”라고 말합니다, 그 이유는 스몰토크에서, 의사 변수 self는 오히려 Java에서 this과 같습니다. | ||
이것은 메서드 5.15가 메서드 5.16과 | 이것은 메서드 5.15가 메서드 5.16과 동등하다는 사실을 나타냅니다. | ||
메서드 5.16: | 메서드 5.16: 명시적으로 self 리턴하기 | ||
<syntaxhighlight lang="smalltalk"> | <syntaxhighlight lang="smalltalk"> | ||
Morph»openInWorld | Morph»openInWorld | ||
Line 80: | Line 83: | ||
왜↑self 는 명시적으로 좋은 시행법이 아닐까요? 글쎄요, 여러분이 무엇인가를 명시적으로 리턴할 때, 여러분은 발신자에게 무엇인가 흥미로운 것을 리턴하고 있다고 의사소통하고 있습니다.여러분이 명확하게 self를 리턴할 때, 여러분은 발신자에게 반환 값을 사용할 것을 기대한다고 말하고 있습니다. 이와 같은 내용은 여기에서 사용할 사례가 아니며, self를 명확하게 리턴하는 최상의 방법이 아닙니다. | |||
이 ↑self는 스몰토크에서 일반적인 용어이며, Kent Beck이 "흥미로운 리턴 값"<ref name="주석5-3">Kent Beck, Smalltalk Best Practice Patterns. Prentice-Hall, 1997.</ref>이라고 언급한 것입니다. | 이 ↑self는 스몰토크에서 일반적인 용어이며, Kent Beck이 "흥미로운 리턴 값"<ref name="주석5-3">Kent Beck, Smalltalk Best Practice Patterns. Prentice-Hall, 1997.</ref>이라고 언급한 것입니다. | ||
<center>{{HighlightDoubleBox|여러분은 | <center>{{HighlightDoubleBox|여러분은 전송자가 값을 사용하기를 원할 때에만 값을 리턴하십시오.}}</center> | ||
===재지정과 확장 (Overriding and extension)=== | ===재지정과 확장 (Overriding and extension)=== | ||
만약 우리가 그림 5.2에서 EllipseMorph 클래스 계층도를 다시 본다면, 클래스 | 만약 우리가 그림 5.2에서 EllipseMorph 클래스 계층도를 다시 본다면, 클래스 Morph와 EllipseMorph 모두 defaultColor을 실행한다는 것을 알 수 있습니다. 사실, 만약 우리가 새로운 Morph(Morph new openInworld)를 연다면, ellipse는 디폴트로 노랑색이 될 것이지만, 파랑 morph를 얻게 될 것입니다. | ||
우리는 | 우리는 defaultColor메서드가 Morph로부터 상속하도록 EllipseMorph가 defaultColor 메서드를 재지정하였다고 말할 수 있습니다. 상속된 메서드는 anEllipse의 관점에서 볼 때 더 이상 존재하는 것이 아닙니다. | ||
때때로, 우리는 상속된 메서드들을 재지정 하기를 원치 않으며, 몇 가지 새로운 기능으로 그 메서드들을 확장하기 원할 것입니다. 그 이유는 우리가 서브 클래스에서 지정하고 있는 새로운 기능 뿐만 아니라 재지정된 메서드를 불러올 수 있기를 원하기 때문입니다. | 때때로, 우리는 상속된 메서드들을 재지정 하기를 원치 않으며, 몇 가지 새로운 기능으로 그 메서드들을 확장하기 원할 것입니다. 그 이유는 우리가 서브 클래스에서 지정하고 있는 새로운 기능 뿐만 아니라 재지정된 메서드를 불러올 수 있기를 원하기 때문입니다. | ||
스몰토크에서, 단일 상속을 지원하는 많은 오브젝트 지향 언어들처럼, 이 작업 역시 super send( | 스몰토크에서, 단일 상속을 지원하는 많은 오브젝트 지향 언어들처럼, 이 작업 역시 super send(상위 발송)의 도움으로 수행될 수 있습니다. | ||
이 메커니즘의 가장 중요한 어플리케이션은 initialize | 이 메커니즘의 가장 중요한 어플리케이션은 initialize 메서드에 있습니다. 클래스의 새로운 인스턴스가 초기화될 때마다, 모든 상속된 인스턴스 변수를 초기화하는 것 또한 매우 중요합니다. 그럼에도 불구하고, 어떻게 이 작업을 해야 할 지에 관한 지식은 상속 사슬(the inheritance chain)에 있는 각 상위 클래스의 메서드들을 초기화 하는 기술에 정확히 담겨 있습니다. 서브클래스는 심지어 상속된 인스턴스 변수 초기화를 시도할 때에도 아무런 역할을 하지 않습니다. | ||
그러므로 모든 더 많은 초기화 작업을 수행하기 전에 super initialize를 보내기 위해 initialize 메서드를 실행하는 것이 좋은 실행법 입니다: | 그러므로 모든 더 많은 초기화 작업을 수행하기 전에 super initialize를 보내기 위해 initialize 메서드를 실행하는 것이 좋은 실행법 입니다: | ||
Line 111: | Line 115: | ||
<center>{{HighlightDoubleBox|Initialize | <center>{{HighlightDoubleBox|Initialize 메서드는 항상 super Initialize(상위 초기화)를 보내며 시작합니다.}}</center> | ||
===Self sends 와 super sends=== | ===Self sends 와 super sends=== | ||
우리는 그렇지 않으면 재지정 될 | 우리는 그렇지 않으면 재지정 될 상속 동작을 작성하기 위해 상위 발송이 필요합니다. 메서드를 작성하는 평상시의 방법은, 상속된 메서드인지 그렇지 않던지 간에, self send를 사용하는 것입니다. | ||
self | self send는 super send와 어떻게 다를까요? Self 와 같이 super는 메시지의 수신자를 표시합니다. 유일하게 변경된 것은 메서드 검색입니다. 수신자의 클래스에서 검색을 시작하는 대신에, super는 super send가 발생하는 위치에서 메서드의 클래스의 상위 클래스 내부 에서 시작합니다. | ||
Super가 | Super가 상위 클래스가 아닌 것에 주의합시다! 이 오해는 일상적이고 자연스러운 것입니다. 또한 수신자의 상위 클래스 내부에서 검색을 시작한다고 생각하는 것 또한 흔한 실수 입니다. | ||
우리가 모든 | 우리가 모든 morph에 보낼 수 있는 메시지 initString을 생각해 봅시다: | ||
<syntaxhighlight lang="smalltalk"> | <syntaxhighlight lang="smalltalk"> | ||
Line 130: | Line 135: | ||
리턴 값은 morph를 다시 만들기 위해 | 리턴 값은 morph를 다시 만들기 위해 처리할 수 있는 문자열입니다. 어떻게 이 문자열이 self와 super send의 조합을 통해 얻을 수 있는 결과와 정확히 같을 수 있을까요? | ||
먼저, 그림 5.3에 보이는 것 처럼, anEllipse initString은 메서드 initString을 클래스 Morph에서 발견되도록 해줍니다. | 먼저, 그림 5.3에 보이는 것 처럼, anEllipse initString은 메서드 initString을 클래스 Morph에서 발견되도록 해줍니다. | ||
[[image:initStringLookup.png|none| | [[image:initStringLookup.png|none|800px|thumb|그림 5.3: self 와 super sends]] | ||
Line 146: | Line 151: | ||
메서드 '''Morph»initString''' 는 '''fullPrintOn: '''의 '''self send'''를 수행합니다. 이 작업은 클래스 EllipseMorph에서 두 번째 검색을 시작하여 fullPrintOn: in BorderedMorph를 찾습니다. (그림 5.3을 다시 보십시오) | 메서드 '''Morph»initString''' 는 '''fullPrintOn: '''의 '''self send'''를 수행합니다. 이 작업은 클래스 EllipseMorph에서 두 번째 검색을 시작하여 fullPrintOn: in BorderedMorph를 찾습니다. (그림 5.3을 다시 보십시오) | ||
반드시 기억해야 할 사실은 self send가 anEllipse의 클래스인 수신자 | 반드시 기억해야 할 사실은 self send가 anEllipse의 클래스인 수신자 클래스에서 메서드 찾기를 다시 시작하도록 만든다는 사실입니다. | ||
Line 162: | Line 167: | ||
이 지점에서 '''BorderedMorph»fullPrintOn:'''는 그 자체의 ''' | 이 지점에서 '''BorderedMorph»fullPrintOn:'''는 그 자체의 '''상위 클래스'''에서 상속하는 '''fullPrintOn: behaviour'''를 확장하기 위해 '''super send'''를 수행합니다. 이 수행은 상위 발송이기 때문에, 곧 Morph에서 상위 발송이 발생하는 위치에 소재한 클래스의 상위 클래스 에서 검색을 지금 시작합니다. 그 다음, 우리는 즉시, Morph»fullPrintOn:를 찾고 처리해야 합니다. | ||
Super 검색이 수신자의 상위 클래스에서 시작하지 않았음에 주의합니다. 만약 상위 클래스에서 시작한다면 끝없는 반복의 결과를 가져오는, BorderedMorph에서의 검색이 시작되게 할 것입니다. | |||
<center>{{HighlightDoubleBox|Supersend는 Super send를 수행하는 메서드가 가진 클래스의 상위 클래스에서 시작되는 정적 메서드 검색을 수행합니다.}}</center> | |||
만약 여러분이 super send와 그림 5.3을 세심하게 살펴보셨다면, '''super binding'''이 정적이라는 것을 알게 되셨을 것입니다: 모든 문제는 상위 발송을 발견한 text의 위치에 있는 클래스 입니다. 반대로, self의 의미는 동적입니다: 이것은 항상 현재 실행 중인 메시지의 수신자를 표현하며, self에 발송된 모든 메시지들이 수신자 클래스에서 시작하여 검색된다는 것을 의미합니다. | |||
Line 177: | Line 183: | ||
우리가 찾고 있는 메시지가 발견되지 않을 경우 어떤 현상이 발생할까요? | 우리가 찾고 있는 메시지가 발견되지 않을 경우 어떤 현상이 발생할까요? | ||
우리가 메시지 foo를 우리의 | 우리가 메시지 foo를 우리의 ellipse에 보낸다고 가정해 봅시다. 첫 번째로 보통 메서드 찾기는 상속 관계에서 오브젝트까지(또는 ProbeObject) 이 메서드를 찾기 위해 이동할 것입니다. 메서드가 발견되면, 가상 머신은 오브젝트에게 self doesNotUnderstand: #foo를 보내게 만들 것입니다. (그림 5.4를 보십시오) | ||
[[image:fooNotFound.png|none| | [[image:fooNotFound.png|none|800px|thumb|그림 5.4: 메시지 foo를 이해할 수 없습니다]] | ||
Line 188: | Line 192: | ||
왜 우리가 이 명백한 에러를 취급하기 위해 이 꾸불꾸불한 경로를 받아들여야 할까요? 글쎄요, 이 경로는 개발자에게 이와 같은 에러를 가로채어 대안적인 행동을 취하는 작업에 활용할 쉬운 방법을 제공합니다. 어떤 분은 모든 오브젝트의 서브 클래스에서 메서드 doesNotUnderstand”를 쉽게 재지정하고, 에러를 취급할 수 있는 다른 방법을 제공하실 수 있을 것입니다. | 왜 우리가 이 명백한 에러를 취급하기 위해 이 꾸불꾸불한 경로를 받아들여야 할까요? 글쎄요, 이 경로는 개발자에게 이와 같은 에러를 가로채어 대안적인 행동을 취하는 작업에 활용할 쉬운 방법을 제공합니다. 어떤 분은 모든 오브젝트의 서브 클래스에서 메서드 doesNotUnderstand”를 쉽게 재지정하고, 에러를 취급할 수 있는 다른 방법을 제공하실 수 있을 것입니다. | ||
사실, 이 작업은 한 개의 오브젝트에서 다른 오브젝트로, 메시지의 자동 위임을 수행하는 쉬운 방법일 수 있습니다. 위임자 | 사실, 이 작업은 한 개의 오브젝트에서 다른 오브젝트로, 메시지의 자동 위임을 수행하는 쉬운 방법일 수 있습니다. 위임자 오브젝트는 단순히 자신이 이해하지 못하는 모든 메시지들을, 메시지들을 취급하는 책임을 담당하는 오브젝트에 위임하거나, 자체적으로 에러 메시지를 발생시켜버립니다. | ||
==Notes== | ==Notes== |
Revision as of 12:28, 6 March 2013
메서드 찾기는 상속 관계를 따른다
오브젝트가 메시지를 수신할 때 정확히 어떤 일이 생깁니까?
프로세스는 꽤 단순합니다: 수신자의 클래스는 메시지를 취급하기 위해 사용할 메서드를 검색합니다. 만약 이 클래스가 메서드를 갖고 있지 안다면, 클래스는 그것의 상위 클래스와 다른 것을 상속 관계를 거슬러 올라가 찾습니다. 메서드를 발견 했을 때, 인자는 그 메서드의 파라미터에 묶이며, 가상머신은 그 인수를 실행합니다.
이 작업은 본래 이렇게 단순한 것입니다. 그럼에도 불구하고, 몇몇 분들이 궁금해 할 질문들에 대해 답변해드려야 할 몇 가지 내용이 존재합니다.
- 메서드가 명확하게 값을 리턴하지 못할 때 어떤 현상이 생기게 됩니까?
- 클래스가 상위 클래스 메서드를 다시 실행할 때 어떤 현상이 생깁니까?
- Self와 상위 발송의 차이점은 무엇입니까?
- 어떤 메서드도 발견되지 않으면 무슨 일이 발생합니까?
우리가 여기서 제시한 메서드 보기의 규칙들은 개념적입니다: 가상 머신 실행자는 메서드 검색 속도를 늘리기 위해 모든 종류의 기술과 최적화를 사용합니다. 그것이 가상 머신 실행자의 임무지만, 여러분은 우리의 규칙들과 임무의 다른 점을 발견하지 못하실 것입니다.
먼저, 기본 검색 기술들을 살펴보고, 그 다음 이 부가적인 질문들을 고려해 봅시다.
메서드 검색
우리가 EllipseMorph의 인스턴스를 만든다고 가정해 봅시다.
anEllipse := EllipseMorph new.
만약 우리가 이 오브젝트 메시지 defaultColor를 보낸다면, 우리는 Color yellow: 를 결과로 얻게 됩니다.
anEllipse defaultColorColor ⇒ yellow
클래스 EllipseMorph는 defaultColor를 실행하므로, 적합한 메서드는 즉시 발견됩니다.
메서드 5.14: 로컬에서 실행된 메서드
EllipseMorph»defaultColor
"answer the default color/fill style for the receiver"
↑ Color yellow
반대로, 만약 우리가 메시지 openInWorld를 Ellipse에 보낸다면, 메서드는 즉시 발견되지 않습니다, 그 이유는 클래스 EllipseMorph가 openInWorld를 즉시 실행하지 않기 때문입니다. 그러므로 검색은, openInWorld 메서드가 클래스 Morph에서 발견될 때까지, 상위 클래스 BorderedMorph 그리고 다른 것에서 계속됩니다. (그림 5.2를 보십시오)
메서드 5.15: 상속된 메서드
Morph»openInWorld
"Add this morph to the world. If in MVC, then provide a Morphic window for it."
self couldOpenInMorphic
ifTrue: [self openInWorld: self currentWorld]
ifFalse: [self openInMVC]
self 리턴하기
EllipseMorph»defaultColor (메서드 5.14)는 명확하게 Color yellow를 리턴하고, 반면에, Morph»openInWorld (메서드 5.15)는 어떤 것도 리턴 하지 않음을 나타납니다.
실제로, 한 개의 메서드는 항상 값과 함께 메시지를 답으로 내놓으며-그것은 물론, 오브젝트입니다. 답은 아마도 메서드에서↑construct 로 정의될 수 있지만, 만약 실행이 ↑를 실행하지 않고, 메서드의 끝부분에 도달한다면, 메서드는 여전히 값을 내놓게 됩니다: 메서드는 메시지를 수신한 오브젝트를 답변으로 내놓습니다. 우리는 보통 메서드는 “self를 답변한다”라고 말합니다, 그 이유는 스몰토크에서, 의사 변수 self는 오히려 Java에서 this과 같습니다.
이것은 메서드 5.15가 메서드 5.16과 동등하다는 사실을 나타냅니다.
메서드 5.16: 명시적으로 self 리턴하기
Morph»openInWorld
"Add this morph to the world. If in MVC,
then provide a Morphic window for it."
self couldOpenInMorphic
ifTrue: [self openInWorld: self currentWorld]
ifFalse: [self openInMVC].
↑ self "Don't do this unless you mean it"
왜↑self 는 명시적으로 좋은 시행법이 아닐까요? 글쎄요, 여러분이 무엇인가를 명시적으로 리턴할 때, 여러분은 발신자에게 무엇인가 흥미로운 것을 리턴하고 있다고 의사소통하고 있습니다.여러분이 명확하게 self를 리턴할 때, 여러분은 발신자에게 반환 값을 사용할 것을 기대한다고 말하고 있습니다. 이와 같은 내용은 여기에서 사용할 사례가 아니며, self를 명확하게 리턴하는 최상의 방법이 아닙니다.
이 ↑self는 스몰토크에서 일반적인 용어이며, Kent Beck이 "흥미로운 리턴 값"[1]이라고 언급한 것입니다.
재지정과 확장 (Overriding and extension)
만약 우리가 그림 5.2에서 EllipseMorph 클래스 계층도를 다시 본다면, 클래스 Morph와 EllipseMorph 모두 defaultColor을 실행한다는 것을 알 수 있습니다. 사실, 만약 우리가 새로운 Morph(Morph new openInworld)를 연다면, ellipse는 디폴트로 노랑색이 될 것이지만, 파랑 morph를 얻게 될 것입니다.
우리는 defaultColor메서드가 Morph로부터 상속하도록 EllipseMorph가 defaultColor 메서드를 재지정하였다고 말할 수 있습니다. 상속된 메서드는 anEllipse의 관점에서 볼 때 더 이상 존재하는 것이 아닙니다.
때때로, 우리는 상속된 메서드들을 재지정 하기를 원치 않으며, 몇 가지 새로운 기능으로 그 메서드들을 확장하기 원할 것입니다. 그 이유는 우리가 서브 클래스에서 지정하고 있는 새로운 기능 뿐만 아니라 재지정된 메서드를 불러올 수 있기를 원하기 때문입니다. 스몰토크에서, 단일 상속을 지원하는 많은 오브젝트 지향 언어들처럼, 이 작업 역시 super send(상위 발송)의 도움으로 수행될 수 있습니다.
이 메커니즘의 가장 중요한 어플리케이션은 initialize 메서드에 있습니다. 클래스의 새로운 인스턴스가 초기화될 때마다, 모든 상속된 인스턴스 변수를 초기화하는 것 또한 매우 중요합니다. 그럼에도 불구하고, 어떻게 이 작업을 해야 할 지에 관한 지식은 상속 사슬(the inheritance chain)에 있는 각 상위 클래스의 메서드들을 초기화 하는 기술에 정확히 담겨 있습니다. 서브클래스는 심지어 상속된 인스턴스 변수 초기화를 시도할 때에도 아무런 역할을 하지 않습니다.
그러므로 모든 더 많은 초기화 작업을 수행하기 전에 super initialize를 보내기 위해 initialize 메서드를 실행하는 것이 좋은 실행법 입니다:
메서드 5.17: Super initialize
BorderedMorph»initialize
"initialize the state of the receiver"
super initialize.
self borderInitialize
Self sends 와 super sends
우리는 그렇지 않으면 재지정 될 상속 동작을 작성하기 위해 상위 발송이 필요합니다. 메서드를 작성하는 평상시의 방법은, 상속된 메서드인지 그렇지 않던지 간에, self send를 사용하는 것입니다.
self send는 super send와 어떻게 다를까요? Self 와 같이 super는 메시지의 수신자를 표시합니다. 유일하게 변경된 것은 메서드 검색입니다. 수신자의 클래스에서 검색을 시작하는 대신에, super는 super send가 발생하는 위치에서 메서드의 클래스의 상위 클래스 내부 에서 시작합니다.
Super가 상위 클래스가 아닌 것에 주의합시다! 이 오해는 일상적이고 자연스러운 것입니다. 또한 수신자의 상위 클래스 내부에서 검색을 시작한다고 생각하는 것 또한 흔한 실수 입니다.
우리가 모든 morph에 보낼 수 있는 메시지 initString을 생각해 봅시다:
anEllipse initString −→ '(EllipseMorph newBounds: (0@0 corner: 50@40) color:
Color yellow) setBorderWidth: 1 borderColor: Color black'
리턴 값은 morph를 다시 만들기 위해 처리할 수 있는 문자열입니다. 어떻게 이 문자열이 self와 super send의 조합을 통해 얻을 수 있는 결과와 정확히 같을 수 있을까요?
먼저, 그림 5.3에 보이는 것 처럼, anEllipse initString은 메서드 initString을 클래스 Morph에서 발견되도록 해줍니다.
메서드 5.18: self send
Morph»initString
↑ String streamContents: [:s | self fullPrintOn: s]
메서드 Morph»initString 는 fullPrintOn: 의 self send를 수행합니다. 이 작업은 클래스 EllipseMorph에서 두 번째 검색을 시작하여 fullPrintOn: in BorderedMorph를 찾습니다. (그림 5.3을 다시 보십시오)
반드시 기억해야 할 사실은 self send가 anEllipse의 클래스인 수신자 클래스에서 메서드 찾기를 다시 시작하도록 만든다는 사실입니다.
메서드 5.19: super와 self send 결합하기
BorderedMorph»fullPrintOn: aStream
aStream nextPutAll: '('.
super fullPrintOn: aStream.
aStream nextPutAll: ') setBorderWidth: '; print: borderWidth;
nextPutAll: ' borderColor: ' , (self colorString: borderColor)
이 지점에서 BorderedMorph»fullPrintOn:는 그 자체의 상위 클래스에서 상속하는 fullPrintOn: behaviour를 확장하기 위해 super send를 수행합니다. 이 수행은 상위 발송이기 때문에, 곧 Morph에서 상위 발송이 발생하는 위치에 소재한 클래스의 상위 클래스 에서 검색을 지금 시작합니다. 그 다음, 우리는 즉시, Morph»fullPrintOn:를 찾고 처리해야 합니다.
Super 검색이 수신자의 상위 클래스에서 시작하지 않았음에 주의합니다. 만약 상위 클래스에서 시작한다면 끝없는 반복의 결과를 가져오는, BorderedMorph에서의 검색이 시작되게 할 것입니다.
만약 여러분이 super send와 그림 5.3을 세심하게 살펴보셨다면, super binding이 정적이라는 것을 알게 되셨을 것입니다: 모든 문제는 상위 발송을 발견한 text의 위치에 있는 클래스 입니다. 반대로, self의 의미는 동적입니다: 이것은 항상 현재 실행 중인 메시지의 수신자를 표현하며, self에 발송된 모든 메시지들이 수신자 클래스에서 시작하여 검색된다는 것을 의미합니다.
이해할 수 없는 메시지
우리가 찾고 있는 메시지가 발견되지 않을 경우 어떤 현상이 발생할까요?
우리가 메시지 foo를 우리의 ellipse에 보낸다고 가정해 봅시다. 첫 번째로 보통 메서드 찾기는 상속 관계에서 오브젝트까지(또는 ProbeObject) 이 메서드를 찾기 위해 이동할 것입니다. 메서드가 발견되면, 가상 머신은 오브젝트에게 self doesNotUnderstand: #foo를 보내게 만들 것입니다. (그림 5.4를 보십시오)
지금, 이것은 완벽하게 일상적인, 동적 메시지 보내기이므로, 검색은 클래스 EllipseMorph로부터 다시 시작되지만, 이번에는 메서드 doesNotUnderstand:를 검색합니다. 나타난 것처럼, 오브젝트는 doesNotUnderstand:를 실행합니다: 이 메서드는 새로운 MessageNotUnderstood 오브젝트를 만들 것이며, 이 오브젝트는 현재 실행 상황에서 디버거의 시작을 가능하게 만듭니다.
왜 우리가 이 명백한 에러를 취급하기 위해 이 꾸불꾸불한 경로를 받아들여야 할까요? 글쎄요, 이 경로는 개발자에게 이와 같은 에러를 가로채어 대안적인 행동을 취하는 작업에 활용할 쉬운 방법을 제공합니다. 어떤 분은 모든 오브젝트의 서브 클래스에서 메서드 doesNotUnderstand”를 쉽게 재지정하고, 에러를 취급할 수 있는 다른 방법을 제공하실 수 있을 것입니다.
사실, 이 작업은 한 개의 오브젝트에서 다른 오브젝트로, 메시지의 자동 위임을 수행하는 쉬운 방법일 수 있습니다. 위임자 오브젝트는 단순히 자신이 이해하지 못하는 모든 메시지들을, 메시지들을 취급하는 책임을 담당하는 오브젝트에 위임하거나, 자체적으로 에러 메시지를 발생시켜버립니다.
Notes
- ↑ Kent Beck, Smalltalk Best Practice Patterns. Prentice-Hall, 1997.