SqueakByExample:5.3: Difference between revisions

From 흡혈양파의 번역工房
Jump to navigation Jump to search
(번역수정)
(용어수정)
 
(12 intermediate revisions by the same user not shown)
Line 1: Line 1:
==모든 오브젝트는 클래스의 인스턴스다==
==모든 객체는 클래스의 인스턴스입니다==


모든 오브젝트는 클래스를 가지고 있으며, 스몰토크는 오브젝트에 class라는 메시지를 보내서 원하는 오브젝트의 클래스를 알아낼 수 있습니다.
모든 객체는 클래스를 가지고 있으며, 스몰토크는 객체에 class라는 메시지를 보내어 원하는 객체의 클래스를 알아낼 수 있습니다.


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




클래스는 인스턴스 변수를 통해 클래스 스스로에 대한 인스턴스의 구조를 정의하며, 메서드를 통해 클래스 인스턴스의 동작을 정의합니다. 메서드는 다른곳에서 호출될 수 있는 고유한 이름을 가진 셀렉터로서 클래스안에 있습니다.
클래스는 인스턴스 변수를 통해 클래스 스스로에 대한 인스턴스의 '''구조'''를 정의하고, 메서드를 통해 클래스 인스턴스의 '''동작'''을 정의합니다. 메서드는 다른곳에서 호출될 수 있는 고유한 이름을 가진 셀렉터로서 클래스안에 있습니다.


클래스는 오브젝트이고, 모든 오브젝트는 클래스의 인스턴스이기 때문에, 클래스는 반드시 클래스의 인스턴스가 되어야 한다는 규칙이 있습니다. 이렇게 클래스의 인스턴스가 되는 클래스를 메타클래스라고 합니다. 여러분이 클래스를 만들 때마다, 시스템은 자동으로 여러분을 위한 메타클래스를 만듭니다. 메타 클래스는 자신의 인스턴스인 클래스의 구조와 동작을 정의합니다. 99%의 경우, 여러분은 메타클래스에 관해 생각할 필요가 없으며, 기꺼이 무시해도 됩니다. (우리는 12장에서 메타클래스를 상세히 살펴볼 것입니다.)
'''클래스는 객체'''이고, '''모든 객체는 클래스의 인스턴스'''이기 때문에, 클래스는 반드시 클래스의 인스턴스가 되어야 한다는 규칙이 따라옵니다. 클래스가 인스턴스인 클래스를 '''메타 클래스'''라고 합니다. 클래스를 만들 때마다 언제든지 시스템은 메타 클래스를 자동으로 만들어줍니다. 메타 클래스는 자신의 인스턴스가 되는 클래스의 구조와 동작을 정의합니다. 99%의 경우, 메타 클래스에 대해 생각할 필요가 없으며, 기꺼이 무시해도 됩니다. (12장에서 메타클래스를 좀 더 자세히 살펴보겠습니다.)




Line 27: Line 27:
===인스턴스 변수===
===인스턴스 변수===


스몰토크에서 인스턴수 변수는 해당 인스턴스 내부(private)에서만 사용됩니다. 스몰토크와는 반대로 인스턴스 변수에 대한 외부접근을 허용하는 Java와 C++등에서는(이러한 변수를 필드 또는 멤버변수라고 합니다) 같은 클래스에서 만들어진 다른 어떤 인스턴스에서도 인스턴스 변수에 접근이 가능합니다. Java와 C++에서는 오브젝트의 캡슐화 범위를 클래스까지로 보지만 스몰토크에서는 인스턴스까지를 캡슐화 범위로 생각해야 합니다.
스몰토크에서 인스턴수 변수는 해당 인스턴스 내부(private)에서만 사용합니다. 스몰토크와는 반대로 인스턴스 변수에 대한 외부접근을 허용하는 Java와 C++등에서는(이러한 변수를 필드 또는 멤버변수라고 합니다) 같은 클래스에서 만들어진 다른 어떤 인스턴스에서도 인스턴스 변수에 접근이 가능합니다. Java와 C++에서는 객체의 캡슐화 범위를 클래스까지로 보지만 스몰토크에서는 인스턴스까지를 캡슐화 범위로 생각해야 합니다.


스몰토크에서, 동일한 클래스로 만든 두 개의 인스턴스는, 클래스가 "접근자 메서드"를 정의하지 않으면, 서로 다른 인스턴스의 인스턴스 변수에 접근할 수 없습니다. 스퀵은 자신외의 그 어떤 다른 오브젝트도 인스턴스 변수로 바로 접근하는 기능을 제공되는 문법을 언어에서 지원하지 않습니다. (사실 reflection(반영적)이라고 하는 기법은 메타 프로그래밍처럼 다른 오브젝트에에서 인스턴스 변수의 값을 요청할 수 있는 방법을 제공합니다: 메타프로그래밍은 오브젝트 인스펙터처럼 다른 오브젝트의 내부를 살펴볼수 있는 툴을 위해 만들어졌습니다.)
스몰토크에서, 동일한 클래스로 만든 두 개의 인스턴스는, 클래스가 "접근자 메서드"를 정의하지 않으면, 서로 다른 인스턴스의 인스턴스 변수에 접근할 수 없습니다. 스퀵은 자신외의 그 어떤 다른 객체도 인스턴스 변수로 바로 접근하는 기능을 제공되는 문법을 언어에서 지원하지 않습니다. (사실 reflection(반영적)이라고 하는 기법은 메타 프로그래밍처럼 다른 객체에서 인스턴스 변수의 값을 요청할 수 있는 방법을 제공합니다: 메타 프로그래밍은 객체 인스펙터처럼 다른 객체 내부를 살펴볼 수 있는 도구를 위해 만들어졌습니다.)


