SqueakByExample:5.7: Difference between revisions

From 흡혈양파의 번역工房
Jump to navigation Jump to search
(이미지 스타일 수정)
 
(번역수정)
 
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
==공유 변수==
==공유 변수==


이제 우리는, 우리의 다섯가지 규칙들: 공유된 변수들, 에 의해 쉽게 다룰 수 없는 스몰토크의 일면을 살펴볼 것입니다.
이제 다섯 가지 규칙이 잘 적용되지 않는 스몰토크의 다른부분을 살펴보겠습니다 : 공유변수<sup>Shared variables</sup> 입니다.


스몰토크는 3가지 종류의 공유된 변수가 있습니다: (1) 글로벌 공유 변수 (2) 인스턴스와 클래스 사이에 공유된 변수(클래스 변수) 그리고 (3)클래스의 그룹 사이에서 공유된 변수[그룹(pool) 변수들)모든 이 공유된 변수들은 우리에게, 그 변수들이 정말로 공유되었다는 것을 경고하기 위해 대문자로 시작됩니다.  
스몰토크에는 3가지 종류의 공유변수가 있습니다: (1) 전역 공유 변수 (2) 인스턴스와 클래스 사이에서 공유되는 변수(클래스 변수) 그리고 (3)클래스의 그룹(Category) 사이에서 공유된 변수(pool 변수)들. 이 세가지의 공유변수들은 사용자에게, 변수들은 공유된 변수라는 것을 알려주기 위해 대문자로 시작됩니다.  






===글로벌 변수===
===전역 변수===


스퀵에서, 모든 글로벌 변수는 클래스 SystemDictionary의 인스턴스로서 실행된 스몰토크라 불리는 이름영역에 저장됩니다. 글로벌 변수는 어디서나 접근할 수 있습니다. 모든 클래스는 글로벌 변수에 의해 이름이 작성되며; 이에 더하며, 몇 가지 글로벌 변수는 특별한 이름을 만드는 작업에 사용하거나 일반적으로 유용한 오브젝트에 사용됩니다.  
스퀵에서, 모든 전역 변수는 클래스 SystemDictionary 의 인스턴스로서 실행되며, Smalltalk 라고 불리는 네임스페이스에 저장됩니다. 스몰토크의 전역 변수는 어디서나 접근이 가능합니다. 모든 클래스는 전역 변수 같은 형식의 이름을 가지게 됩니다.<ref name="역자주1">첫 문자가 대문자인 규칙을 말하는듯 합니다.</ref> 게다가, 일부 이름은 특별하거나 일반적으로 유용한 객체를 이름지을 때 사용합니다.


변수 Transcript는 스크롤되는 창에 기록하는 스트림인 Transcript Stream의 인스턴스 이름을 작성하고, Transcript에서 다음 라인으로 이동합니다.
Transcript 변수는 스크롤 창에 데이터를 기록하는 스트림인 TranscriptStream 인스턴스의 변수명 입니다. 다음 코드는 Transcript에 일부 정보를 표시하고 표시 커서를 다음 줄로 이동시킵니다.


<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Line 17: Line 17:
</syntaxhighlight>
</syntaxhighlight>


{{Template:HighlightGray|do it}} 을 클릭하기 전에, ''Tool'' 플랩 에서 Transcript를 드래그하여 트랜스크립트를 엽니다.  
{{Template:HighlightGray|do it}} 을 클릭하기 전에, ''Tool'' 플랩 에서 Transcript 를 드래그하여 트랜스크립트를 열어야 합니다.  




{{Template:HighlightBox|HINT}} ''트랜스크립트에 기록하는 작업은 트랜스크립트창이 열렸을 때 특히 느리게 진행됩니다. 따라서, 여러분이 트랜스크립트에 기록을 할 때 매우 느려지는 것을 경험한다면, 창을 닫는 것을 고려해 보십시오.''
{{Template:HighlightBox|HINT}} ''Transcript 에 데이터를 기록하는 작업은 Transcript 창이 열렸을 때 더 느려집니다. 따라서, Transcript 에 데이터를 기록하는 동작이 느리다고 느껴진다면, Transcript 창을 닫는 것을 고려해 보십시오.''


===다른 유용한 전역 변수들===


 
* Smalltalk는 스몰토크 자신을 포함해서, 모든 전역 변수를 정의하는 SystemDictionary 의 인스턴스 입니다. SystemDictionary 라는 dictionary의 핵심은 스몰토크 코드에서 전역 오브젝트를 이름짓는 심볼입니다. 아래 예를 보겠습니다.<syntaxhighlight lang="smalltalk">
===다른 유용한 글로벌 변수들===
 
