SqueakByExample:2.6
SBEGame 클래스 정의하기
이제 SBEGame 이라고 이름붙일 게임에 필요한 다른 클래스를 만들어보도록 하겠습니다.
브라우저 메인 창에서 클래스 정의 템플릿을 보이게 하십시오.
이미 선택한 클래스 카테고리의 이름을 두 번 클릭하거나, (instance 버튼을 클릭하여)SBECell의 정의를 보이게 하십시오. 다음과 같이 코드를 편집하고 accept 하십시오.
클래스 2.3: SBEGame 클래스 정의
BorderedMorph subclass: #SBEGame
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'SBE-Quinto'
이제 BoderedMorph의 하위클래스를 만들 차례입니다.[1] Morph 는 스퀵의 모든 그래픽 도형에 대한 상위클래스가 되며(놀랍군요!), BorderedMorph 는 테두리를 가진 Morph 입니다. 두번째 줄의 따옴표 사이에 인스턴스 변수의 이름을 넣을 수도 있겠지만, 지금은 그냥 빈 목록으로 남겨 놓겠습니다.
자 이제 SBEGame에 대한 initialize 메서드를 정의하도록 하겠습니다.
SBEGame에 대한 메서드를 브라우저에 다음과 같이 입력하고, accept 해보십시오:
메서드 2.4: 게임 초기화
initialize
| sampleCell width height n |
super initialize.
n := self cellsPerSide.
sampleCell := SBECell new.
width := sampleCell width.
height := sampleCell height.
self bounds: (5@5 extent: ((width*n) @(height*n)) + (2 * self borderWidth)).
cells := Matrix new: n tabulate: [ :i :j | self newCellAt: i at: j ].
스퀵은 일부 용어의 의미를 모른다고 불평할 것입니다. 스퀵은 CellsPerSide 메시지를 모른다고 할 것이며, 대신하기 위한 수많은 컬렉션을 제안할텐데, 이 경우에는 철자 오류가 있습니다.
그림 2.8 알려지지 않은 selector를 감지한 스퀵 | 그림 2.9: 새로운 인스턴스 변수 선언하기 |
하지만 cellsPerSide 는 잘못 입력한 것이 아닙니다. ---아직 정의하지 않은 메서드일 뿐입니다--- 짧은 시간 안에 메서드를 정의해보겠습니다.
메뉴에서 첫 번째 항목을 선택하면 cellsPerside 를 실제로 의미했음을 확인할 수 있습니다.
다음차례로, 스퀵은 cells 의 의미를 모른다고 불평할 것입니다. 이런 문제를 수정하기 위한 수많은 방법이 제공됩니다.
cells 를 인스턴스 변수로 만들 것이므로, declare instance 를 선택하십시오.
마지막으로, 스퀵은 마지막 줄에서 보낸 newCellAt:at: 메시지에 대해 불평할 것입니다. 이것도 실수가 아니기 때문에 메시지를 확인하기 바랍니다.
클래스 정의를 다시 한 번 살펴보면(instance 버튼을 클릭하면 실행할 수 있음), Browser 가 인스턴스 변수인 cells 를 포함하도록 클래스의 정의를 수정[2]했다는 것을 알 수 있습니다.
이제 initialize 메서드를 살펴 보겠습니다. | sampleCell width height n | 줄에서는 임시로 4개의 변수를 선언합니다. 변수의 범위와 수명이 메서드에 국한되기 때문에 임시 변수temporary variables라고 부르도록 하겠습니다. 설명이 잘 되어있는 이름이 붙은 임시 변수는 코드 가독성을 더 좋게 하는데 도움이 됩니다. Smalltalk 에는 상수와 변수를 구분하기 위한 특별한 문법이 없으며, 사실 이 네 개의 모든"변수“variables”"는 사실 상수입니다. 4-7 번째 줄은 이들 상수를 정의합니다.
게임판은 얼마나 커야 할까요? 몇몇 정수 크기만큼의 cell 을 담기에 충분히 크고, 그 cell 주위에 경계를 그리기에 충분할만큼 커야 합니다. cell 의 갯수는 얼마정도가 적당할까요? 5? 10? 100? 아직은 모르지만, 이미 알고 있다고 해도, 나중에 변경하게 될지 모르겠군요. 그렇기 때문에, 이러한 숫자를 알아야 하는 책임을 다른 메서드로 위임하려 합니다. 이 메서드는 cellsPerSide 라는 이름을 가지며, 1~2 분 사이에 작성하려 합니다.
initialize 메서드에 대한 내용을 accept 했을 때, "확인(confirm), 수정(correct) 또는 취소(cancel)" 를 물어보는 다이얼로그가 뜨게되는데, 이런 현상은 스퀵이 요청한 이름을 가진 메서드가 정의되지도 않았을때, cellsPerSide 라는 메시지를 보내기 때문입니다. 걱정하지 마세요. 이런 방법은 아직 정의되지 않은 다른 메서드로 작업을 진행하는 좋은 방법입니다. 왜 일까요? 글쎄요, initialize 메서드를 작성하기 전에 먼저 필요성을 느낀것도 아니기 때문에, 상황이 발생되었을때 의미있는 이름을 부여하고 작업의 흐름을 방해하지 않고 작업을 계속 진행할 수 있습니다.
네번째 줄은 이렇게 정의된 cellsPerSide 메서드를 사용합니다. 스몰토크에서 self cellsPerSide 는 cellsPerSide 메시지를 나 자신(SBEGame)을 의미하는 객체인 self 에 보냅니다. 게임판의 각 모서리당 cell 의 갯수 응답을 n 에 할당합니다.
그 다음의 세 줄에서는, 새로운 SBECell 객체를 만들고, 객체의 너비와 높이를 적당한 임시 변수에 넣습니다.
여덟번째 줄은 새로운 객체의 bounds 를 설정합니다. 세부적인 내용에 대한것은 걱정할 필요가 없으며, 괄호 안에 있는 표현이 point (5,5) 와 그 하단 오른쪽 모서리에 원점orign을 둔 정사각형(즉, 왼쪽 위)을 만든다고 믿으십시오. 적절한 개수의 cell 이 있는 공간을 허용하려는 목적에 맞게 충분하게 멀리 떨어져 있습니다.
마지막 줄은 SBEGame 객체의 인스턴스 변수인 cells 에 올바른 행과 열의 개수대로 새로 만든 Matrix 를 할당합니다. Matrix 클래스(클래스도 객체이므로, 메시지를 보낼 수 있습니다)에 new:tabulate: 메시지를 보내서 처리했습니다. new:tabulate: 의 이름에 두 개의 콜론(:)이 있는걸 보면, 이 메서드는 인자를 두 개 받는다는걸 알 수 있습니다. 인자는 콜론 바로 다음에 위치하게 됩니다. 만약 당신이 인자를 모두 괄호 안에 넣는 프로그래밍 언어를 사용해 왔었다면, 이 표기법은 매우 이상하게 느껴질 것입니다. 당황하지 마세요, 이것은 단지 문법일 뿐입니다! 이런 방법은 메서드의 이름으로 인자의 역할을 설명하는데 활용할 수 있기 때문에, 상당히 좋은 문법으로 알려져 있습니다. 예를 들어, Matrix rows: 5 columns: 2 는 5개의 가로 열과 2개의 세로 열을 갖는 것이지 2개의 가로 열과 5개의 세로 행을 가진 것이 아니라는 점을 확실히 알게 해줍니다.
Matrix n tabulate: [ :i :j | self newCellAt: i at: j ] 은 새로운 n x n 2차원 배열을 만들고 요소를 초기화합니다. 각각의 초기 값은 좌표에 따라 다릅니다. (i,j)번째 요소는 newCellAt: i at: j. 처리 결과로 초기화합니다.
지금까지의 내용이 바로 initialize 입니다. 이 메시지 내용을 accept 하면, 아마도 작성한 코드를 가지런히 다듬고(pretty-up formating) 싶을 것입니다. 직접 일일히 할 필요는 없고, 노랑-버튼 메뉴에서, more▷prettyprint를 선택하면, 사용자를 위해 브라우저가 알아서 해줄 것입니다. 메서드를 깔끔하게 출력하고 난 후에, 다시 accept 하거나, 결과가 맘에 들지 않으면 물론 취소 (CMD-l 숫자 1이 아닌 L의 소문자입니다)할 수 있습니다. 대신, 코드가 보일때는 언제나 pretty-printer 를 브라우저가 사용하도록 설정할 수도 있습니다. 보이는 것을 조절하려면 버튼 표시줄에서 가장 오른쪽 버튼을 사용하십시오.
more... 를 더 많이 사용하신다면, more... 를 직접 불러오려고 클릭할 때 Shift 키를 누른 채로 클릭하면 그 상태가 유지된다는 것을 알고 계시는 것이 좋습니다.