인스턴스 변수는 인스턴스를 생성한 클래스에서 정의된 인스턴스의 메서드 또는 인스턴스를 정의한 클래스의 하위클래스에서 정의한 메서드 중 어떤메서드를 사용해도 접근이 가능합니다. 이것은 스몰토크 인스턴스 변수들이, C++과 Java에서, 보호(Protected)된 변수들과 비슷하다는 뜻이 됩니다. 그럼에도 불구하고, 스몰토크에서는 이 인스턴스 변수들을 개별적(private,혹은 전용)이라고 부르는것을 선호하는데, 이유는 서브클래스에서부터 직접 인스턴스 변수에 접근하는 형식을, 좋지 않게 생각하기 때문입니다.
인스턴스 변수는 인스턴스를 생성한 클래스에서 정의된 인스턴스의 메서드 또는 인스턴스를 정의한 클래스의 하위클래스에서 정의한 메서드 중 어떤메서드를 사용해도 접근이 가능합니다. 이것은 스몰토크 인스턴스 변수들이, C++과 Java에서, 보호(Protected)된 변수들과 비슷하다는 뜻이 됩니다. 그럼에도 불구하고, 스몰토크에서는 이 인스턴스 변수들을 개별적(private,혹은 전용)이라고 부르는것을 선호하는데, 이유는 서브클래스에서부터 직접 인스턴스 변수에 접근하는 형식을, 좋지 않게 생각하기 때문입니다.
Line 37: Line 37:
===Example===
===Example===


'''Method Point>>dist:'''(메서드 5.1) 는 수신자와 다른 점(point) 사이의 거리를 계산합니다. 수신자의 인스턴스 변수인 x와 y 두개는 메서드 안에서 직접 접근이 가능합니다. 그러나 자신 외의 다른 점(point)의 인스턴스 변수들은 aPoint로 들어온 오브젝트의 x와 y 메서드에 메세지를 보내서 값을 받는방법으로 변수에 접근해야 합니다.
'''Method Point>>dist:'''(메서드 5.1) 는 수신자와 다른 점 사이의 거리를 계산합니다. 수신자의 인스턴스 변수인 x와 y 두개는 메서드 안에서 직접 접근이 가능합니다. 그러나 자신 외의 다른 점의 인스턴스 변수들은 aPoint로 들어온 객체의 x와 y 메서드에 메시지를 보내서 값을 받는 방법으로 변수에 접근해야 합니다.




Line 52: Line 52:




클래스 기반 캡슐화 보다 인스턴스 기반 캡슐화를 선호하는 이유는, 인스턴스로 생성된 오브젝트들을 사용하려할때 추상화된 인스턴스의 사용법만 동일하다면 인스턴스 내부의 구현을 다르게 만들 수 있기 때문입니다. 예를 들면, method point>>dist: 메서드는 인수 aPoint가 수신자와 동일한 클래스의 인스턴스인지의 여부를 알 필요도 없고 신경쓸 필요도 없습니다. 인수 오브젝트가 메시지 x와 y에 반응된다면, aPoint라는 오브젝트는 끝 좌표가 될수도 있고, 데이터베이스에서 얻어진 데이터가 될 수도 있고 분산된 시스템의 다른컴퓨터의 데이터가 될 수도 있습니다. 메세지 x와 y에서 응답을 얻을 수 있을때까지, 메서드 5.1의 코드는 잘 작동합니다.
클래스 기반 캡슐화 보다 인스턴스 기반 캡슐화를 선호하는 이유는, 인스턴스로 생성된 객체를 사용하려 할 때 추상화된 인스턴스의 사용법만 동일하다면 인스턴스 내부의 구현을 다르게 만들 수 있기 때문입니다. 예를 들면, method point>>dist: 메서드는 인자 aPoint가 수신자와 동일한 클래스의 인스턴스인지의 여부를 알 필요도 없고 신경쓸 필요도 없습니다. 인자 객체가 메시지 x와 y에 반응된다면, aPoint라는 객체는 끝 좌표가 될수도 있고, 데이터베이스에서 얻어진 데이터가 될 수도 있고 분산된 시스템의 다른컴퓨터의 데이터가 될 수도 있습니다. 메시지 x와 y에서 응답을 얻을 수 있을때까지, 메서드 5.1의 코드는 잘 작동합니다.




Line 58: Line 58:
===메서드===
===메서드===


모든 메서드는 public 입니다<ref name="주석5-1">글쎄요, 대부분, 스퀵에서, 문자열 '''pvt''' 로 시작하는 셀렉터인 메서드는 private입니다: '''pvt''' 메시지는 클래스(인스턴스) 내부에서만 사용할 수 있죠. 이런 특성이 있어도 '''pvt''' 메서드가 많이 사용되지는 않습니다.</ref>. 메서드들은 목적에 따라 프로토콜을 이용해서 분류됩니다. 몇가지 공통으로 쓰이는 프로토콜 이름은 관례에 따라 만들어 졌습니다. 모든 접근자 메서드에 대한 접근과, 오브젝트에 적용되는 초기화 상태를 만들기 위한 initialization 프로토콜이 이런 관례의 예가 되겠습니다. private 프로토콜은 가끔 외부에서 보면 안되는 메서드를 그룹으로 만들 때 사용됩니다. 그렇다고 해서 private메서드로 구현된 셀렉터에 메세지를 보내는것을 막는 방법은 없습니다.<ref name="역자주1">private 프로토콜로 메서드 또는 셀렉터를 만들었다고해서 그게 딱히 외부에서의 접근에 대해 보호받는건 아니라는 의미입니다. 분류는 해놓을 수 있지만 굳이 사용하려한다면 사용은 할 수 있다는 의미인거죠</ref>
모든 메서드는 public 입니다<ref name="주석5-1">글쎄요, 대부분, 스퀵에서, 문자열 '''pvt''' 로 시작하는 셀렉터인 메서드는 private입니다: '''pvt''' 메시지는 클래스(인스턴스) 내부에서만 사용할 수 있죠. 이런 특성이 있어도 '''pvt''' 메서드를 많이 사용하지는 않습니다.</ref>. 메서드들은 목적에 따라 프로토콜을 이용해서 분류됩니다. 몇가지 공통으로 쓰이는 프로토콜 이름은 관례에 따라 만들어 졌습니다. 모든 접근자 메서드에 대한 접근과, 객체에 적용하는 초기화 상태를 만들기 위한 initialization 프로토콜이 이런 관례의 예가 되겠습니다. private 프로토콜은 가끔 외부에서 보면 안되는 메서드를 그룹으로 만들 때 사용합니다. 그렇다고 해서 private메서드로 구현된 셀렉터에 메시지를 보내는것을 막는 방법은 없습니다.<ref name="역자주1">private 프로토콜로 메서드 또는 셀렉터를 만들었다고해서 그게 딱히 외부에서의 접근에 대해 보호받는건 아니라는 의미입니다. 분류는 해놓을 수 있지만 굳이 사용하려한다면 사용은 할 수 있다는 의미인거죠</ref>


