Smalltalk80LanguageImplementationKor:Chapter 14
- 제 14 장 커널 지원 클래스
커널 지원 클래스
Object
Magnitude
Character
Date
Time
Number
Float
Fraction
Integer
LargeNegativeInteger
LargePositiveInteger
SmallInteger
LookupKey
Association
Link
Process
Collection
SequenceableCollection
LinkedList
Semaphore
ArrayedCollection
Array
Bitmap
DisplayBitmap
RunArray
String
Symbol
Text
ByteArray
Interval
OrderedCollection
SortedCollection
Bag
MappedCollection
Set
Dictionary
IdentifyDictionary
Stream
PositionableStream
ReadStream
WriteStream
ReadWriteStream
ExternalStream
FileStream
Random
File
FileDirectory
FilePage
UndefinedObject***
Boolean***
False***
True***
ProcessorScheduler
Delay
SharedQueue
Behavior
ClassDescription
Class
MetaClass
Point
Rectangle
BitBit
CharacterScanner
Pen
DisplayObject
DisplayMedium
Form
Cursor
DisplayScreen
InfiniteForm
OpaqueForm
Path
Arc
Circle
Curve
Line
LinearFit
Spline
UndefinedObject 클래스
nil 객체는 초기화되지 않은 변수에 대한 값을 나타낸다. 또 의미가 없는 결과를 나타내기도 한다. nil 객체는 UndefinedObject 객체의 유일한 인스턴스에 해당한다.
UndefinedObject 클래스를 시스템에 포함시키는 목적은 오류 메시지를 처리하기 위함이다. Smalltalk-80 표현식을 평가하는 데에 발생하는 일반적인 오류는 일부 객체가 이해하지 못하는 메시지를 수신하였을 때다. 때로는 변수가 적절하게 초기화되지 않아서 발생하기도 하는데, 다른 객체를 참조해야 하는 변수명이 대신 nil을 참조해서 발생하는 경우가 다반사다. 오류 메시지는 아래와 같은 형태를 취한다.
className does not understand messageSelector
여기서 className은 수신자의 클래스를 언급하고, messageSelector는 잘못 전송된 메시지의 선택자다.
nil이 Object의 인스턴스라면 그곳으로 전송되는 메시지에 오류가 발생할 경우 아래와 같이 나타내는데,
Object does not understand messageSelector
정의되지 않은 객체가 메시지를 이해하지 못한다는 말보다는 덜 명시적이다. 클래스 설명을 희생하면 오류 메시지의 개선이 가능했다.
객체가 nil인지 확인하는 검사는 Object 클래스에서 처리되지만 UndefinedObject에서 재구현된다. Object 클래스에서는 isNil과 notNil 메시지가 아래와 같이 구현된다.
isNil
↑false
notNil
↑true
UndefinedObject 클래스에서는 isNil과 notNil 메시지가 아래와 같이 구현되므로
isNil
↑true
notNil
↑false
Object에서는 어떤 조건부 검사도 필요하지 않다.
Boolean, True, False 클래스
논리값에 대한 프로토콜은 Boolean 클래스에서 제공하는데, 논리 값은 Boolean의 서브클래스, True와 False에 의해 표현된다. 서브클래스는 새로운 프로토콜을 추가하지 않고, 슈퍼클래스의 메서드보다 더 나은 성능을 가진 여러 메시지를 재구현한다. 이에 대한 개념은 Object와 UndefinedObject에서 nil의 검사와 비슷하므로, true는 그것이 논리적 사실을 표현하고 false는 그것이 논리적 거짓을 나타낸다고 이해한다. 이를 설명하기 위해 제어 프로토콜의 구현을 몇 가지 보이겠다.
논리 연산은 다음과 같다.
논리 연산 | |
& aBoolean | 평가하는 논리곱(conjunction). 수신자와 인자가 모두 true일 경우 true를 응답한다. |
| aBoolean | 평가하는 논리곱. 수신자와 인자 중 하나가 true일 경우 true를 응답한다. |
not | 부정(negation). 수신자가 false일 경우 true를, 수신자가 true일 경우 false를 응답한다. |
eqv: aBoolean | 수신자가 aBoolean 인자와 동등할 경우 true를 응답한다. |
xor: aBoolean | 배타적 논리합(Exclusive OR). 수신자가 aBoolean과 동등하지 않을 경우 true를 응답한다. |
Boolean 인스턴스 프로토콜 |
이러한 논리곱과 논리합(disjunction) 연산은 "evaluating"(평가하는) 연산으로, 수신자의 값과 상관없이 인자가 평가됨을 의미한다. 이는 수신자가 인자의 평가 여부를 결정하는 and: 와 or: 과는 상반된다.
제어하기(controlling) | |
and: alternativeBlock | 비평가 논리곱. 수신자가 true일 경우 인자의 값을 응답하고, 그 외의 경우 인자를 평가하지 않은 채 false를 응답한다. |
or: alternativeBlock | 비평가 논리합. 수신자가 false일 경우 인자의 값을 응답하고, 그 외의 경우 인자를 평가하지 않은 채 true를 응답한다. |
ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock | 조건문. 수신자가 true일 경우 trueAlternativeBlock의 평가 결과를 응답하고, 그 외의 경우 falseAlternativeBlock의 평가 결과를 응답한다. |
ifFalse: falseAlternativeBlock ifTrue: trueAlternativeBlock | 조건문. 수신자가 true일 경우 trueAlternativeBlock의 평가 결과를 응답하고, 그 외의 경우 falseAlternativeBlock의 평가 결과를 응답한다. |
ifTrue: trueAlternativeBlock | 조건문. 수신자가 true일 경우 trueAlternativeBlock의 평가 결과를 응답하고, 그 외의 경우 nil을 응답한다. |
ifFalse: falseAlternativeBlock | 조건문. 수신자가 false일 경우 falseAlternativeBlock의 평가 결과를 응답하고, 그 외의 경우 nil을 응답한다. |
Boolean 인스턴스 프로토콜 |
평가를 연기하기 위해서는 and: 과 or: 에 대한 인자가 블록이어야 한다. 앞서 명시했을 뿐 아니라 여러 장에 걸쳐 예로 소개하였듯 조건문은 ifTrue:ifFalse:, ifFalse:ifTrue:, ifTrue:, ifFalse: 메시지로서 제공된다. 메시지는 Boolean 클래스의 서브클래스에 구현되어 적절한 인자 블록이 평가되도록 한다.
True 클래스에서 메서드는 다음과 같다.
ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock
↑trueAlternativeBlock value
ifFalse: falseAlternativeBlock ifTrue: trueAlternativeBlock
↑trueAlternativeBlock value
ifTrue: trueAlternativeBlock
↑trueAlternativeBlock value
ifFalse: falseAlternativeBlock
↑nil
False 클래스에서 메서드는 다음과 같다.
ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock
↑falseAlternativeBlock value
ifFalse: falseAlternativeBlock ifTrue: trueAlternativeBlock
↑falseAlternativeBlock value
ifTrue: trueAlternativeBlock
↑nil
ifFalse: falseAlternativeBlock
↑falseAlternativeBlock value
만약 x가 3이면
x > 0 ifTrue: [x ← X - 1] ifFalse: [x ← x + 1]
위는 x>0이 True 클래스의 유일한 인스턴스인 true로 평가되는 것으로 해석되고, ifTrue:ifFalse: 에 대한 메서드는 True 클래스에서 발견되므로 추가 검사 없이 블록 [x ← x - 1] 이 평가된다.
이렇게 메시지 검색 메커니즘은 추가 프리미티브 연산이나 순환(circular) 정의 없이 조건부 제어의 효과적인 구현을 제공한다.
Object 클래스에 대한 추가 프로토콜
모든 객체가 공유하는 Object 클래스에 대한 프로토콜은 제 6장에서 소개하였다. 몇 가지 메시지 범주는 초기 논의에 포함하지 않았다. 그 중 대부분은 메시지 처리, 의존성 관계, 프리미티브 메시지 처리, 시스템 프리미티브에 대한 시스템 지원을 제공하기 위한 Object의 프로토콜에 속한다.
객체 간 의존성 관계
Smalltalk-80 시스템에서 정보는 객체에 의해 표현된다. 객체의 변수들이 객체를 참조하고, 이런 의미에서 객체는 서로 명시적으로 관계가 있거나 의존한다고 볼 수 있다. 클래스는 그들의 슈퍼클래스와 메타클래스에 연관되며, 이러한 클래스들은 외부 및 내부 설명을 공유하고 그에 따라 서로 의존한다. 이러한 의존성 형태는 Smalltalk-80 언어의 의미에 핵심이 된다. 이는 객체 간 설명적 정보를 조정한다.
Object 클래스에서는 또 다른 유형의 의존성이 지원된다. 그 목적은 여러 객체 간 활동을 조정하는 데에 있다. 구체적으로 말하자면 A라는 객체를 B라는 객체 또는 여러 객체로 연결시켜 A의 내용이 변경되면 B에게 알려주는 것을 목적으로 한다. A의 변경 내용과 특성에 대해 알림을 받은 B는 자신의 상태를 업데이트 하는 등 조치를 취하도록 결정내릴 수 있다. 따라서 변경과 업데이트에 대한 개념은 이러한 세 번째 객체 의존성 관계 유형을 지원하는 데에 중요하다.
Object 클래스 내의 프로토콜은 다음과 같다.
의존객체 접근(dependents access) | |
addDependent: anObject | anObject 인자를 수신자의 의존 객체들 중 하나로 추가한다. |
removeDependent: anObject | 수신자의 의존 객체들 중 하나인 anObject 인자를 제거한다. |
dependents | 수신자가 변경되면 알려야 하는 객체들, 즉 수신자에 의존하는 객체들의 OrderedCollection을 응답한다. |
release | 수신자로 다시 참조할 수 있는 객체의 참조를 제거한다. 이 메시지는 의존 객체에 참조를 생성하는 서브클래스라면 어디서든 재구현되는데, super release 표현식은 그러한 재구현이라면 어디든 포함되어 있다. |
변경 및 업데이트(change and update) | |
changed | 수신자가 일반적인 방식으로 변경되었으므로 update: 메시지를 각 의존 객체에게 전송하여 알린다. |
changed: aParameter | 수신자가 변경되었으므로 aParameter 인자에 의해 표기된다. 보통 인자는 의존 객체의 변경 프로토콜의 일부로서 Symbol로 되어 있고, 수신자 자체를 인자로 사용하는 것이 기본 행위다. 의존 객체에게 모두 알린다. |
update: aParameter | 수신자가 의존하는 객체가 변경되었다. 수신자는 그에 따라 그 상태를 업데이트한다 (기본 행위는 어떠한 조치도 취하지 않는다). |
broadcast: aSymbol | aSymbol 인자를 수신자의 모든 의존 객체에 단항 메시지로서 전송한다. |
broadcast: aSymbol with: anObject | aSymbol 인자를 anObject 인자로 된 키워드 메시지로 하여 수신자의 모든 의존 객체로 전송한다. |
Object 인스턴스 프로토콜 |
신호등을 모델링하는 객체를 예로 들어보자. 길모퉁이에 있는 일반 신호등에는 각각 다른 색으로 된 불이 3개가 있다. 주어진 시간동안 하나에만 불이 들어올 수 있다(ON). 이런 의미에서 3개의 불 각각의 ON-OFF 상태는 나머지 두 개의 상태에 따라 좌우된다. 이러한 관계를 생성하는 방법에는 여러 가지가 있다. 아래와 같이 Light 클래스를 생성한다고 가정하자.
클래스명 | Light |
슈퍼클래스 | Object |
인스턴스 변수명 | status |
클래스 메서드 | instance creation
setOn
↑self new setOn
setOff
↑self new setOff
|
인스턴스 메서드 | status
turnOn
self isOff
ifTrue: [status ← true. self changed]
turnOff
self isOn
ifTrue: [status ← false]
testing
isOn
↑status
isOff
↑status not
change and update
update: aLight
aLight = = self ifFalse: [self turnOff]
private
setOn
status ← true
setOff
status ← false
|
모델은 매우 간단하다. Light는 켜지거나 꺼지거나 둘 중 하나이므로 상태 플래그는 인스턴스 변수로 유지되고, Light가 켜지면 true가, Light가 꺼지면 false가 된다. Light가 켜질 때마다(turnOn) 스스로에게 changed 메시지를 전송한다. 또 다른 Light가 켜졌기 때문에 그에 대한 반응으로 Light가 꺼졌다는 가정 하에서 그 외의 상태 변경은 의존 객체에게 방송되지 않는다. changed 에 대한 기본 반응은 모든 의존 객체에게 update: self 메시지를 전송하는 것이다 (예: 변경된 객체는 update: 메시지에 대한 인자다). 이후 꺼졌음을 의미하기 위해 update: 이 Light에서 구현된다. 매개변수가 수신자일 경우 물론 update: 은 무시된다.
TrafficLight 클래스는 불의 개수를 조정하기 위해 정의된다. with: 을 이용한 인스턴스 생성 메시지는 생성되어야 할 Lights 개수를 그 인자로 취한다. 각 Light는 다른 모든 Lights에 의존한다. TrafficLight가 파괴되면 그 Lights 간 모든 의존 관계도 끊어진다 (의존 객체의 연결을 끊기 위해 Object 클래스로부터 상속된 메시지는 release로서, 모든 Lights에게 메시지를 방송하기 위해 TrafficLight에서 구현된다).
클래스명 | TrafficLight |
슈퍼클래스 | Object |
인스턴스 변수명 | lights |
클래스 메서드 | instance creation
with: numberOfLights
↑self new lights: numberOfLights
|
인스턴스 메서드 | operate
turnOn: lightNumber
(lights at: lightNumber) turnOn
initialize-release
release
super release.
lights do: [ :eachLight | eachLight release].
lights ← nil
private
lights: numberOfLights
lights ← Array new: (numberOfLights max: 1).
lights at: 1 put: Light setOn.
2 to: numberOfLights do:
[ :index | lights at: index put: Light setOff].
lights do:
[ :eachLight |
lights do:
[ :dependentLight |
eachLight ~~ dependentLight
ifTrue: [eachLight addDependent: dependentLight]]]
|
Private 초기화 메서드는 lights: numberOfLights다. 첫 번째 불만 제외하고 불마다 꺼진 채로 생성된다. 이후 각 불은 (addDependent: 메시지를 이용해) 다른 모든 불로 연결된다. 시뮬레이트된 TrafficLight는 라운드 로빈식으로 작동하여 일정 시간 동안 순서대로 불이 하나씩 켜지도록 작동할 것이다. 아래 간단한 예제는 첫 번째 불이 켜진 TrafficLight를 생성한 후 한 번에 하나씩 불을 켠다. 신호등 시뮬레이션은 불을 제어하는 여러 모델을 포함할 수 있다.
trafficLight ← TrafficLight with: 3.
trafficLight turnOn: 2.
trafficLight turnOn: 3
turnOn: to a TrafficLight 메시지는 지정된 Light로 turnOn 메시지를 전송한다. Light가 현재 꺼져 있으면 켜진 것으로 설정되고 changed 메시지가 전송된다. changed 메시지는 각 의존하는 Light에 update: 을 전송하고, 의존하는 불이 켜져 있으면 꺼진다.
이러한 의존성 프로토콜이 특히 중요할 때는 객체의 그래픽한 이미지를 여러 개 지원할 때다. 객체가 변경되면 이러한 변경이 표시되는 내용에 영향을 미치는지 결정할 수 있도록 이미지가 그러한 변경을 알게 된다는 의미에서 각 이미지는 객체에 의존한다고 볼 수 있다. Smalltalk-80 시스템에 대한 사용자 인터페이스는 객체가 변경되었다는 알림을 방송하기 위해 이러한 지원을 자유롭게 활용하는데, 이는 사용자가 화면에 표시되는 정보의 내용과 관련해 취할 수 있는 모든 행위의 메뉴 시퀀스의 내용을 조정하는 데에 사용된다. 메뉴 자체는 가능한 행위를 모두 연결하여 생성할 수 있는데, 이는 신호등을 모두 연결시킨 방법과 매우 유사하다.
메시지 처리(Handling)
Smalltalk-80 시스템에서 모든 처리는 객체로 메시지를 전송함으로써 실행된다. 효율성을 이유로 Message 클래스의 인스턴스는 오류가 발생할 때만 생성되고, 메시지 상태는 접근 가능한 구조체에 저장되어야 한다. 따라서 시스템 내 대부분 메시지는 Message의 인스턴스를 직접 생성하여 객체로 전송하는 형태를 취하지 않는다.
일부 상황에서는 메시지 전송의 메시지 선택자를 계산하는 편이 유용하다. 가령 가능한 메시지 선택자의 목록을 객체가 유지하고 있고, 계산을 바탕으로 하여 이러한 선택자들 중 하나가 선택된다고 가정하자. 이것이 변수 selector의 값으로 할당된다고 치자. 이제 일부 객체, 가령 수신자에게 메시지를 전송하고자 한다. 아래와 같이 간단히 표현식을 작성할 수는 없는 것이,
receiver selector
receiver가 참조하는 객체에게 단항 메시지 selector를 전송하라는 의미이기 때문이다. 하지만 아래와 같이 작성할 수는 있겠다.
receiver perform: selector
selector 인자의 값을 receiver를 향한 메시지로서 전송하는 결과가 발생한다. 계산된 메시지를 객체로 전송하는 기능을 지원하는 프로토콜이 Object 클래스에서 제공된다. 이러한 프로토콜은 단항 메시지를 비롯해 계산된 키워드를 전송하기 위한 메서드를 포함한다.
메시지 처리(message handling) | |
perform: aSymbol | aSymbol 인자가 나타내는 단항 메시지를 수신자에게 전송한다. 인자는 메시지의 선택자다. 선택자에게 예상되는 인자의 개수가 0이 아닐 경우 오류를 보고한다. |
perform:aSymbol with: anObject | 인자가 나타내는 키워드 메시지를 수신자에게 전송한다. 첫 번째 인자 aSymbol은 메시지의 선택자이다. 또 다른 인자 anObject는 전송될 메시지의 인자다. 선택자에게 예상되는 인자의 개수가 1이 아닐 경우 오류를 보고한다. |
perform: aSymbol with: firstObject with: secondObject | 인자가 나타내는 키워드 메시지를 수신자에게 전송한다. 첫 번째 인자 aSymbol은 메시지의 선택자이다. 또 다른 인자 firstObject와 secondObject는 전송될 메시지의 인자다. 선택자에게 예상되는 인자의 개수가 2가 아닐 경우 오류를 보고한다. |
perform: aSymbol with: firstObject with: secondObject with: thirdObject | 인자가 나타내는 키워드 메시지를 수신자에게 전송한다. 첫 번째 인자 aSymbol은 메시지의 선택자이다. 또 다른 인자 firstObject, secondObject, thirdObject는 전송될 메시지의 인자다. 선택자에게 예상되는 인자의 개수가 3이 아닐 경우 오류를 보고한다. |
perform: selector withArguments: anArray | 인자가 나타내는 키워드 메시지를 수신자에게 전송한다. selector 인자는 메시지의 선택자이다. 메시지의 인자는 anArray의 요소들이다. 선택자에게 예상되는 인자의 개수가 anArray의 크기와 동일하지 않으면 오류를 보고한다. |
Object 인스턴스 프로토콜 |
이러한 프로토콜을 사용할 수 있는 한 가지 방법은 사용자 명령의 디코더(decoder)로서 사용하는 것이다. 가령 피연산자가 연산자보다 우선하는 매우 간단한 계산기를 모델화한다고 가정하자. 한 가지 가능한 구현은 (1) 현재 결과, 즉 첫 번째 피연산자와 (2) 정의되지 않은 두 번째 피연산자가 있는 계산기를 표현한다. 각 연산자는 결과가 이해하는 메시지 선택자다. clear 메시지는 한 번만 보내면 피연산자를 리셋하고, 피연산자가 리셋될 때 clear 메시지를 보내면 결과를 리셋할 것이다.
클래스명 | Calculator |
슈퍼클래스 | Object |
인스턴스 변수명 | result operand |
클래스 메서드 | instance creation
new
↑super new initialize
|
인스턴스 메서드 | accessing
result
↑result
calculating
apply: operator
(self respondsTo: operator)
ifFalse: [self error: 'operation not understood'].
operand isNil
ifTrue: [result ← result perform: operator]
ifFalse: [result ← result perform: operator with: operand]
clear
operand isNil
ifTrue: [result ← 0]
ifFalse; [operand ← nil]
operand: aNumber
operand ← aNumber
private
initialize
result ← 0
|
예에서는 Calculator 클래스의 사용을 설명한다.
hp ← Calculator new
Calculator로서 hp를 생성한다. 인스턴스 변수는 결과 0과 피연산자 nil로 초기화된다.
hp operand: 3
사용자가 3이라는 키를 누르고 피연산자를 설정했다고 가정하자.
hp apply: #+
사용자가 덧셈을 선택한다. apply 에 대한 메서드는 연산자가 이해되었으며 operand가 nil이 아니라고 결정하므로 결과는 아래 표현식으로 설정된다.
result perform: operator with: operand
그리고 위의 표현식은 아래와 같다.
0 + 3
메서드는 result를 3으로 설정하고, 피연산자는 3으로 남아 있으므로
hp apply: #+
위는 3을 추가하여 결과는 이제 6이 된다.
hp operand: 1.
hp apply: #-.
hp clear.
hp apply: #squared
결과는 6이고 1을 뺀 후 제곱을 계산하면 최종 결과가 25가 된다.
시스템 프리미티브 메시지
Object 클래스에는 전체적인 시스템 구현의 요구를 지원하기 위한 목적의 메시지들이 다수 있다. 이는 시스템 프리미티브로 분류된다. 이러한 메시지들은 인스턴스의 상태로 직접 접근을 제공하고, 각 객체가 값을 그 변수로 저장하는 데 대해 독자적인 제어를 갖고 있다는 원칙에 어느 정도 위배된다. 하지만 이러한 접근은 언어의 인터프리터에서 필요로 한다. 이는 프로그래밍 환경에 클래스 설명/개발 유틸리티를 제공하는 데에 유용하다. 이러한 메시지의 예로 instVarAt: anInteger와 instVarAt: anInteger put: anObject를 들 수 있는데, 이는 각각 명명된 인스턴스 변수의 값을 검색하고 저장하는 기능을 수행한다.
시스템 프리미티브(system primitives) | |
become: otherObject | 수신자의 인스턴스 포인터와 otherObject 인자를 교환한다. 전체 시스템에서 수신자를 가리키는 모든 변수는 이제 인자를 가리키고 인자는 그러한 변수들을 가리킨다. 두 객체 중 하나가 SmallInteger일 경우 오류를 보고한다. |
instVarAt: index | 수신자에 명명된 변수를 응답한다. 변수의 번호는 명명된 인스턴스 변수가 정의된 순서에 따른다. |
instVarAt: index put: value | value 인자를 수신자에 명명된 변수에 저장한다. 변수의 번호는 명명된 인스턴스 변수가 정의된 순서를 따른다. value를 응답한다. |
nextInstance | 해당 클래스의 모든 인스턴스의 열거에서 수신자 다음에 오는 인스턴스를 응답한다. 모든 인스턴스가 열거된 경우 nil을 응답한다. |
numberOfPointers | 수신자가 참조하는 객체의 개수를 응답한다. |
refct | 수신자 위치에서 가리키는 시스템 내 객체 포인터의 개수를 응답한다. 수신자가 SmallInteger일 경우 0을 응답한다. |
Object 인스턴스 프로토콜 |
아마도 가장 특이하면서 효과적인 시스템 프리미티브 메시지는 become: otherObject 메시지일 것이다. 이 메시지에 대한 응답은 수신자의 인스턴스 포인터를 otherObject 인자의 인스턴스 포인터와 바꾸는 것이다. 해당 메시지의 사용 예는 몇몇 컬렉션 클래스에서 grow의 구현을 살펴보면 된다. grow 메시지는 컬렉션을 복사하지 않고 (고정된 길이의) 컬렉션에 저장 가능한 요소 개수를 증가시켜야 하는 경우 전송되는데, 컬렉션을 복사할 경우 컬렉션으로 공유된 참조가 모두 유지되어야 하므로 바람직하지 못하다. 따라서 새로운 컬렉션이 생성되고 그 요소들이 저장된 다음 원본 컬렉션은 새로운 컬렉션으로 변형된다. 원본 컬렉션에 대한 모든 포인터는 이제 새로운 컬렉션을 향한 포인터로 대체된다.
아래는 SequenceableCollection 클래스에 명시된 grow에 대한 메서드다.
grow
| newCollection |
newCollection ← self species new: self size + self growSize.
newCollection replaceFrom: 1 to: self size with: self.
↑self become: newCollection
growSize
↑10
서브클래스는 확장할 요소의 개수를 별도로 명시하도록 growSize 메시지에 대한 응답을 재정의할 수 있다.