SqueakByExample:5.3

From 흡혈양파의 번역工房
Revision as of 07:41, 20 January 2013 by Onionmixer (talk | contribs) (큰따옴표 수정)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

모든 오브젝트는 클래스의 인스턴스다

모든 객체는 클래스를 갖고 있으며, 여러분은 메시지 클래스에 클래스를 보내서 그 클래스를 알아낼 수 있습니다.


1 class ⇒ SmallInteger
20 factorial class ⇒ LargePositiveInteger
'hello' class ⇒ ByteString
#(1 2 3) class ⇒ Array
(4@5) class ⇒ Point
Object new class ⇒ Object


클래스는 인스턴스 변수를 통해 그 자체의 인스턴스의 구조를 정의하며, 메서드를 통해 자체의 인스턴스의 동작을 정의합니다. 각각의 메서드는 그 자체의 선택자로 지칭되는 이름을 갖고 있으며, 그 이름은 클래스 내부에서 고유성을 가집니다.

클래스가 오브젝트이므로, 모든 오브젝트는 클래스의 인스턴스이며, 클래스는 반드시 클래스의 인스턴스가 되어야 한다는 규칙을 따릅니다. 클래스의 인스턴스가 되는 클래스는 메타클래스라고 합니다. 여러분이 클래스를 만들 때마다, 시스템은 자동으로 여러분을 위한 메타클래스를 만듭니다. 메타 클래스는 그 자체의 인스턴스인 클래스의 구조와 동작을 정의합니다. 99%의 경우, 여러분은 메타클래스에 관해 생각할 필요가 없으며, 기꺼이 간과해도 됩니다. (우리는 12장에서 메타클래스를 상세히 살펴볼 것입니다.

인스턴스 변수

스몰토크에서 인스턴수 변수는 인스턴스 자체에 대해 개별적(혹은 전용적)입니다. 이 개별성은, 동일한 클래스가 될 수 있도록 발생하는 모든 다른 인스턴스에 의해 접근되는 인스턴스 변수를 허용하는 [필드 또는 멤버 변수] Java와 C++에서는 반대가 됩니다. 우리는 Java와 C++에서 경계 캡슐화(혹은 추상화- 큰 프로그램을 만들 때, 사용자의 편의를 위해 서로 밀접하게 관련이 있는 정보들을 한 곳으로 묶는 것. 추상화를 하면 사용자는 그 부분을 이용할 때 감추어진 정보를 세세하게 알지 않아도 되며, 관련된 부분만 따로 처리할 수 있으므로 프로그램 수정시 편리하다. 안에 있는 정보는 직접적으로 다루거나 사용하는 것이 허용되지 않는다: 역주)은 클래스라고 말하며, 반면에 스몰토크에서는 인스턴스라고 부릅니다.

스몰토크에서, 동일한 클래스의 두 개의 인스턴스는, 클래스가 "접근자 메서드"를 정의하지 않으면, 각각의 다른 인스턴스 변수에 접근할 수 없습니다. 스퀵에는 모든 다양한 오브젝트의 인스턴스 변수에 직접적인 접근을 제공하는 어떤 언어 구문도 없습니다. [실제로, 반영이라고 하는 메커니즘은 인스턴스 변수의 값에 쓸 다른 오브젝트를 요청할 수 있는 방법을 제공하며, 바로 이 방법인 메타 프로그래밍은, 다른 오브젝트들을 들여다 보는 단일 목적에 쓰이는 오브젝트 인스펙터와 같은 쓰기 도구를 위한 프로그래밍입니다.]

인스턴스 변수들은 그 변수들을 정의하는 클래스의 인스턴스 메서드들 중의 이름들 중 어떤 이름을 사용해도 접근이 가능하며, 또한 그 변수들의 서브클래스들에서 정의된 메서드들의 이름들 중 어떤 이름을 사용해서도 접근이 가능합니다. 이 사실은 스몰토크 인스턴스 변수들이, C++과 Java에서, 보호된 변수들과 유사하다는 것을 의미합니다. 그럼에도 불구하고, 우리는 이 인스턴스 변수들이 개별적(private,혹은 전용)이라고 말하는 것을 선호하며, 그 이유는 스몰토크에서는, 서브클래스에서부터 직접 인스턴스 변수에 접근하는 형식을, 나쁜 형식으로 간주하기 때문입니다.

Example

Method Point»dist:(메서드 5.1)는 수신자와 다른 점(point) 사이의 거리를 계산합니다. 수신자의 인스턴스 변수 x와 y로 메서드 바디를 사용하여 직접 접근합니다. 그러나 다른 점의 인스턴스 변수들은 메시지 x와 y에 변수를 발송하여 접근하여야 합니다.


메서드 5.1: 두 점 사이의 각도

Point»dist: aPoint
  "Answer the distance between aPoint and the receiver."
  | dx dy |
  dx := aPoint x -- x.
  dy := aPoint y -- y.
   ((dx * dx) + (dy * dy)) sqrt
  1@1 dist: 4@5      5.0


클래스 기반 인켑슐레이션 보다 인스턴스 기반 인켑슐레이션을 선호하는 이유는, 공동으로 존재하는 동일한 추상 실행을 가능하게 해주기 때문입니다. 예를 들면, method point»dist:, 는 인수 aPoint가 수신자와 동일한 클래스의 인스턴스인지의 여부를 알 필요 또는 신경 쓸 필요가 없습니다. 인수 오브젝트는 메시지 x와 y에 반응하는 한, 극 좌표로 표시할 수 있거나 또는 데이터베이스에서 기록으로서 표시될 수 있거나 또는 배포된 다른 컴퓨터에서, 배포 시스템에서 표시할 수 있으므로, 메서드 5.1에서 코드는 여전히 작동하게 될 것입니다.

메서드

모든 메서드는 공용입니다.[1] 메서드들은 그들의 의도를 가리키는 프로토콜 속으로 그룹지어 제작됩니다. 몇몇 공통 프로토콜 이름은 관례에 따라 만들어 졌습니다. 예를 들어, 그 관례는 모든 접근자 메서드에 대한 접근과, 오브젝트에 시행하는 일관된 초기화 상태를 구축하기 위한 초기화입니다. 개별 프로토콜(the protocol private, 혹은 전용 프로토콜)은 때때로 외부에서 보면 안되는 메서드를 그룹으로 만들 때 사용됩니다. 그럼에도 불구하고, "개별(혹은 전용)" 메서드로 실행하는 메시지를 보내는 작업을 여러분이 시행할 때, 이 작업을 막을 수 있는 것은 아무것도 없습니다.

메서드는 오브젝트의 모든 인스턴스 변수에 접근할 수 있습니다. 몇몇 스몰토크 개발자들은 접근자를 통해 인스턴스 변수에 접근하는 방법을 선호합니다. 이 실행법은 약간의 가치가 있지만, 여러분의 클래스 인터페이스를 어수선하게 채우며, 외부 세계에 개별적 상태(private state)를 노출시킵니다.

인스턴스 측면과 클래스 측면

클래스들이 오브젝트이기 때문에, 그 자체 인스턴스 변수와 메서드를 갖습니다. 우리는 이 클래스 변수들과 클래스 메서드들을 콜하지만, 이것들은 보통 인스턴스 변수들과 메서드들과 다른 것이 조금도 없습니다: 클래스 인스턴스 변수들은 단지 메타클래스로 정의되는 인스턴스 변수들이며, 클래스 메서드는 단지 메타클래스에 의해 정의되는 메서드 일 뿐입니다.

클래스와 그 클래스의 메타클래스는 두 개의 구별된 클래스 이며, 심지어 전자는 후자의 인스턴스이기도 합니다. 그럼에도 불구하고, 이 내용은 프로그래머인 여러분에게 대체적으로 관계가 없습니다: 오히려 여러분은 여러분의 오브젝트의 동작을 정의하는 작업과 그 동작들을 정의하는 클래스들과 중요한 관련이 있습니다.

그림 5.1: 클래스와 그 자체의 메타 클래스(metaclass)를 검색하기

이러한 이유 때문에, 브라우저는 그림 5.1에 보이는 것처럼 두 개의 "sides": "instance side" 그리고 "class side"를 가진 단일체 처럼, 클래스와 메타클래스를 검색할 수 있도록 돕습니다. 클래스 color 등을 검색하기 위해 instance 버튼을 클릭하면, blue와 같은 color의 인스턴스에 메시지가 발송될 당시, 여러분은 실행된 메서드를 검색하면 됩니다. class 버튼 클릭은, 클래스 Color class를 검색하므로, 실제로, 여러분은 클래스 color 자체에 발송된 메시지가 발송될 때, 실행될 메시지를 볼 수 있게 됩니다. 예를 들면, Color blue는 클래스 color에 메시지를 발송합니다. 그렇게 되면, 여러분은 인스턴스 측면이 아닌, color의 클래스 측면에서 정의된 메서드 blue를 찾을 수 있을 것입니다.


aColor := Color blue.   "Class side method blue"
aColor ⇒ Color blue  
aColor red ⇒ 0.0 "Instance side accessor method red"
aColor blue ⇒ 1.0 "Instance side accessor method blue"


여러분은 인스턴스 사이드 위에 제안된 탬플릿을 채움으로써, 클래스를 정의합니다. 여러분이 이 탬플릿을 수락하면, 시스템은 여러분이 정의한 클래스뿐만 아니라, 이에 대응하는 메타클래스도 제작합니다. 여러분은 또한 class 버튼을 클릭하여 메타클래스를 검색할 수 있습니다. 여러분이 직접 편집할 수 있는 메타클래스 생성 탬플릿의 유일한 부분은 인스턴스 변수 이름의 목록 뿐입니다.

일단 클래스가 만들어지면, instance 버튼 클릭은, 여러분이 클래스의 인스턴스로 차지될 메서드를 편집하고 검색할 수 있도록 만들어줍니다. 예를 들면, 그림 5.1에서 여러분은 클래스 color의 인스턴스에서 정의된 메서드 hue를 볼 수 있습니다. 반대로, class 버튼은 여러분이 메타클래스(이번 경우 Color 클래스)를 검색하고 편집할 수 있도록 만들어줍니다.


클래스 메서드

클래스 메서드는 꽤 유용할 수 있으므로, 몇몇 좋은 예시를 위해 Color 클래스를 검색하겠습니다.그렇게 하면, 여러분은 클래스에서 정의된 두 가지 종류의 메서드를 보게 될 것입니다: Color class»blue와 같이 클래스의 인스턴스를 만드는 메서드들과 Color class»showColorCubedhk 같이, 유틸리티 기능을 수행하는 메서드들이 존재합니다. 비록 여러분이 다른 방법들로 사용되는 클래스 메서드들을 찾으실 것이지만, 이 메서드는 매우 전형적인 메서드입니다.

클래스 사이드(the class side)에서 유틸리티 메서드들을 배치하는 작업은 편리하며, 그 이유는 그 메서드들을 먼저 부가적인 모든 메서드를 만들 필요 없이 실행할 수 있기 때문입니다. 게다가, 그 메서드 중 많은 메서드들은, 실행하기 쉽게 만들기 위해 고안된 주석을 포함하게 될 것이므로 더욱 사용이 편리합니다.

Squeak comment.png메서드 Color class»showColorCube를 검색하고, 주석 "Color showColorCube" 에서 따옴표 내부를 더블 클릭한 다음, CMD-d를 타이핑 합니다.

여러분은 이 메서드를 실행하는 효과를 보시게 될 것입니다. [효과를 취소(undo)하기 위해 World ▷ restore display (r) 을 선택합니다.]

Java와 C++에 친숙한 분들은, 클래스 메서드가 정적 메서드(static method)와 유사하게 보이실 것입니다. 그럼에도 불구하고, 스몰토크에서 균일성의 의미는 약간 다릅니다: 비록 Java 메서드가 정말로, 정적으로 해결된 절차(statically resolved procedures)일지라도, 스몰토크 클래스 메서드는 동적으로 처리된(dynamically-dispatched) 메서드인 것입니다. 이것이 의미하는것은, 다시 말해서 Java에서는, (여러 가지 것들이) 정적 메서드에 동작하지 않는 반면, 스몰토크에서는 상속, 재지정 그리고 상위 발송(super-sends)이 클래스 메서드에 동작한다는 것입니다.


클래스 인스턴스 변수

일반적인 인스턴스 변수들과 함께, 클래스의 모든 인스턴스들은 동일한 변수 이름들의 세트를 소유하며, 그리고 그 클래스의 서브클래스의 인스턴스는 변수 이름들을 상속하지만, 그럼에도 불구하고, 각각의 인스턴스는 자체적으로 개별적 값의 세트를 갖습니다. 이 사실은 정확히 클래스 인스턴스 변수들에게도 적용됩니다: 각 클래스는 그 자체의 개별적(private) 클래스 인스턴스 변수를 갖습니다. 서브클래스는 인스턴스 변수들이 가진 클래스 인스턴스 변수들을 상속할 것이지만, 그것의 개별적 복사본들도 갖게 됩니다. 오브젝트가 인스턴스 변수들을 공유하지 않는 것처럼, 클래스들과 그 클래스들의 서브클래스들도 클래스 인스턴스 변수들을 공유하지 않습니다.

여러분은 count라 지칭되는 클래스 인스턴스 변수를, 여러분에게 주어진 클래스를 만드는 작업에 얼마나 많은 인스턴스를 실행하였는지를 지속적으로 알기 위해 사용할 수 있습니다.

예시: 클래스 인스턴스 변수는 서브클래스와 공유되지 않습니다. 우리는 Hyena(하이에나)가 Dog(개)로부터 클래스 인스턴스 변수 count를 상속받는 위치에서, 클래스 Dog(개)와 Hyena(하이에나)를 정의할 것입니다.


클래스 5.2 Dog와 Hyena

Object subclass: #Dog
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''
  category: 'SBE--CIV'

Dog class
  instanceVariableNames: 'count'

Dog subclass: #Hyena
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''
  category: 'SBE--CIV'


지금, 우리는 Dog의 count(카운트)를 0으로 초기화 하고, 새로운 인스턴스가 만들어 질 때 count가 증가하도록 Dog를 위한 클래스 메서드를 정의한다고 가정해 보겠습니다:


메서드 5.3: 새로운 개의 count 유지하기

Dog class»initialize
  super initialize.
  count := 0.

Dog class»new
  count := count +1.
   super new

Dog class»count
   count


지금, 우리가 새로운 Dog를 만들 때, 그것의 count가 증가되며, Hyena를 만들 때도 마찬가지이지만, 개와 하이에나의 count는 서로 분리됩니다:

Dog initialize.  
Hyena initialize.  
Dog count ⇒ 0
Hyena count ⇒ 0
Dog new.  
Dog count ⇒ 1
Dog new.  
Dog count ⇒ 2
Hyena new.  
Hyena count ⇒ 1


클래스 인스턴스 변수는 인스턴스 변수가 인스턴스에 개별적(private)인 것과 똑같이, 클래스 인스턴스 변수 또한 클래스에 개별적이라는 사실에 주의해 주십시오. 클래스와 그 클래스의 인스턴스들이 다른 오브젝트들인 것처럼, 이 사실은 다음의 즉각적인 결과들을 갖게 됩니다.


클래스는 그 자체의 인스턴스 변수에 대한 접근성을 소유하지 않습니다.


클래스의 인스턴스는 그 자체의 클래스가 갖는 클래스 인스턴스 변수에 대한 접근 권한을 가지지 않습니다.


이러한 이유 때문에, 인스턴스 초기화 메서드는 항상 인스턴스 측면에서 정의해야 합니다- 클래스 측면에서는 인스턴스 변수에 대한 접근을 갖지 않으므로, 그 변수들을 초기화할 수 없습니다. 클래스가 할 수 있는 작업은, 단지 새롭게 만들어진 인스턴스에 접근자를 사용하여 초기화 메시지를 보내는 것입니다.

유사하게, 인스턴스들은 접근자 메시지를 그것들의 클래스에 보냄으로서, 클래스 인스턴스 변수에 간접적으로만 접근할 수 있습니다.


Java는 클래스 인스턴스 변수의 등가물을 갖고 있지 않습니다. Java와 C++ 정적 변수는 우리가 섹션 5.7: -모든 서브클래스와 그것들의 모든 인스턴스는 동일한 정적 변수를 공유한다-에서 논의할 스몰토크의 클래스 변수와 좀더 비슷합니다.


Example: 싱글톤을 정의합니다. 싱글톤 패턴[2]은 클래스 인스턴스 변수와 클래스 메서드의 전형적인 사용의 예시를 제공합니다. 우리가 클래스 웹서버(class webserver)를 사용하기를 원하고, 그것이 하나의 인스턴스만을 가지도록 확정하기 위해 싱글톤 패턴을 사용한다고 상상해 봅시다. 브라우저에서 인스턴스 버튼을 클릭하여, 우리는 다음과 같이(클래스 5.4) 클래스 WebServer를 정의할 수 있습니다.


클래스 5.4: 싱글톤 클래스(the singleton class)

Object subclass: #WebServer
  instanceVariableNames: 'sessions'
  classVariableNames: ''
  poolDictionaries: ''
  category: 'Web'


그 다음,class 버튼을 클릭하여, 클래스 측면에서 인스턴수 변수 uniqueInstance를 더합니다.


클래스 5.5 싱글톤 클래스의 클래스 사이드

WebServer class
  instanceVariableNames: 'uniqueInstance'


이 결과는 지금 클래스 WebServer는 superclass와 methodDict와 같은 상속 변수들뿐만 아니라 다른 인스턴스 변수도 갖고 있다는 것을 보여줍니다.

우리는 메서드 5.6에 보이는 것 처럼 uniqueInstance라고 지명된 클래스 메서드를 정의할 수 있습니다. 이 메서드는 uniquenInstance가 초기화 되었는지의 여부를 점검합니다.만약 초기화 되지 않았다면, 메서드는 메서드를 만들고 클래스 인스턴스 변수 uniquenInstance에 그 메서드를 할당할 것입니다. 마침내 uniquenInstance의 값이 리턴됩니다. uniquenInstance가 클래스 인스턴스 변수이므로, 이 메서드는 그 클래스 인스턴스 변수에 직접 접근할 수 있습니다.


메서드 5.6: uniqueInstance (in class side)

WebServer class»uniqueInstance
  uniqueInstance ifNil: [uniqueInstance := self new].
   uniqueInstance


첫 번째로, WebServer uniquenInstance가 실행됩니다. 클래스 WebServer가 만들어질 것이고 uniquenInstance 변수에 할당될 것입니다. 그 다음에는 이전에 만들어진 인스턴스가 새로운 인스턴스를 만드는 대신 리턴될 것입니다.

메서드 5.6에 있는 조건문 내부의 인스턴스 생성 코드가 그 자체로 새롭게 작성된 것이고, WebServer에 새롭게 작성된 것이 아님에 주의하십시오. 차이가 무엇일까요? uniquenInstance 메서드가 WebServer 클래스에서 작성된 것이기 때문에, 여러분은 아마도 둘 다 동일한 것이라고 생각할 것입니다. 그리고, 실제로, WebServer의 서브클래스를 만들 때까지, 둘 다 모두 정확히 같습니다. 그러나 ReliableWebServer가 WebServer의 서브클래스라고 가정하고, uniquenInstance 메서드를 상속해 보십시오. 우리는 ReliableWebServer가 ReliableWebServer에 답변하기 위한 고유한 인스턴스 임을 명백하게 볼 수 있을 것입니다: self 사용은, 이 결과 발생을 보증합니다.그 이유는 이 작업이 각각의 클래스에 묶이기 때문입니다. 또한 WebServer와 RealiableWebServer가 각각 uniqueInstance라 지칭되는 그것들 자신의 클래스를 갖게 될 것이라는 것에 유의 합니다. 이 두 개의 변수는 물론 다른 값을 갖습니다.

Notes

  1. 글쎄요, 거의 대부분의 경우, 스퀵에서, 문자열 pvt로 시작하는 선택자인 메서드는 개별적(private)입니다: pvt 메시지는 오직 그 자체에게만 발송될 수 있습니다. 그럼에도 불구하고 pvt 메서드는 많이 사용하지 않습니다.
  2. Sherman R. Alpert, Kyle Brown and BobbyWoolf, The Design Patterns Smalltalk Companion. Addison Wesley, 1998, ISBN 0–201–18462–1