메서드는 오브젝트의 모든 인스턴스 변수에 접근할 수 있습니다. 일부 스몰토크 개발자들은 오직 접근자<sup>accessor</sup>만 인스턴스 변수에 접근할 수 있도록 하는 방법을 좋아합니다. 이런방법을 사용하는 경우는 있지만, 여러분의 클래스 인터페이스를 어수선하게 만들며, 외부에서 인스턴스 또는 클래스에 대한 private의 상태를 알 수 있는 가능성이 됩니다.
메서드는 객체의 모든 인스턴스 변수에 접근할 수 있습니다. 일부 스몰토크 개발자들은 오직 접근자<sup>accessor</sup>만 인스턴스 변수에 접근할 수 있도록 하는 방식을 좋아합니다. 이런 방식을 활용하는 경우는 있지만, 사용자의 클래스 인터페이스를 어수선하게 만들며, 외부에서 인스턴스 또는 클래스에 대한 private의 상태를 알 수 있는 가능성이 됩니다.




Line 66: Line 66:
===인스턴스 관점과 클래스 관점===
===인스턴스 관점과 클래스 관점===


클래스는 곧 오브젝트이므로, 클래스는 인스턴스 변수와 메서드를 보유할 수 있습니다. 이런것들을 클래스 인스턴스 변수 와 클래스 메서드라고 하지만, 보통의 인스턴스 변수, 메서드와는 실제로 차이가 있는건 아닙니다: 클래스 인스턴스 변수들은 단지 메타클래스로 정의되는 인스턴스 변수들이며, 클래스 메서드는 단지 메타클래스에 의해 정의되는 메서드 일 뿐입니다.
클래스는 곧 객체이므로, 클래스는 인스턴스 변수와 메서드를 보유할 수 있습니다. 이런것들을 클래스 인스턴스 변수 와 클래스 메서드라고 하지만, 보통의 인스턴스 변수, 메서드와는 실제로 차이가 있는건 아닙니다: 클래스 인스턴스 변수들은 단지 메타클래스로 정의되는 인스턴스 변수들이며, 클래스 메서드는 단지 메타클래스에 의해 정의되는 메서드 일 뿐입니다.


클래스와 그 클래스의 메타클래스는 두 개의 구별된 클래스 이며, 심지어 클래스는 메타클래스의 인스턴스이기도 합니다. 하지만, 이런 내용은 프로그래머에게 크게 상관은 없습니다: 당신은 오브젝트와 클래스를 만드는것에 신경쓰면 됩니다.
클래스와 그 클래스의 메타클래스는 두 개의 구별된 클래스 이며, 심지어 클래스는 메타클래스의 인스턴스이기도 합니다. 하지만, 이런 내용은 프로그래머에게 크게 상관은 없습니다: 당신은 객체와 클래스를 만드는것에 신경쓰면 됩니다.


[[image:Color-Buttons.png|none|800px|thumb|그림 5.1: 클래스와 그 자체의 메타 클래스(metaclass)를 검색하기]]
[[image:Color-Buttons.png|none|800px|thumb|그림 5.1: 클래스와 그 자체의 메타 클래스(metaclass)를 검색하기]]




이렇게 클래스는 메타클래스의 인스턴스 라는 특성때문에, 그림 5.1에 보이는 것처럼 System Browser 에서는 클래스와 메타클래스를 "instance" side 그리고 "class" side 의 양면성을 지닌 한몸처럼 탐색할 수 있습니다. 클래스 color 등을 검색하기 위해 {{Template:HighlightBox|instance}} 버튼을 클릭하면, blue와 같은 color의 인스턴스에 메시지를 보냈을때, 실행되는 메서드를 탐색하면 됩니다. {{Template:HighlightBox|class}} 버튼을 누르면, 클래스 Color class 를 탐색합니다. 이런 방법으로 당신은 color 클래스에 메시지가 실제로 보내질 때, 실행되는 메서드를 볼 수 있습니다. 예를 들어, Color blue는 클래스 color에 메시지를 보냅니다. 그렇게 되면, 여러분은 인스턴스 관점이 아닌, color의 클래스 관점에서 정의된 메서드 blue를 찾게됩니다.
이렇게 클래스는 메타클래스의 인스턴스 라는 특성때문에, 그림 5.1에 보이는 것처럼 System Browser 에서는 클래스와 메타클래스를 "instance" side 그리고 "class" side 의 양면성을 지닌 한몸처럼 탐색할 수 있습니다. 클래스 color 등을 검색하기 위해 {{Template:HighlightBox|instance}} 버튼을 클릭하면, blue와 같은 color의 인스턴스에 메시지를 보냈을때, 실행되는 메서드를 탐색하면 됩니다. {{Template:HighlightBox|class}} 버튼을 누르면, 클래스 Color class 를 탐색합니다. 이런 방법으로 당신은 color 클래스에 메시지가 실제로 보내질 때, 실행되는 메서드를 볼 수 있습니다. 예를 들어, Color blue는 클래스 color에 메시지를 보냅니다. 그렇게 되면, 인스턴스 관점이 아닌, color의 클래스 관점에서 정의된 메서드 blue 를 찾게됩니다.


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