* 스몰토크는 스몰토크 자체를 포함하여 모든 글로벌 변수를 정의하는 SystemDictionary의 인스턴스 입니다. 이 사전에 대한 키들은 스몰토크 코드에서 글로벌 오브젝트 이름을 만드는 심볼들입니다. 그러므로 예를 들어,<br><syntaxhighlight lang="smalltalk">
Smalltalk at: #Boolean  ⇒  Boolean
Smalltalk at: #Boolean  ⇒  Boolean
</syntaxhighlight><br>스몰토크 자체가 글로벌 변수이므로,<br><syntaxhighlight lang="smalltalk">
</syntaxhighlight><br>Smalltalk 자체가 전역 변수이므로,<br><syntaxhighlight lang="smalltalk">
Smalltalk at: #Smalltalk  ⇒  a SystemDictionary(lots of globals)}
Smalltalk at: #Smalltalk  ⇒  a SystemDictionary(lots of globals)}
</syntaxhighlight><br>그리고<br><syntaxhighlight lang="smalltalk">
</syntaxhighlight><br>그리고<br><syntaxhighlight lang="smalltalk">
(Smalltalk at: #Smalltalk) == Smalltalk  ⇒    true
(Smalltalk at: #Smalltalk) == Smalltalk  ⇒    true
</syntaxhighlight>  
</syntaxhighlight>
* Sensor는 EventSensor의 인스턴스이며, Squeak에 입력을 표시합니다. 예를 들면, Sensor keyborad는 키보드에 다음 문자 입력을 답하며, Sensor mousePoint가 Point는 현재 마우스 위치를 가리키고 있다고 답하는 반면에, 만약 왼쪽 쉬프트 키가 눌려 있는 상태인 경우 Sensor leftShiftDown은 true로 응답합니다. • World는 화면을 표시하는 PasteUpMorph의 인스턴스입니다. World bounds 는 전체 화면 공간을 정의하는 직사각형을 답으로 내놓으며, 화면의 모든 모프는 World의 서브모프입니다.
* Sensor는 EventSensor의 인스턴스이며, Squeak으로 들어가는 입력들을 처리합니다. 예를 들면, Sensor keyborad는 키보드에 다음 문자 입력에 응답하며, Sensor mousePoint 는 Point는 현재 마우스 위치를 알려주는 반면에, 만약 왼쪽 쉬프트 키가 눌려 있는 상태인 경우 Sensor leftShiftDown 은 true로 응답됩니다.
* World 는 화면을 표시하는 PasteUpMorph 클래스의 인스턴스입니다. World bounds 는 전체 화면 공간을 정의하는 직사각형의 정보를 반환하며, 화면의 모든 모프는 World 의 하위모프가 됩니다.
* ActiveHand는 HandMorph의 현재 인스턴스이며, 커서의 그래픽 표현입니다.  ActiveHand의 서브모프는 마우스로 드래그한 모든 것을 잡습니다.
* ActiveHand는 HandMorph의 현재 인스턴스이며, 커서의 그래픽 표현입니다.  ActiveHand의 서브모프는 마우스로 드래그한 모든 것을 잡습니다.
* Undeclared 는 다른 딕셔너리입니다- 이것은 모든 선언하지 않은 변수들을 포함합니다. 만약 여러분이 선언하지 않은 함수를 참조하는 메서드를 작성하실 것이라면, 브라우저는 보통 그 것을 선언하기 위해 여러분을 프롬프트 상태로 만들 것입니다. 그럼에도 불구하고, 만약 여러분이 나중에 선언문을 지운다면, 코드는 선언하지 않은 변수들을 참조할 것입니다. Undeclared 점검하는 작업은 때로는 이상한 동작 설명을 도울 수 있습니다.  
* Undeclared 는 또다른 dictionary입니다 - Undeclared dictionary에는 모든 미리 선언되지 않은 변수들이 포함됩니다. 만약 당신이 선언하지 않은 함수를 참조하는 메서드를 작성하게 된다면, 시스템 브라우저는 대개의 경우 선언되지 않은 함수를 선언하도록 알려줍니다. 하지만, 만약 나중에 해당되는 선언문을 삭제한다면, 코드는 선언문이 지워졌기 때문에, dictionary에는 있지만 실제로는 선언되지 않은 변수들을 참조하는 상황이 됩니다. Undeclared dictionary를 살펴보면 가끔 이상한 동작을 하는경우에 대해 도움을 얻을 있는 경우도 있습니다.
* SystemOrganization 은 SystemOrganizer의 인스턴스입니다: 이것은 패키지에 클래스들의 조직을 기록합니다. 좀 더 정확하게 말하자면, 이것은 클래스들의 이름을 그룹으로 만들어 분류(categorizes)합니다. 그러므로<br><syntaxhighlight lang="smalltalk">
* SystemOrganization 은 SystemOrganizer 클래스의 인스턴스입니다: SystemOrganization은 패키지별로 클래스들의 그룹을 기록합니다. 좀 더 정확하게 말하자면, 이것은 클래스들의 이름을 그룹으로 만들어 분류(categorizes)합니다. 그 덕분에 다음과 같은 코드로 결과를 얻을 수 있습니다<ref name="역자주2">이 부분에 대한 내용은 [http://ta.onionmixer.net/wordpress/?p=180 여기]를 보면 아주 조금 더 자세하게 볼 수 있습니다</ref>.<syntaxhighlight lang="smalltalk">
SystemOrganization categoryOfElement: #Magnitude  ⇒  #'Kernel--Numbers'
SystemOrganization categoryOfElement: #Magnitude  ⇒  #'Kernel-Numbers'
</syntaxhighlight>
</syntaxhighlight>




현재의 실행은 글로벌 변수들의 사용을 엄격히 제한하므로, 인스턴스 변수 또는 클래스 변수를 사용하거나, 글로벌 변수들에 접근하기 위해 클래스 메서드를 제공하는 방법이 더 나은 방법입니다. 실제로, 만약 스퀵이 현재 버전의 스크래치(프로그래밍 언어의 일종:역주)로부터 실행될 것이었다면, 클래스가 아닌 대부분의 글로벌 함수들은 아마도 싱글톤으로 대체할 것입니다.
현재 진행하실 연습에서는 전역 변수의 사용을 엄격히 제한해주세요. 인스턴스 변수 또는 클래스 변수를 사용하거나, 전역 변수들에 접근하기 위해서 클래스 메서드를 제공하는 방법이 더 좋은 방법입니다. 정말로, 스퀵을 현재 상태로 바닥부터 구현한다면, 클래스가 아닌 대부분의 전역번수들은 아마도 Singleton 으로 대체되었을 겁니다.


글로벌 변수를 정의하는 일상적인 방법은, 대문자로 변경되었지만 선언하지 않은 식별자에 대한 할당에, 단지 {{Template:HighlightGray|do it}}을 실행하는 것입니다. 파서는 그 다음, 여러분을 위해 선언할 글로벌 변수를 제공할 것입니다. 만약 여러분이 프로그램으로 글로벌 변수들을 정의하기 원하신다면, Smalltalk at: #AGlobalName put: nil을 실행할 수 있습니다. 이것을 제거하려면, Smalltalk removeKey: #AGlobalName를 실행합니다.
보통 전역 요소를 정의할때는 머릿 글자를 대문자로 하였지만 아직 선언하지 않은 식별자<sup>undeclared identifier</sup>를 마우스로 영역선택하고 {{Template:HighlightGray|do it}}을 실행하면 됩니다. 파서<sup>parser</sup>는 그 다음, 사용자를 위해 선언할 전역 변수를 제공할 것입니다. 만약 프로그램문장 으로 전역 변수를 정의하기 원한다면, Smalltalk at: #AGlobalName put: nil 을 실행하면 됩니다. 이렇게 만들어진 변수를 제거하려면, Smalltalk removeKey: #AGlobalName 를 실행합니다.




Line 49: Line 48:
===클래스 변수===
===클래스 변수===


때때로, 우리는 모든 클래스의 인스턴스들과 클래스 자체들 사이에 몇 가지 데이터를 공유할 필요가 있습니다. 이 작업은 클래스 변수를 사용하여 수행할 수 있습니다. 클래스 변수라는 용어는 변수의 수명이 클래스의 수명과 같다는 의미를 갖습니다. 그럼에도 불구하고, 이 용어가 담지 못하는 내용은 이 변수들이 그림 5.5에 보이는 것처럼 클래스 자체 뿐만 아니라 클래스의 모든 인스턴스들 사이에서도 공유된다는 것을 포함합니다. 실제로, 클래스 변수 보다 좀 더 나은 이름은 ‘공유 변수’ 일 것입니다. 왜냐하면, 이 이름이 좀 더 명확하게 그 변수의 역할을 설명해주기 때문이며 또한, 변수들이 수정되었을 경우, 사용의 위험성을 사용자에게 경고하기 때문입니다.
때로는 클래스 인스턴스와 클래스 자신 양쪽에 무슨 데이터든 공유할 필요가 있습니다. 이런 작업에는 클래스 변수를 사용하면 됩니다. 클래스 변수라는 용어는 변수의 수명이 클래스의 수명과 같음을 나타냅니다. 하지만 이런 의미로는 좀 모자라는것이, 클래스 변수는  그림 5.5에 보이는 것처럼 클래스 자체 뿐만 아니라 클래스의 모든 인스턴스들 사이에서도 그 내용이 공유되기 때문입니다. 실제로, 클래스 변수 보다 좀 더 나은 이름을 쓴다면 '공유 변수' 가 되겠죠. 왜냐하면, 이 이름이 좀 더 명확하게 클래스변수의 역할을 설명해주기 때문이며 또한, 변수들이 수정되는 경우, 사용상 위험성을 사용자에게 경고하기 때문입니다.


[[image:privateSharedVarColor.png|none|800px|thumb|그림 5.5: 다양한 변수들에 접근하는 인스턴스와 클래스 메서드]]
[[image:privateSharedVarColor.png|none|800px|thumb|그림 5.5: 다양한 변수들에 접근하는 인스턴스와 클래스 메서드]]




그림 5.5에서, 우리는 rgb와 cachedDepth가 Color의 인스턴스 변수인 것을 알 수 있으며, Color 인스턴스에 유일하게 접근할 수 있는 대상들임을 또한 알 수 있습니다. 우리는 또한 superclass,subclass,methodDic와 다른 것들이 클래스 인스턴스 변수임을 알 수 있습니다. 예컨대, 인스턴스 변수들은 오직 color 클래스에 접근할 수 있습니다.
그림 5.5에서, 우리는 rgb와 cachedDepth가 Color 인스턴스의 인스턴스 변수인 것을 알 수 있으며, Color 인스턴스만 접근이 가능한 존재라는걸 알 수 있습니다<ref name="역자주3">스몰토크에서 변수는 일반적으로 Private으로 취급되기 때문입니다</ref>. 또한 superclass, subclass, methodDic 과 다른 것들이 color 클래스의 클래스 인스턴스 변수임을 알 수 있습니다. 말하자면, 이런 클래스 인스턴스 변수들은 오직 color 클래스에서만 접근이 가능다는 내용입니다.


그러나 우리는 또한 약간 새로운 것을 볼 수 있습니다: ColorNames 와 CachedColormaps는 Color를 위해 정의된 class 변수입니다. 이 변수들의 대문자화는 우리에게 이 변수들이 공유되었다는 힌트를 제공합니다. 사실, Color의 모든 인스턴스들은 공유된 변수들에 접근할 수 있을 뿐만 아니라, Color 클래스 자체와 그 클래스의 모든 하위 클래스에도 접근할 수 있습니다. 인스턴스 메서드와 클래스 메서드 둘 모두, 이 공유된 변수들에 접근할 수 있습니다.  
그러나 약간 새로운 점도 있습니다: ColorNames 와 CachedColormaps 는 Color 를 위해 정의된 class 변수입니다. 이 변수들의 이름중 맨 앞글자가 대문자인걸 보면 이것들이 공유변수라는걸 암시합니다. 사실, Color 의 모든 인스턴스는 공유변수들에 접근가능할 뿐만 아니라, Color 클래스 자체와 그 클래스의 모든 서브클래스도 공유변수에 대한 접근이 가능합니다. 인스턴스 메서드와 클래스 메서드 양쪽다, 이 공유변수에 접근이 가능합니다.


클래스 변수는 클래스 정의 템플릿에 선언합니다. 예를 들면, 클래스 색상은 color 생성 속도를 높이기 위해 많은 수의 클래스 변수들을 정의하며, 그 변수의 정의는 아래에 보입니다. (클래스 5.20)
클래스 변수는 클래스 정의 템플릿에 선언됩니다. 예를 들어, Color 클래스에서는 색상을 빨리 만들기 위한 수많은 클래스 변수를 정의하고 있으며, 내용은 아래에서 볼 수 있습니다. (클래스 5.20)




Line 80: Line 79:




클래스 변수 ColorNames는 빈번하게 사용되는 색상의 이름을 포함하고 있는 배열입니다. 이 배열은 color의 모든 인스턴스에 의해 공유되며, 배열의 서브클래스는 TranslucentColor입니다. 이 배열은 모든 인스턴스와 클래스 메서드들로 부터 접근할 수 있습니다.  
클래스 변수인 ColorNames 는 자주쓰이는 색상의 이름을 포함하고 있는 배열입니다. 이 배열은 color의 모든 인스턴스에서 공유되며, 배열의 서브클래스는 TranslucentColor 입니다. 이 배열은 모든 인스턴스와 클래스 메서드들로 부터 접근 가능합니다.


ColorNames는 일단 Color class»initializeNames에서 초기화되지만, Color의 인스턴스로부터 접근됩니다. 메서드 Color»name은 color의 이름을 찾기 위해 변수를 사용합니다. 대부분의 color가 이름을 갖고 있지 않기 대문에, 모든 color에 인스턴스 변수 이름을 더하는 것이 적절한 것으로 간주됩니다.
클래스 변수인 ColorNames 는 일단 Color클래스>>initializeNames 에서 초기화되지만, Color 클래스의 인스턴스들이 접근(사용)합니다. 메서드 Color>>name 은 색상의 이름을 찾기 위해 ColorNames 변수를 사용합니다. 모든 색상의 이름이 있는건 아니라서, 인스턴스 변수에 대부분 색상의 이름을 추가하는건 좋지 않습니다.






===클래스 초기화===
====클래스 초기화====


클래스 변수의 존재는, 다음과 같은 질문을 떠오르게 합니다: 어떻게 이 변수를 초기화 할까? 이것에 대한 한 가지 해결책은 게으른 초기화(lazy initialization) 입니다. 이 초기화는 실행될 때, 변수가 아직 초기화 되지 않았을 경우, 변수를 초기화 하는 접근자 메서드를 소개함으로써, 수행됩니다. 게으른 초기화는 우리가 항상 접근자를 사용하고 클래스 변수를 결코 직접 사용하면 안 된다는 사실을 암시합니다. 이 작업은 더 나아가 접근자 보내기와 초기화 테스트의 부담을 사용자에게 전가합니다. 또한 이 초기화는 실제로 더 이상 공유되지 않기 때문에, 클래스 변수 사용을 거의 틀림없이 무산시킵니다.
클래스 변수를 보면 이런 의문이 생깁니다: 어떻게 초기화하지? 이런 질문에 대한 해결책중 하나는 게으른초기화(lazy initialization) 입니다. 아직 초기화가 되지 않은 변수에 접근할 때 초기화를 진행하는 접근자 메서드를 도입하면 가능합니다. 하지만 게으른 초기화는 언제나 접근자를 사용해야 하며 클래스 변수를 직접 사용하면 안된다는 의미가 됩니다. 더욱이 접근자 전송과 초기화 테스트의 부담을 보태게 되죠. 또한 이 방법은 클래스 변수를 사용하는 이유을 없애버립니다, 왜냐하면 사실상 클래스변수가 더 이상 공유 되지 않기 때문입니다.




메서드 5.21: Color class»colorNames
메서드 5.21: Color class>>colorNames
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Color class»colorNames
Color class>>colorNames
   ColorNames ifNil: [self initializeNames].
   ColorNames ifNil: [self initializeNames].
   ↑ ColorNames
   ↑ ColorNames
Line 99: Line 98:




다른 해결책은, 클래스 메서드 초기화를 재지정하는 것입니다.  
게으른초기화 외의 다른방법은, 클래스 메서드 초기화를 재지정<sup>override</sup> 하는 것입니다.


메서드 5.22: Color class»initialize
메서드 5.22: Color class>>initialize
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Color class»initialize
Color class>>initialize
   ...
   ...
   self initializeNames
   self initializeNames
Line 109: Line 108:




만약 여러분이 이 해결책을 채택한다면, 여러분이 초기화 메서드를 정의한 다음 (예를 들어) Color initialize를 수행하여 초기화 메서드를 불러올 필요성을 기억하실 필요가 있습니다. 비록 클래스 측면 초기화 메서드들이 코드가 메모리에 로드 될 때, 자동으로 실행되지만, 그 메서드 들은 브라우저에 처음 타이핑 되거나 컴파일 되거나 또는 수정되거나 다시 컴파일 될 때, 자동으로 실행되지 않습니다.
만약 초기화를 재지정하는 방법을 쓴다면 initialize 메서드를 정의한다음 실행해야 할 필요가 있다는걸 알고있어야 하는데 Color initialize 를 실행하는것처럼 하면 됩니다. 비록 클래스의 코드가 메모리에 로드될때 (시스템브라우저의)클래스측면에서 볼 수 있는 초기화 메서드들이 자동으로 실행되지만, 시스템 브라우저 에서 처음 입력하고 편집 및 컴파일했을때도 자동으로 실행되는건 아닙니다.




Line 115: Line 114:
===Pool 변수===
===Pool 변수===


pool 변수들은 상속으로 연관되지 않을 수 있는 여러 개의 클래스들 사이에서 공유되는 변수들입니다. pool 변수들은 본래 pool 딕셔너리에 저장되지만, 지금은, 개별 클래스(Sharedpool의 하위 클래스)의 클래스 변수로서 정의되어야만 합니다.  
Pool 변수는 상속 관계가 없을 수 있는 수많은 클래스사이에 공유되는 변수입니다. Pool 변수는 원래 pool dictionary에 저장됩니다; 하지만 지금은 단독 클래스(SharedPool의 서브클래스)의 클래스 변수처럼 정의해야 합니다. 우리는 변수를 사용하지 않기를 권합니다. 왜냐하면 매우 특히한 상황에서만 Pool변수가 필요하기 때문이죠. 여기서 Pool변수를 설명하는 이유는 코드를 분석할때 도움이 되도록 하기 위함입니다.
우리가 드릴 조언은 변수들을 회피하시라는 것이며, 그 이유는 매우 드물고 특정한 상황들에서만 이 변수들이 필요하기 때문입니다. 이 지면에서 우리의 목표는 여러분이 코드를 읽을 때, 이해하실 수 있도록 Pool 변수를 충분히 설명하는 것입니다.


pool 변수에 접근하는 클래스는 클래스 정의에서 반드시 pool을 언급해야 합니다. 예를 들면, 클래스 Text는 그것이 pool 딕셔너리 Textcontansts를 사용하고 있다는 것을 가리키며, 이것은 CR과 LF와 같은 모든 문자 상수를 포함합니다.
pool 변수에 접근하는 클래스는 접근을 원하는 클래스 정의에서 반드시 pool을 언급해야 합니다. 예를 들면, Text 클래스는 자신이 pool dictionary인 TextContansts를 사용하고 있다는걸 선언하며, TextContansts 라는 Pool변수는 CR과 LF와 같은 모든 문자 상수를 포함합니다. TextContansts dictionary는, 예를 들면 캐리지 리턴<sup>carriage return</sup> 문자값을 가진 Character cr 에 묶인 key #CR 등을 가지고 있습니다<ref name="역자주4">system browser에서 해당되는 class를 선택한후 instance버튼을 눌러 인스턴스 메서드를 확인하면 내용을 정확히 알 수 있습니다.</ref>.
이 사전은 예를 들면 캐리지 리턴 문자와 같은 값 Character cr에 묶인 key #CR을 갖고 있습니다.  




클래스 5.23: Text 클래스에 있는 Pool 딕셔너리
클래스 5.23: Text 클래스에 있는 Pool dictionary
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
ArrayedCollection subclass: #Text
ArrayedCollection subclass: #Text
Line 132: Line 129:




이는 예를 들어, 명백한 딕셔너리 검색 보다는 변수 구문를 사용하여, 메서드 코드 내용에 있는 딕셔너리의 key들에 접근하기 위해 클래스 Text의 메서드를 허용합니다. 예를 들어, 우리는 다음 메서드를 작성할 수 있습니다.
이러한 poolDictionary의 선언은, 클래스 5.23에 있는 Text 클래스의 메서드가 메서드 내용의 dictionary 키로 직접<sup>directly</sup> 접근하는 것을 허용합니다. 예를 들어, 다음과같은 메서드를 작성할 수 있다는 얘기죠.




메서드 5.24: Text»testCR
메서드 5.24: Text»testCR
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Text»testCR
Text>>testCR
   ↑ CR == Character cr
   ↑ CR == Character cr
</syntaxhighlight>
</syntaxhighlight>




다시 말해서, 우리는 여러분에게 pool 변수와 pool 딕셔너리 사용을 피하실 것을 권합니다.
다시 말하지만, 우리는 당신이 pool 변수와 pool dictionary 사용을 피하실 것을 권장하는 바입니다.
 





Latest revision as of 02:34, 17 September 2013

공유 변수

이제 다섯 가지 규칙이 잘 적용되지 않는 스몰토크의 다른부분을 살펴보겠습니다 : 공유변수Shared variables 입니다.

스몰토크에는 3가지 종류의 공유변수가 있습니다: (1) 전역 공유 변수 (2) 인스턴스와 클래스 사이에서 공유되는 변수(클래스 변수) 그리고 (3)클래스의 그룹(Category) 사이에서 공유된 변수(pool 변수)들. 이 세가지의 공유변수들은 사용자에게, 이 변수들은 공유된 변수라는 것을 알려주기 위해 대문자로 시작됩니다.


전역 변수

스퀵에서, 모든 전역 변수는 클래스 SystemDictionary 의 인스턴스로서 실행되며, Smalltalk 라고 불리는 네임스페이스에 저장됩니다. 스몰토크의 전역 변수는 어디서나 접근이 가능합니다. 모든 클래스는 전역 변수 같은 형식의 이름을 가지게 됩니다.[1] 게다가, 일부 이름은 특별하거나 일반적으로 유용한 객체를 이름지을 때 사용합니다.

Transcript 변수는 스크롤 창에 데이터를 기록하는 스트림인 TranscriptStream 인스턴스의 변수명 입니다. 다음 코드는 Transcript에 일부 정보를 표시하고 표시 커서를 다음 줄로 이동시킵니다.

Transcript show: 'Squeak is fun and powerful' ; cr

do it 을 클릭하기 전에, Tool 플랩 에서 Transcript 를 드래그하여 트랜스크립트를 열어야 합니다.


HINT Transcript 에 데이터를 기록하는 작업은 Transcript 창이 열렸을 때 더 느려집니다. 따라서, Transcript 에 데이터를 기록하는 동작이 느리다고 느껴진다면, Transcript 창을 닫는 것을 고려해 보십시오.

다른 유용한 전역 변수들

  • Smalltalk는 스몰토크 자신을 포함해서, 모든 전역 변수를 정의하는 SystemDictionary 의 인스턴스 입니다. SystemDictionary 라는 dictionary의 핵심은 스몰토크 코드에서 전역 오브젝트를 이름짓는 심볼입니다. 아래 예를 보겠습니다.
    Smalltalk at: #Boolean      Boolean
    

    Smalltalk 자체가 전역 변수이므로,
    Smalltalk at: #Smalltalk      a SystemDictionary(lots of globals)}
    

    그리고
    (Smalltalk at: #Smalltalk) == Smalltalk       true
    
  • Sensor는 EventSensor의 인스턴스이며, Squeak으로 들어가는 입력들을 처리합니다. 예를 들면, Sensor keyborad는 키보드에 다음 문자 입력에 응답하며, Sensor mousePoint 는 Point는 현재 마우스 위치를 알려주는 반면에, 만약 왼쪽 쉬프트 키가 눌려 있는 상태인 경우 Sensor leftShiftDown 은 true로 응답됩니다.
  • World 는 화면을 표시하는 PasteUpMorph 클래스의 인스턴스입니다. World bounds 는 전체 화면 공간을 정의하는 직사각형의 정보를 반환하며, 화면의 모든 모프는 World 의 하위모프가 됩니다.
  • ActiveHand는 HandMorph의 현재 인스턴스이며, 커서의 그래픽 표현입니다. ActiveHand의 서브모프는 마우스로 드래그한 모든 것을 잡습니다.
  • Undeclared 는 또다른 dictionary입니다 - Undeclared dictionary에는 모든 미리 선언되지 않은 변수들이 포함됩니다. 만약 당신이 선언하지 않은 함수를 참조하는 메서드를 작성하게 된다면, 시스템 브라우저는 대개의 경우 선언되지 않은 함수를 선언하도록 알려줍니다. 하지만, 만약 나중에 해당되는 선언문을 삭제한다면, 코드는 선언문이 지워졌기 때문에, dictionary에는 있지만 실제로는 선언되지 않은 변수들을 참조하는 상황이 됩니다. Undeclared dictionary를 살펴보면 가끔 이상한 동작을 하는경우에 대해 도움을 얻을 수 있는 경우도 있습니다.
  • SystemOrganization 은 SystemOrganizer 클래스의 인스턴스입니다: SystemOrganization은 패키지별로 클래스들의 그룹을 기록합니다. 좀 더 정확하게 말하자면, 이것은 클래스들의 이름을 그룹으로 만들어 분류(categorizes)합니다. 그 덕분에 다음과 같은 코드로 결과를 얻을 수 있습니다[2].
    SystemOrganization categoryOfElement: #Magnitude      #'Kernel-Numbers'
    


현재 진행하실 연습에서는 전역 변수의 사용을 엄격히 제한해주세요. 인스턴스 변수 또는 클래스 변수를 사용하거나, 전역 변수들에 접근하기 위해서 클래스 메서드를 제공하는 방법이 더 좋은 방법입니다. 정말로, 스퀵을 현재 상태로 바닥부터 구현한다면, 클래스가 아닌 대부분의 전역번수들은 아마도 Singleton 으로 대체되었을 겁니다.

보통 전역 요소를 정의할때는 머릿 글자를 대문자로 하였지만 아직 선언하지 않은 식별자undeclared identifier를 마우스로 영역선택하고 do it을 실행하면 됩니다. 파서parser는 그 다음, 사용자를 위해 선언할 전역 변수를 제공할 것입니다. 만약 프로그램문장 으로 전역 변수를 정의하기 원한다면, Smalltalk at: #AGlobalName put: nil 을 실행하면 됩니다. 이렇게 만들어진 변수를 제거하려면, Smalltalk removeKey: #AGlobalName 를 실행합니다.


클래스 변수

때로는 클래스 인스턴스와 클래스 자신 양쪽에 무슨 데이터든 공유할 필요가 있습니다. 이런 작업에는 클래스 변수를 사용하면 됩니다. 클래스 변수라는 용어는 변수의 수명이 클래스의 수명과 같음을 나타냅니다. 하지만 이런 의미로는 좀 모자라는것이, 클래스 변수는 그림 5.5에 보이는 것처럼 클래스 자체 뿐만 아니라 클래스의 모든 인스턴스들 사이에서도 그 내용이 공유되기 때문입니다. 실제로, 클래스 변수 보다 좀 더 나은 이름을 쓴다면 '공유 변수' 가 되겠죠. 왜냐하면, 이 이름이 좀 더 명확하게 클래스변수의 역할을 설명해주기 때문이며 또한, 변수들이 수정되는 경우, 사용상 위험성을 사용자에게 경고하기 때문입니다.

그림 5.5: 다양한 변수들에 접근하는 인스턴스와 클래스 메서드


그림 5.5에서, 우리는 rgb와 cachedDepth가 Color 인스턴스의 인스턴스 변수인 것을 알 수 있으며, Color 인스턴스만 접근이 가능한 존재라는걸 알 수 있습니다[3]. 또한 superclass, subclass, methodDic 과 다른 것들이 color 클래스의 클래스 인스턴스 변수임을 알 수 있습니다. 말하자면, 이런 클래스 인스턴스 변수들은 오직 color 클래스에서만 접근이 가능다는 내용입니다.

그러나 약간 새로운 점도 있습니다: ColorNames 와 CachedColormaps 는 Color 를 위해 정의된 class 변수입니다. 이 변수들의 이름중 맨 앞글자가 대문자인걸 보면 이것들이 공유변수라는걸 암시합니다. 사실, Color 의 모든 인스턴스는 이 공유변수들에 접근가능할 뿐만 아니라, Color 클래스 자체와 그 클래스의 모든 서브클래스도 공유변수에 대한 접근이 가능합니다. 인스턴스 메서드와 클래스 메서드 양쪽다, 이 공유변수에 접근이 가능합니다.

클래스 변수는 클래스 정의 템플릿에 선언됩니다. 예를 들어, Color 클래스에서는 색상을 빨리 만들기 위한 수많은 클래스 변수를 정의하고 있으며, 내용은 아래에서 볼 수 있습니다. (클래스 5.20)


클래스 5.20: Color와 Color의 클래스 변수

Object subclass: #Color
  instanceVariableNames: 'rgb cachedDepth cachedBitPattern'
  classVariableNames: 'Black Blue BlueShift Brown CachedColormaps ColorChart
  ColorNames ComponentMask ComponentMax Cyan DarkGray Gray
  GrayToIndexMap Green GreenShift HalfComponentMask HighLightBitmaps
  IndexedColors LightBlue LightBrown LightCyan LightGray LightGreen
  LightMagenta LightOrange LightRed LightYellow Magenta MaskingMap Orange
  PaleBlue PaleBuff PaleGreen PaleMagenta PaleOrange PalePeach PaleRed
  PaleTan PaleYellow PureBlue PureCyan PureGreen PureMagenta PureRed
  PureYellow RandomStream Red RedShift TranslucentPatterns Transparent
  VeryDarkGray VeryLightGray VeryPaleRed VeryVeryDarkGray
  VeryVeryLightGray White Yellow'
  poolDictionaries: ''
  category: 'Graphics--Primitives'


클래스 변수인 ColorNames 는 자주쓰이는 색상의 이름을 포함하고 있는 배열입니다. 이 배열은 color의 모든 인스턴스에서 공유되며, 배열의 서브클래스는 TranslucentColor 입니다. 이 배열은 모든 인스턴스와 클래스 메서드들로 부터 접근 가능합니다.

클래스 변수인 ColorNames 는 일단 Color클래스>>initializeNames 에서 초기화되지만, Color 클래스의 인스턴스들이 접근(사용)합니다. 메서드 Color>>name 은 색상의 이름을 찾기 위해 ColorNames 변수를 사용합니다. 모든 색상의 이름이 있는건 아니라서, 인스턴스 변수에 대부분 색상의 이름을 추가하는건 좋지 않습니다.


클래스 초기화

클래스 변수를 보면 이런 의문이 생깁니다: 어떻게 초기화하지? 이런 질문에 대한 해결책중 하나는 게으른초기화(lazy initialization) 입니다. 아직 초기화가 되지 않은 변수에 접근할 때 초기화를 진행하는 접근자 메서드를 도입하면 가능합니다. 하지만 게으른 초기화는 언제나 접근자를 사용해야 하며 클래스 변수를 직접 사용하면 안된다는 의미가 됩니다. 더욱이 접근자 전송과 초기화 테스트의 부담을 보태게 되죠. 또한 이 방법은 클래스 변수를 사용하는 이유을 없애버립니다, 왜냐하면 사실상 클래스변수가 더 이상 공유 되지 않기 때문입니다.


메서드 5.21: Color class>>colorNames

Color class>>colorNames
  ColorNames ifNil: [self initializeNames].
   ColorNames


게으른초기화 외의 다른방법은, 클래스 메서드 초기화를 재지정override 하는 것입니다.

메서드 5.22: Color class>>initialize

Color class>>initialize
  ...
  self initializeNames


만약 초기화를 재지정하는 방법을 쓴다면 initialize 메서드를 정의한다음 실행해야 할 필요가 있다는걸 알고있어야 하는데 Color initialize 를 실행하는것처럼 하면 됩니다. 비록 클래스의 코드가 메모리에 로드될때 (시스템브라우저의)클래스측면에서 볼 수 있는 초기화 메서드들이 자동으로 실행되지만, 시스템 브라우저 에서 처음 입력하고 편집 및 컴파일했을때도 자동으로 실행되는건 아닙니다.


Pool 변수

Pool 변수는 상속 관계가 없을 수 있는 수많은 클래스사이에 공유되는 변수입니다. Pool 변수는 원래 pool dictionary에 저장됩니다; 하지만 지금은 단독 클래스(SharedPool의 서브클래스)의 클래스 변수처럼 정의해야 합니다. 우리는 이 변수를 사용하지 않기를 권합니다. 왜냐하면 매우 특히한 상황에서만 Pool변수가 필요하기 때문이죠. 여기서 Pool변수를 설명하는 이유는 코드를 분석할때 도움이 되도록 하기 위함입니다.

pool 변수에 접근하는 클래스는 접근을 원하는 클래스 정의에서 반드시 pool을 언급해야 합니다. 예를 들면, Text 클래스는 자신이 pool dictionary인 TextContansts를 사용하고 있다는걸 선언하며, TextContansts 라는 Pool변수는 CR과 LF와 같은 모든 문자 상수를 포함합니다. TextContansts dictionary는, 예를 들면 캐리지 리턴carriage return 문자값을 가진 Character cr 에 묶인 key #CR 등을 가지고 있습니다[4].


클래스 5.23: Text 클래스에 있는 Pool dictionary

ArrayedCollection subclass: #Text
  instanceVariableNames: 'string runs'
  classVariableNames: ''
  poolDictionaries: TextConstants
  category: 'Collections--Text'


이러한 poolDictionary의 선언은, 클래스 5.23에 있는 Text 클래스의 메서드가 메서드 내용의 dictionary 키로 직접directly 접근하는 것을 허용합니다. 예를 들어, 다음과같은 메서드를 작성할 수 있다는 얘기죠.


메서드 5.24: Text»testCR

Text>>testCR
   CR == Character cr


다시 말하지만, 우리는 당신이 pool 변수와 pool dictionary 사용을 피하실 것을 권장하는 바입니다.


Notes

  1. 첫 문자가 대문자인 규칙을 말하는듯 합니다.
  2. 이 부분에 대한 내용은 여기를 보면 아주 조금 더 자세하게 볼 수 있습니다
  3. 스몰토크에서 변수는 일반적으로 Private으로 취급되기 때문입니다
  4. system browser에서 해당되는 class를 선택한후 instance버튼을 눌러 인스턴스 메서드를 확인하면 내용을 정확히 알 수 있습니다.