Smalltalk クラス作成編
既存クラス等の紹介をしてきましたが、今回はSystem Browserを使って自分でクラスを作成をしてみます。また作成したクラスのテストケースを作成し、ユニットテストをしてみます。
作成するクラスを考える
今回はOrderedDictionaryというクラスを作りましょう。Dictionaryは順序は保存しないクラスですが、順序を持ったDictionaryを作成しましょう。(RubyのOrderedHashを作るということです)。ちなみに、Smalltalkコミュニティのメンバーが作成したSmallTalk R4.1 http://smalltalk.smalltalk-users.jp/ にはHashというクラスで実装されております。
**[追記]**後日知ったのですがPharoには、OrderedIdentityDictionaryといったクラスも存在しておりました。OrderedDictionaryと異なりvaluesの結果は順序が保たれてはいないようです。
OrderedDictionaryの要件
OrderedDictionaryの実行結果を以下に記述しますので、実現するクラスを作成していきます。
"workspace"
| dict |
dict := OrderedDictionary new.
dict at: #x put: 1.
dict at: #z put: 3.
dict at: #y put: 2.
dict values. "print it ==> #(1 3 2)"
dict do:[:v | Transcript cr; show: v] .
dict removeKey:#x.
dict removeKey:#y.
dict removeKey:#z.
System Browserの確認
Smalltalk開発環境入門編にてSystem Browserを少し説明しましたが、ここでSystem Browserの見方を紹介します。System Browserは左から、クラスカテゴリ、クラス、メソッドカテゴリ、メソッドの順に表示され、それぞれを定義します。下の一番大きい領域はコードペインと呼び、実際のコードを記述します。
Objective-Cを使ったことがある方は、Smalltalkのクラス定義の方法を知るとヘッダーファイルとメソッドファイルの存在理由がわかると思います。
パッケージ(クラスカテゴリ)の追加
まずはパッケージ、クラスカテゴリの追加をします。クラスカテゴリはクラスをまとめるために使います。クラスカテゴリの追加は、クラスカテゴリにて右クリック[Add Package]をクリックして、カテゴリ名を入力します。
今回ははAdvent-Collectionsとします。
クラスの定義
続いては、クラスを定義していきましょう。クラス定義は、コードペインに記述します。クラスカテゴリ追加時点では、以下のようなクラス定義のテンプレートが表示されていると思います。
Object subclass: #NameOfSubclass
instanceVariableNames: ''
classVariableNames: ''
category: 'Advent-Collections'
このテンプレートを変更してクラスを作成していきます。Smalltalkのクラスは必ずスーパークラスを持ちます。
クラス定義 スーパークラス名 subclass: #クラス名
instanceVariableNames: 'インスタンス変数名1 インスタンス変数名2'
classVariableNames: 'クラス変数名1 クラス変数名2'
category: 'クラスカテゴリ'
OrderedDictionaryを作成します。コードペインを以下のように記述します。今回はObjectクラスを継承したクラスにします。インスタンス変数は、キーの順序を保管するkeysとキーと値を保管するcontentsを用意します。
Object subclass: #OrderedDictionary
instanceVariableNames: 'keys contents'
classVariableNames: ''
category: 'Advent-Collections'
メソッドカテゴリの追加
続いてインスタンス変数を整理するためのメソッドカテゴリを作成します。メソッドカテゴリの名前は、Smalltalkの他のクラスを参考にしながら作成するとよいでしょう。例えば、初期化をするinitializeはinitializationカテゴリに所属することが多いです。インスタンスメソッドinitializeはクラスにnewを送ってオブジェクト生成する際に自動起動するメソッドです。System Browserでクラス名を選択した状態でメソッドカテゴリ領域で右クリックで追加できます。
インスタンスメソッドの定義
インスタンスメソッドはSystem Browserでメソッドカテゴリ名を選択した状態でコードペインにて記述します。以下のコードを記述します。
initialize
keys := OrderedCollection new.
contents := Dictionary new.
記述したらコードを保存します。保存方法はコードペイン上で右クリックをして[Accept]をクリックします(ショートカット Macならcommand + s, WindowsならALt + s)。初めてソースコードを保存する場合は、名前を確認されますので入力しましょう。
その他のメソッドも実装します。
accesingカテゴリ
at: key
^ contents at: key
at: key put: value
contents at: key put: value.
(keys includes:key) ifFalse:[
keys add: key. ].
^ value.
keys
^ keys
size
^ keys size
values
| out |
out := WriteStream on: (Array new: keys size).
keys do:[:v |
out nextPut: (contents at:v)].
^ out contents
removeKey: key
keys remove: key.
contents removeKey: key.
emulatingカテゴリ
do: aBlock
keys do:[:v | aBlock value:v ].
動作確認
Worksapceで動作確認をしてみましょう。
自動テストを作成する
作成したOrderedDictionaryですが、機能としては少なく改良の余地がありそうです。改良するたびに毎回テストするのは非常に辛い作業です。そこでSUnitを使って自動テストを行うようにします。実はJavaのJUnitはSUnitを元に開発されていたりします。
SUnitを使ったテストの記述方法は色々とありますが、今回は一番単純なものを紹介します。テストするメソッドを選択して、右クリック。[Generate test and jump]をクリック。
あとはテスト内容を記述します。
testAt
| dict |
dict := OrderedDictionary new.
dict at: #x put: 1.
dict at: #z put: 3.
dict at: #y put: 2.
self assertCollection: (dict keys) equals:(Array with:#x with:#z with:#y) asOrderedCollection.
自動テストの実行
テストを実行するには、テストメソッドの左のボタンをクリックします。
テストが成功するとボタンがグリーンになります。
またはTest Runnerからも実行できます。
参考
自由自在 Squeakプログラミング PDF版
http://swikis.ddo.jp/squeak/13
今日から使おうSmalltalk
http://www.slideshare.net/newapplesho/smalltalk-32627289
Pharo by Example
http://www.pharobyexample.org/
SmallTalk R4.1
http://smalltalk.smalltalk-users.jp/
Smalltalk開発環境入門編
http://qiita.com/newapplesho/items/4a90c0771e15506c8484
テスト駆動開発入門, Kent Beck, ピアソン