클래스는 System Browser의 인스턴스 관점에서 언급한 템플릿에 내용을 채워 정의합니다. 이렇게 내용이 채워진 템플릿을 Accept 하게되면, 시스템은 여러분이 정의한 클래스뿐만 아니라, 이에 대응하는 메타클래스도 같이 만들게 됩니다. 또한 System Browser의 {{Template:HighlightBox|class}} 버튼을 클릭하여 메타클래스를 검색하는것도 가능합니다. 메타클래스 생성 탬플릿에서 인스턴스 변수이름목록 외에 당신이 편집할 수 있는건 없습니다.
클래스는 System Browser의 인스턴스 관점에서 언급한 템플릿에 내용을 채워 정의합니다. 이렇게 내용이 채워진 템플릿을 Accept 하게되면, 시스템은 사용자가 정의한 클래스뿐만 아니라, 이에 대응하는 메타클래스도 같이 만들게 됩니다. 또한 System Browser의 {{Template:HighlightBox|class}} 버튼을 클릭하여 메타클래스를 검색하는것도 가능합니다. 메타클래스 생성 탬플릿에서 인스턴스 변수이름목록 외에 당신이 편집할 수 있는건 없습니다.


클래스를 만들고 나면, {{Template:HighlightBox|instance}} 버튼을 클릭하면, 클래스의 인스턴스(와 클래스의 하위 클래스)가 소유할 메서드를 편집하고 탐색할 수 있습니다. 이런 방법으로, 그림 5.1처럼 클래스 color의 인스턴스에서 정의된 메서드 hue를 확인할 수 있습니다. 반대로, {{Template:HighlightBox|class}} 버튼을 이용하면 여러분이 메타클래스(이번 경우 Color 클래스)를 검색하고 편집할 수 있습니다.
클래스를 만들고 나면, {{Template:HighlightBox|instance}} 버튼을 클릭하면, 클래스의 인스턴스(와 클래스의 서브클래스)가 소유할 메서드를 편집하고 탐색할 수 있습니다. 이런 방법으로, 그림 5.1처럼 클래스 color의 인스턴스에서 정의된 메서드 hue를 확인할 수 있습니다. 반대로, {{Template:HighlightBox|class}} 버튼을 이용하면 메타클래스(이번 경우 Color 클래스)를 검색하고 편집할 수 있습니다.




Line 95: Line 95:
===클래스 메서드===
===클래스 메서드===


클래스 메서드는 유용하기 때문에, 좋은 보기를 위해서 Color 클래스를 탐색하겠습니다. 탐색을 하고 나면, 당신은 클래스에서 정의된 두 가지 종류의 메서드를 발견하게 됩니다: '''Color class>>blue'''와 같이 클래스의 인스턴스를 만드는 메서드들과 '''Color class>>showColorCubedhk''' 처럼, 유틸리티 함수를 수행하는 메서드가 있죠. 가끔 다른 방식으로 사용되는 클래스 메서드를 보겠지만, 이 두 가지 종류가 일반적입니다.
클래스 메서드는 그럭저럭 쓸모가 있습니다. 좋은 예 몇가지를 들어보기 위해 Color class를 탐색하겠습니다. 클래스에 정의한 메서드에는 두 가지 종류가 있음을 발견하실 것입니다: '''Color class>>blue'''와 같이 클래스의 인스턴스를 만드는 메서드들과 '''Color class>>showColorCube''' 처럼, 유틸리티 함수를 수행하는 메서드가 있죠. 가끔 다른 방식으로 사용하는 클래스 메서드를 보겠지만, 이 두 가지 종류가 일반적입니다.


클래스 측면(the class side)에서 유틸리티 메서드들을 배치하는 작업은 편리하며, class 측면에서 유틸리티 메서드를 배치하기 편리한 이유는, 추가 오브젝트를 먼저 생성할 필요 없이 메서스들을 실행해볼 수 있기 때문입니다. 이런 메소드 대부분은 쉽게 실행할 수 있도록 설계시 주석이 들어가 있습니다.
클래스 측면(the class side)에서 유틸리티 메서드들을 배치하는 작업은 편리하며, class side 에서 유틸리티 메서드를 배치하기 편리한 이유는, 추가 객체를 먼저 생성할 필요 없이 메서스들을 실행해볼 수 있기 때문입니다. 이런 메서드 대부분은 쉽게 실행할 수 있도록 설계시 주석이 들어가 있습니다.




Line 105: Line 105:
이 메서드가 실행되는 결과를 확인할 수 있습니다. [실행결과를 취소(undo)하기 위해 {{Template:HighlightGray|World ▷ restore display (r)}} 을 선택합니다.]
이 메서드가 실행되는 결과를 확인할 수 있습니다. [실행결과를 취소(undo)하기 위해 {{Template:HighlightGray|World ▷ restore display (r)}} 을 선택합니다.]


