みなさん、こんにちは。
今日は、私も含め、Pharo 開発者が
よくやってしまう間違いについて お話します。
よくある間違いをより早く見つけて 修正する方法をお見せします。
ここにコードがあります。
実行すると、デバッガーが開いて
あるオブジェクトに self メッセージが送られ
そのオブジェクトが self メッセージを 理解できない、と言ってきます。
self はメッセージではないと言いたいのですが 実際、とてもよく送られます。
コードのどこかに間違いがあるのです。
ちょっと見てみると
ピリオドが抜けていることに気付きます。
そのために実行中に
self が DiceHandle new の結果に 送られてしまいます。
DiceHandle クラスには self メソッドはないので
デバッガーが開きます。
修正するには、最初の行の最後に ピリオドを追加すれば良いです。
よくあるもう 1 つの問題は
繋がらないはずのメッセージが 繋がってしまっている場合です。
ここで「includes:ifTrue: は存在しない」 というエラーになります。
includes: も ifTrue: も存在しますが includes:IfTrue: は存在しません。
よく見ると
本当に includes:ifTrue: メッセージが
送られている、という事実に気付きます。
レシーバー x に引数が 2 つ。 33 と ブロックです。
動きません。
Pharo がキーワードを見る時には
それに続く全てのキーワードを見ようとします。
全てのキーワードを取り込んで それを 1 つのメッセージと見做します。
つまり、括弧が
欠けていたのです。
括弧があれば ifTrue: メッセージが x includes: 33 の結果に送られます。
同様に assert:includes: なんてありません。
意図しているのは includes: の結果を assert: することなので
括弧が必要です。
1 つの式の中で 複数のキーワードメッセージを使う時は
躊躇せずに括弧を入れて
キーワードメッセージを分離してください。 さもなくば Pharo は 1 つのメッセージとして
くっつけてしまいます。
この例では
numbers に数値のコレクションが欲しいのですが
今のところ数はたった 1 つ 35 しかありません。
しかし、numbers の値を見ると コレクションではなく
35 という数値になっています。 おかしいですね。
同様に、このコードでは
Dice クラスに new メッセージを送ると
整数 6 が得られます。
6 面のダイスではなく。
どちらの問題も、原因は同じです。
もっとしっかり見てみると
add: の後に yourself を追加すると
問題が解決します。 なぜかというと
add: は加えたオブジェクトを返すからです。
つまり、 OrderedCollection new add: 35
は、35 を返します。
最後に yourself を追加すれば 確実にレシーバーが得られます。
そうすれば numbers は 数値のコレクションになるはずです。
つまりこの場合の解決策は
各メッセージの最後に yourself を追加することです。
また別の問題があります。
Book クラスの borrow メソッドがあります。
実行すると
nil does not understand ifFalse: というエラーメッセージが出ます。
つまり、ここでは ifFalse: メッセージを nil に送ってしまっています。
どういうことかというと inLibrary の値は nil つまり
変数のデフォルト値のままということです。
おそらくは、inLibrary は 初期化されていないのでしょう。
この変数にデフォルト値を設定してあげる 必要があります。
修正はとても簡単です。
initialize メソッドで
Book クラスのインスタンスを 生成するごとに
インスタンス変数 inLibrary に
True を入れればよいのです。
ただし、このコードを今実行すると
別のエラーメッセージが出ます。
Class True does not understand ifFalse:
原因はどこでしょう？ 答えは、ここで代入したものが
クラスだからです。
真偽値ではなくクラスなのです。
真偽値の真の値は true です。 小文字で始まります。
クラス名は一般に大文字で始まります。
つまり True はクラスで
true は True クラスの 唯一のインスタンスです。
また別の問題があります。
Dice クラスの roll メソッドでは
ダイスを振ると
1 から面の数までの数が得られます。
ただし、この場合は ダイスを振るとダイスが得られてしまいます。
出た目ではなく。
お見せしたメソッドは 下のほうのメソッドと等価です。
つまりリターンのないメソッドは デフォルトで self を返すのです。
そのため roll メソッドが実行されると 振った結果として
ダイス自身を返します。
faces コレクションに atRandom を 送った結果ではなく。
同じ原因ですがちょっと違う例です。
Dice クラスに新しいメソッドを
定義しています。
Dice クラスに
Dice クラスのインスタンスを生成する 新しいメソッドを追加したいのですが
デフォルトの面の数を ゼロに初期化しています。
Dice クラスに new メッセージを送ると
Dice クラス自身が返ってきます。
Dice クラスの 新しいインスタンスではなく。
どちらの場合でも
リターンがないために
デフォルトで self つまり レシーバーを返しています。
クラスメソッドの場合 self はクラスです。
これらの問題を解決するには
返したい値を返すために キャレットを追加するだけです。
次の問題は
このコードが実行されると
システムが固まってしまって 何もできなくなります。
Pharo と対話することが できなくなります。
何が原因でしょう？
Dice クラスに新しいメソッドを
定義していますが
self は Dice クラスで
つまり self new は 自分自身を再帰的に呼び出しています。
ここで意図していることは
Dice クラスのスーパークラスの デフォルトのインスタンス生成を
利用することで
それに何か処理を加えたいのです。
このように書くと 無限ループになります。
なので、ここでは self の代わりに super と書き換える必要があります。
すると スーパークラスでの実装を 使うことができます。
理解していただきたいことは
我々は皆、たくさんの間違いをおかします。
今お見せした間違いは 全ての Pharo 開発者が
とても頻繁にやってしまうものです。
ありがちなのは
ピリオドの抜け
括弧の抜け
キャレットの抜け
そして yourself の抜けです。
問題の根本を見つけるために
デバッガーを使ってみてください。
すぐに閉じたりしないでください。
すぐに閉じてしまうと、問題を修正する 道を見失ってしまいます。