Java와 C++에 익숙하다면, 클래스 메서드가 정적 메서드(static method)와 비슷하게 보일겁니다. 하지만, 스몰토크에서는 그렇지 않습니다: 비록 Java의 메서드가 정적인 프로시져<sup>statically resolved procedures</sup>라고해도, 스몰토크 클래스 메서드는 동적으로 내보내는<sup>dynamically-dispatched</sup> 메서드입니다. 이런 특성은 스몰토크에서는 클래스의 메서드에 대해 동적으로 상속<sup>inheritance</sup>, 재지정<sup>overriding</sup>, super-sends등이 가능하지만 Java의 정적메서드에서는 이런것들을 처리하지 못한다는것을 의미합니다.
Java와 C++에 익숙하다면, 클래스 메서드가 정적 메서드(static method)와 비슷하게 보일겁니다. 하지만, 스몰토크에서는 그렇지 않습니다: 비록 Java의 메서드가 정적인 프로시져<sup>statically resolved procedures</sup>라고해도, 스몰토크 클래스 메서드는 동적으로 내보내는<sup>dynamically-dispatched</sup> 메서드입니다. 이런 특성은 스몰토크에서는 클래스의 메서드에 대해 동적으로 상속<sup>inheritance</sup>, 재지정<sup>overriding</sup>, super-send등이 가능하지만 Java의 정적메서드에서는 이런 동작을 처리하지 못함을 의미합니다.
 
 


===클래스 인스턴스 변수===
===클래스 인스턴스 변수===


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


여러분은 count라 지칭되는 클래스 인스턴스 변수를, 여러분에게 주어진 클래스를 만드는 작업에 얼마나 많은 인스턴스를 실행하였는지를 지속적으로 알기 위해 사용할 수 있습니다.
count라고 불리는 클래스 인스턴스 변수는 클래스에 따른 인스턴스가 얼마나 많은지를 알아보는데 사용할 수 있습니다. 하지만 서브클래스는 자신만의 count변수를 가지기때문에 하위클래스의 인스턴스는 별도의 인스턴스 카운트값을 가지게 됩니다.


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




Line 124: Line 122:
   classVariableNames: ''
   classVariableNames: ''
   poolDictionaries: ''
   poolDictionaries: ''
   category: 'SBE--CIV'
   category: 'SBE-CIV'


Dog class
Dog class
Line 137: Line 135:




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


이제 Dog클래스의 count(카운트)를 0으로 초기화 하는 Dog의 클래스 메서드를 정의하고, count는 새로운 인스턴스가 만들어질 때마다 하나씩 증가한다고 생각해보겠습니다:


메서드 5.3: 새로운 개의 count 유지하기
 
메서드 5.3: 새로운 Dog의 count 유지하기
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Dog class»initialize
Dog class>>initialize
   super initialize.
   super initialize.
   count := 0.
   count := 0.


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


Dog class»count
Dog class>>count
   ↑ count
   ↑ count
</syntaxhighlight>
</syntaxhighlight>




지금, 우리가 새로운 Dog를 만들 때, 그것의 count가 증가되며, Hyena를 만들 때도 마찬가지이지만, 개와 하이에나의 count는 서로 분리됩니다:
지금, 새로운 Dog의 인스턴스가 만들어질때마다, Dog의 count가 증가되며, Hyena를 만들 때도 값은 증가하지만, Dog과 Hyena의 count는 따로 계산됩니다:


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




클래스 인스턴스 변수는 인스턴스 변수가 인스턴스에 개별적(private)인 것과 똑같이, 클래스 인스턴스 변수 또한 클래스에 개별적이라는 사실에 주의해 주십시오. 클래스와 그 클래스의 인스턴스들이 다른 오브젝트들인 것처럼, 이 사실은 다음의 즉각적인 결과들을 갖게 됩니다.
클래스 인스턴스 변수는 인스턴스 변수가 인스턴스에 대해 private 으로 처리되는 것처럼, 클래스 인스턴스 변수 또한 클래스에 자체적으로만 동작한다는 것을 기억해주세요. 클래스와 그 클래스의 인스턴스들이 전혀 다른 객체로 처리하는 등의 특성때문에 다음과 같은 결론을 얻을 수 있습니다.




<center>
<center>
{{HighlightDoubleBox|클래스는 그 자체의 인스턴스 변수에 대한 접근성을 소유하지 않습니다.}}
{{HighlightDoubleBox|클래스는 그 자체의 인스턴스 변수에 대한 접근성을 가지고 있지 않습니다.}}




{{HighlightDoubleBox|클래스의 인스턴스는 그 자체의 클래스가 갖는 클래스 인스턴스 변수에 대한 접근 권한을 가지지 않습니다.}}
{{HighlightDoubleBox|클래스의 인스턴스는 그 자체의 클래스가 갖는 클래스 인스턴스 변수에 대한 접근 권한이 없습니다.}}
</center>
</center>




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


유사하게, 인스턴스들은 접근자 메시지를 그것들의 클래스에 보냄으로서, 클래스 인스턴스 변수에 간접적으로만 접근할 수 있습니다.
비슷한 이유로, 인스턴스는 이들 클래스에 대해 접근자 메시지를 사용해서 간접적으로 클래스 인스턴스 변수에 접근해야 합니다.




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






'''Example: 싱글톤을 정의합니다.''' 싱글톤 패턴<ref name="주석5-2">Sherman R. Alpert, Kyle Brown and BobbyWoolf, The Design Patterns Smalltalk Companion. Addison Wesley, 1998, ISBN 0–201–18462–1 </ref>은 클래스 인스턴스 변수와 클래스 메서드의 전형적인 사용의 예시를 제공합니다. 우리가 클래스 웹서버(class webserver)를 사용하기를 원하고, 그것이 하나의 인스턴스만을 가지도록 확정하기 위해 싱글톤 패턴을 사용한다고 상상해 봅시다. 브라우저에서 인스턴스 버튼을 클릭하여, 우리는 다음과 같이(클래스 5.4) 클래스 WebServer를 정의할 수 있습니다.
'''Example: Singleton 을 정의합니다.''' Singleton 패턴<ref name="주석5-2">Sherman R. Alpert, Kyle Brown and BobbyWoolf, The Design Patterns Smalltalk Companion. Addison Wesley, 1998, ISBN 0–201–18462–1 </ref>은 클래스 인스턴스 변수와 클래스 메서드의 표준적인 사용방법입니다. WebServer 클래스를 구현하고 이 클래스에 대한 단 하나만의 인스턴스만 존재할 수 있도록 Singletone 패턴을 적용한다고 가정하겠습니다. 우리는 시스템 브라우저에서 instance 버튼을 클릭한 뒤, 아래처럼(클래스 5.4) 클래스 WebServer를 정의할 수 있습니다.




클래스 5.4: 싱글톤 클래스(the singleton class)
클래스 5.4: singleton 클래스
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
Object subclass: #WebServer
Object subclass: #WebServer
Line 214: Line 213:




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




클래스 5.5 싱글톤 클래스의 클래스 사이드
클래스 5.5 singleton 클래스의 class side
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
WebServer class
WebServer class
Line 224: Line 223:




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


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




메서드 5.6: uniqueInstance (in class side)
메서드 5.6: uniqueInstance (in class side)
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
WebServer class»uniqueInstance
WebServer class>>uniqueInstance
   uniqueInstance ifNil: [uniqueInstance := self new].
   uniqueInstance ifNil: [uniqueInstance := self new].
   ↑ uniqueInstance
   ↑ uniqueInstance
Line 237: Line 236:




첫 번째로, WebServer uniquenInstance가 실행됩니다. 클래스 WebServer가 만들어질 것이고 uniquenInstance 변수에 할당될 것입니다. 다음에는 이전에 만들어진 인스턴스가 새로운 인스턴스를 만드는 대신 리턴될 것입니다.  
WebServer uniquenInstance를 실행한 뒤 제일 처음으로. WebServer 클래스의 인스턴스가 uniqueInstance 변수를 만들고 할당합니다. 그 이전에 만든 인스턴스를 새로 만든 인스턴스 대신 반환하게 되죠.
 
참고로 메서드 5.6에 적혀있는 인스턴스 생성 코드는 WebServer new가 아닌 self new 처럼 작성했습니다. 이런 방법에는 어떤 차이가 있을까요? 당신은 uniquenInstance 메서드는 WebServer에 정의한 것이기 때문에, 이 둘은 동일한 수준에 있다 생각할 수 있습니다. 물론! WebServer클래스의 서브클래스를 만들어 내기 전까지는 같습니다. 하지만 ReliableWebServer 클래스가 WebServer의 서브클래스 이고 uniqueInstance를 상속한다고 가정한다면, ReliableWebServer uniqueInstance 는 WebServer가 아닌 ReliableWebServer 클래스에 반응합니다: 이 경우 self는 ReliableWebServer만을 대상으로 동작하게 하기 위한 확실한 방법이 됩니다. 왜냐하면 이런 (self를 사용하는 등의) 작업의 결과는 동작을 해당 클래스로 한정되게 해주기 때문이죠. 또한 WebServer와 RealiableWebServer가 uniqueInstance 라고하는 각각 별도의 클래스 인스턴스 변수를 가지게 된다는걸 기억하세요. 물론 이 두 개의 클래스의 인스턴스 변수는 제각기 다른 값을 가집니다.


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





Latest revision as of 02:33, 17 September 2013

모든 객체는 클래스의 인스턴스입니다

모든 객체는 클래스를 가지고 있으며, 스몰토크는 객체에 class라는 메시지를 보내어 원하는 객체의 클래스를 알아낼 수 있습니다.

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장에서 메타클래스를 좀 더 자세히 살펴보겠습니다.)


인스턴스 변수

스몰토크에서 인스턴수 변수는 해당 인스턴스 내부(private)에서만 사용합니다. 스몰토크와는 반대로 인스턴스 변수에 대한 외부접근을 허용하는 Java와 C++등에서는(이러한 변수를 필드 또는 멤버변수라고 합니다) 같은 클래스에서 만들어진 다른 어떤 인스턴스에서도 인스턴스 변수에 접근이 가능합니다. Java와 C++에서는 객체의 캡슐화 범위를 클래스까지로 보지만 스몰토크에서는 인스턴스까지를 캡슐화 범위로 생각해야 합니다.

스몰토크에서, 동일한 클래스로 만든 두 개의 인스턴스는, 클래스가 "접근자 메서드"를 정의하지 않으면, 서로 다른 인스턴스의 인스턴스 변수에 접근할 수 없습니다. 스퀵은 자신외의 그 어떤 다른 객체도 인스턴스 변수로 바로 접근하는 기능을 제공되는 문법을 언어에서 지원하지 않습니다. (사실 reflection(반영적)이라고 하는 기법은 메타 프로그래밍처럼 다른 객체에서 인스턴스 변수의 값을 요청할 수 있는 방법을 제공합니다: 메타 프로그래밍은 객체 인스펙터처럼 다른 객체 내부를 살펴볼 수 있는 도구를 위해 만들어졌습니다.)

인스턴스 변수는 인스턴스를 생성한 클래스에서 정의된 인스턴스의 메서드 또는 인스턴스를 정의한 클래스의 하위클래스에서 정의한 메서드 중 어떤메서드를 사용해도 접근이 가능합니다. 이것은 스몰토크 인스턴스 변수들이, C++과 Java에서, 보호(Protected)된 변수들과 비슷하다는 뜻이 됩니다. 그럼에도 불구하고, 스몰토크에서는 이 인스턴스 변수들을 개별적(private,혹은 전용)이라고 부르는것을 선호하는데, 이유는 서브클래스에서부터 직접 인스턴스 변수에 접근하는 형식을, 좋지 않게 생각하기 때문입니다.


Example

Method Point>>dist:(메서드 5.1) 는 수신자와 다른 점 사이의 거리를 계산합니다. 수신자의 인스턴스 변수인 x와 y 두개는 메서드 안에서 직접 접근이 가능합니다. 그러나 자신 외의 다른 점의 인스턴스 변수들은 aPoint로 들어온 객체의 x와 y 메서드에 메시지를 보내서 값을 받는 방법으로 변수에 접근해야 합니다.


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

Point>>dist: aPoint
  "aPoint와 수신자 사이의 거리를 계산합니다."
  | 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에 반응된다면, aPoint라는 객체는 끝 좌표가 될수도 있고, 데이터베이스에서 얻어진 데이터가 될 수도 있고 분산된 시스템의 다른컴퓨터의 데이터가 될 수도 있습니다. 메시지 x와 y에서 응답을 얻을 수 있을때까지, 메서드 5.1의 코드는 잘 작동합니다.


메서드

모든 메서드는 public 입니다[1]. 메서드들은 목적에 따라 프로토콜을 이용해서 분류됩니다. 몇가지 공통으로 쓰이는 프로토콜 이름은 관례에 따라 만들어 졌습니다. 모든 접근자 메서드에 대한 접근과, 객체에 적용하는 초기화 상태를 만들기 위한 initialization 프로토콜이 이런 관례의 예가 되겠습니다. private 프로토콜은 가끔 외부에서 보면 안되는 메서드를 그룹으로 만들 때 사용합니다. 그렇다고 해서 private메서드로 구현된 셀렉터에 메시지를 보내는것을 막는 방법은 없습니다.[2]

메서드는 객체의 모든 인스턴스 변수에 접근할 수 있습니다. 일부 스몰토크 개발자들은 오직 접근자accessor만 인스턴스 변수에 접근할 수 있도록 하는 방식을 좋아합니다. 이런 방식을 활용하는 경우는 있지만, 사용자의 클래스 인터페이스를 어수선하게 만들며, 외부에서 인스턴스 또는 클래스에 대한 private의 상태를 알 수 있는 가능성이 됩니다.


인스턴스 관점과 클래스 관점

클래스는 곧 객체이므로, 클래스는 인스턴스 변수와 메서드를 보유할 수 있습니다. 이런것들을 클래스 인스턴스 변수 와 클래스 메서드라고 하지만, 보통의 인스턴스 변수, 메서드와는 실제로 차이가 있는건 아닙니다: 클래스 인스턴스 변수들은 단지 메타클래스로 정의되는 인스턴스 변수들이며, 클래스 메서드는 단지 메타클래스에 의해 정의되는 메서드 일 뿐입니다.

클래스와 그 클래스의 메타클래스는 두 개의 구별된 클래스 이며, 심지어 클래스는 메타클래스의 인스턴스이기도 합니다. 하지만, 이런 내용은 프로그래머에게 크게 상관은 없습니다: 당신은 객체와 클래스를 만드는것에 신경쓰면 됩니다.

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


이렇게 클래스는 메타클래스의 인스턴스 라는 특성때문에, 그림 5.1에 보이는 것처럼 System Browser 에서는 클래스와 메타클래스를 "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"


클래스는 System Browser의 인스턴스 관점에서 언급한 템플릿에 내용을 채워 정의합니다. 이렇게 내용이 채워진 템플릿을 Accept 하게되면, 시스템은 사용자가 정의한 클래스뿐만 아니라, 이에 대응하는 메타클래스도 같이 만들게 됩니다. 또한 System Browser의 class 버튼을 클릭하여 메타클래스를 검색하는것도 가능합니다. 메타클래스 생성 탬플릿에서 인스턴스 변수이름목록 외에 당신이 편집할 수 있는건 없습니다.

클래스를 만들고 나면, instance 버튼을 클릭하면, 클래스의 인스턴스(와 클래스의 서브클래스)가 소유할 메서드를 편집하고 탐색할 수 있습니다. 이런 방법으로, 그림 5.1처럼 클래스 color의 인스턴스에서 정의된 메서드 hue를 확인할 수 있습니다. 반대로, class 버튼을 이용하면 메타클래스(이번 경우 Color 클래스)를 검색하고 편집할 수 있습니다.


클래스 메서드

클래스 메서드는 그럭저럭 쓸모가 있습니다. 좋은 예 몇가지를 들어보기 위해 Color class를 탐색하겠습니다. 클래스에 정의한 메서드에는 두 가지 종류가 있음을 발견하실 것입니다: Color class>>blue와 같이 클래스의 인스턴스를 만드는 메서드들과 Color class>>showColorCube 처럼, 유틸리티 함수를 수행하는 메서드가 있죠. 가끔 다른 방식으로 사용하는 클래스 메서드를 보겠지만, 이 두 가지 종류가 일반적입니다.

클래스 측면(the class side)에서 유틸리티 메서드들을 배치하는 작업은 편리하며, 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 메서드입니다. 이런 특성은 스몰토크에서는 클래스의 메서드에 대해 동적으로 상속inheritance, 재지정overriding, super-send등이 가능하지만 Java의 정적메서드에서는 이런 동작을 처리하지 못함을 의미합니다.

클래스 인스턴스 변수

일반적인 인스턴스 변수처럼, 클래스 인스턴스 변수는 클래스의 모든 인스턴스같이 동일한 이름의 변수를 가지게 되고, 현 클래스의 하위클래스에 대한 인스턴스는 이러한 변수 이름들을 상속받습니다. 다만 개별의 인스턴스는 각자의 값을 따로 가지게 되죠. 이런 성질은 클래스 인스턴스 변수들에게도 그대로 적용됩니다: 각 클래스는 그 자체로 private 클래스 인스턴스 변수를 가지게 됩니다. 하위클래스는 인스턴스 변수들이 가진 클래스 인스턴스 변수들을 상속하겠지만, 클래스 인스턴스 변수들의 고유사본을 갖게 됩니다. 객체가 인스턴스 변수들을 공유하지 않듯이, 클래스와 서브클래스간에는 클래스 인스턴스 변수를 공유하지 않습니다.

count라고 불리는 클래스 인스턴스 변수는 클래스에 따른 인스턴스가 얼마나 많은지를 알아보는데 사용할 수 있습니다. 하지만 서브클래스는 자신만의 count변수를 가지기때문에 하위클래스의 인스턴스는 별도의 인스턴스 카운트값을 가지게 됩니다.

Example: 클래스 인스턴스 변수는 서브클래스와 공유되지 않습니다. Dog(개)와 Dog(개)로부터 count 클래스 인스턴스 변수를 상속받는 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으로 초기화 하는 Dog의 클래스 메서드를 정의하고, count는 새로운 인스턴스가 만들어질 때마다 하나씩 증가한다고 생각해보겠습니다:


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

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

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

Dog class>>count
   count


지금, 새로운 Dog의 인스턴스가 만들어질때마다, Dog의 count가 증가되며, Hyena를 만들 때도 값은 증가하지만, Dog과 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 으로 처리되는 것처럼, 클래스 인스턴스 변수 또한 클래스에 자체적으로만 동작한다는 것을 기억해주세요. 클래스와 그 클래스의 인스턴스들이 전혀 다른 객체로 처리하는 등의 특성때문에 다음과 같은 결론을 얻을 수 있습니다.


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


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


이러한 이유 때문에, 인스턴스 초기화 메서드는 항상 instance side 에서 정의해야 합니다: class side 에서는 인스턴스 변수에 대한 접근을 할 수 없기 때문에, 해당되는 변수들을 초기화할 수 없습니다. class side 은 새롭게 만들어진 인스턴스에 접근자를 사용해서 초기화 메시지를 보내는 것 외에는 할 수 없습니다.

비슷한 이유로, 인스턴스는 이들 클래스에 대해 접근자 메시지를 사용해서 간접적으로 클래스 인스턴스 변수에 접근해야 합니다.


Java는 클래스 인스턴스 변수와 비슷한 것이 없습니다. Java와 C++의 정적 변수는 5.7장: 공유 변수 에서 알아볼 스몰토크의 클래스 변수에 더 가깝습니다.


Example: Singleton 을 정의합니다. Singleton 패턴[3]은 클래스 인스턴스 변수와 클래스 메서드의 표준적인 사용방법입니다. WebServer 클래스를 구현하고 이 클래스에 대한 단 하나만의 인스턴스만 존재할 수 있도록 Singletone 패턴을 적용한다고 가정하겠습니다. 우리는 시스템 브라우저에서 instance 버튼을 클릭한 뒤, 아래처럼(클래스 5.4) 클래스 WebServer를 정의할 수 있습니다.


클래스 5.4: singleton 클래스

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


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


클래스 5.5 singleton 클래스의 class side

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 클래스의 인스턴스가 uniqueInstance 변수를 만들고 할당합니다. 그 뒤 이전에 만든 인스턴스를 새로 만든 인스턴스 대신 반환하게 되죠.

참고로 메서드 5.6에 적혀있는 인스턴스 생성 코드는 WebServer new가 아닌 self new 처럼 작성했습니다. 이런 방법에는 어떤 차이가 있을까요? 당신은 uniquenInstance 메서드는 WebServer에 정의한 것이기 때문에, 이 둘은 동일한 수준에 있다 생각할 수 있습니다. 물론! WebServer클래스의 서브클래스를 만들어 내기 전까지는 같습니다. 하지만 ReliableWebServer 클래스가 WebServer의 서브클래스 이고 uniqueInstance를 상속한다고 가정한다면, ReliableWebServer uniqueInstance 는 WebServer가 아닌 ReliableWebServer 클래스에 반응합니다: 이 경우 self는 ReliableWebServer만을 대상으로 동작하게 하기 위한 확실한 방법이 됩니다. 왜냐하면 이런 (self를 사용하는 등의) 작업의 결과는 동작을 해당 클래스로 한정되게 해주기 때문이죠. 또한 WebServer와 RealiableWebServer가 uniqueInstance 라고하는 각각 별도의 클래스 인스턴스 변수를 가지게 된다는걸 기억하세요. 물론 이 두 개의 클래스의 인스턴스 변수는 제각기 다른 값을 가집니다.


Notes

  1. 글쎄요, 대부분, 스퀵에서, 문자열 pvt 로 시작하는 셀렉터인 메서드는 private입니다: pvt 메시지는 클래스(인스턴스) 내부에서만 사용할 수 있죠. 이런 특성이 있어도 pvt 메서드를 많이 사용하지는 않습니다.
  2. private 프로토콜로 메서드 또는 셀렉터를 만들었다고해서 그게 딱히 외부에서의 접근에 대해 보호받는건 아니라는 의미입니다. 분류는 해놓을 수 있지만 굳이 사용하려한다면 사용은 할 수 있다는 의미인거죠
  3. Sherman R. Alpert, Kyle Brown and BobbyWoolf, The Design Patterns Smalltalk Companion. Addison Wesley, 1998, ISBN 0–201–18462–1