1 00:00:00,720 --> 00:00:06,000 みなさん、こんにちは。 反 if キャンペーンについてお話します。 2 00:00:06,480 --> 00:00:09,480 特に、説明したいのは 3 00:00:09,920 --> 00:00:13,720 返り値の nil や nil チェックが いかに良くないかです。 4 00:00:13,920 --> 00:00:18,520 ここに例題コードがあります。 オブジェクト的ではありません。 5 00:00:19,040 --> 00:00:21,680 メソッドの引数は動物です。 6 00:00:21,880 --> 00:00:24,680 動作はその動物によって変わります。 7 00:00:24,880 --> 00:00:30,560 例えば、犬に尻尾を振らせたり アヒルにガーガー鳴かせたりします。 8 00:00:31,000 --> 00:00:33,440 猫には他のことをさせます。 9 00:00:34,040 --> 00:00:38,080 さて、どうして if 文を使うのは 問題があるのでしょう? 10 00:00:39,120 --> 00:00:43,800 特に、レシーバーの型を 検査する場合です。 11 00:00:45,080 --> 00:00:49,200 例えば、ここに新しい動物を 追加したいとします。 12 00:00:49,360 --> 00:00:53,400 当てはまる可能性がありそうな if 文を探して 13 00:00:53,680 --> 00:00:56,880 プロジェクトコード全体を チェックする必要があるでしょう。 14 00:00:57,040 --> 00:01:01,280 プロジェクトを通して大量のコードを 書き換えなければならなくなります。 15 00:01:01,640 --> 00:01:06,560 さらに、メソッドの場合分けを 追加することは厄介で 16 00:01:06,840 --> 00:01:10,440 瑣末なことに 追い立てられてしまいます。 17 00:01:10,720 --> 00:01:13,760 動物を追加すると このメソッドが長大になって 18 00:01:13,920 --> 00:01:17,160 それぞれの動物の種類も 細くなっていくでしょう。 19 00:01:17,320 --> 00:01:21,760 例えば犬に尻尾があるというような 単純な特徴であっても 20 00:01:21,920 --> 00:01:25,920 それぞれに場合分けを 作らなければならなくなります。 21 00:01:26,120 --> 00:01:31,640 コードがどんどん煩雑になり 読みにくくなっていきます。 22 00:01:32,240 --> 00:01:35,760 だから、そんなことはやめて メッセージを送ります。 23 00:01:36,080 --> 00:01:38,480 何度も繰り返してきたポイントです。 24 00:01:38,640 --> 00:01:42,240 覚えるべきキーポイントは メッセージを送ることです。 25 00:01:42,880 --> 00:01:46,280 先ほどのコードは このように置き換わります。 26 00:01:47,040 --> 00:01:51,160 それぞれの関係するクラスに showHappiness メソッドがあり 27 00:01:51,320 --> 00:01:56,480 各クラスがそれぞれの動物が どう喜びを示すのかを決めます。 28 00:01:56,760 --> 00:02:02,720 どんな動物であれ、やることは メッセージを送ることだけです。 29 00:02:05,560 --> 00:02:07,560 動物に showHappiness を送ります。 30 00:02:08,600 --> 00:02:11,360 そして 1 つのメソッドが 実行されます。 31 00:02:11,520 --> 00:02:15,040 Pharo は if という機能を 追い出そうとしているということです。 32 00:02:15,200 --> 00:02:19,960 動物の種類に応じてどのメソッドを 適用するのか Pharo が決めます。 33 00:02:20,120 --> 00:02:22,920 これが自動的に実行されます。 34 00:02:23,120 --> 00:02:27,480 オブジェクトの種類ごとに if を指定する必要はありません。 35 00:02:28,040 --> 00:02:31,200 そんなことをするとコードの 統一性とダイナミズムが減ります。 36 00:02:32,560 --> 00:02:36,040 では、その特殊なケースである nil について議論します。 37 00:02:36,440 --> 00:02:39,160 あるメソッドが nil を返すと 38 00:02:39,320 --> 00:02:43,120 クライアントは if を 使わざるを得なくなります。 39 00:02:43,280 --> 00:02:46,200 if は褒められたものではない のに反して。 40 00:02:47,520 --> 00:02:50,520 パラメータと Interencer を使った 41 00:02:50,760 --> 00:02:55,440 例題コードがあります。 42 00:02:55,600 --> 00:02:57,880 コードの種類は重要ではありません。 43 00:02:58,160 --> 00:03:01,440 ここを見ればわかるように 場合によって nil が返されます。 44 00:03:01,840 --> 00:03:04,480 つまり、このコードを使う時は 45 00:03:04,760 --> 00:03:08,600 メッセージの返り値を チェックする必要があります。 46 00:03:08,800 --> 00:03:11,480 rulesForFact: が nil を返したかどうかを。 47 00:03:11,680 --> 00:03:14,560 応答によって異なることをします。 48 00:03:14,720 --> 00:03:17,280 この場合には 49 00:03:17,880 --> 00:03:20,280 複数形を使っているので 50 00:03:20,440 --> 00:03:23,960 このメソッドはおそらく コレクションを返すのでしょう。 51 00:03:24,200 --> 00:03:26,880 nil を避ける効果的な解決策は 52 00:03:27,080 --> 00:03:31,000 この場合は空のコレクションを 返すことです。 53 00:03:31,200 --> 00:03:33,080 これで大丈夫な場合が多くあります。 54 00:03:33,400 --> 00:03:38,520 nil を返さずに空のコレクションを返すことで コードがシンプルになります。 55 00:03:38,880 --> 00:03:42,400 クライアントは単にそのコレクションを 列挙するだけだからです。 56 00:03:42,560 --> 00:03:45,360 もし空だったら何もしません。 57 00:03:46,480 --> 00:03:48,400 例外を使うケースもあります。 58 00:03:48,840 --> 00:03:52,360 ファイルストリームがあって 59 00:03:52,520 --> 00:03:56,320 書き込みモードで open しておらず エラーにする場合には 60 00:03:56,560 --> 00:04:01,640 nil を返すかわりに 例外をあげてシステムに知らせます。 61 00:04:01,920 --> 00:04:05,600 Pharo ではこれを 「例外を申し立てる」と呼びます。 62 00:04:05,760 --> 00:04:09,560 Exception クラスかそのサブクラスの インスタンスを生成して 63 00:04:09,720 --> 00:04:11,960 signal メッセージを送ります。 64 00:04:13,680 --> 00:04:19,640 こうすることで、問題がありそうな時に nextPutAll: メソッドのクライアントに 65 00:04:19,800 --> 00:04:23,960 返り値が nil かどうかを チェックせずに済みます。 66 00:04:24,200 --> 00:04:26,800 クライアント側や クライアントのクライアントなどが 67 00:04:26,960 --> 00:04:31,680 その例外を処理します。 68 00:04:31,880 --> 00:04:37,840 特定のレベルで集中して 例外を捕まえることができます。 69 00:04:38,560 --> 00:04:40,240 これで if の使いすぎを避けられます。 70 00:04:40,880 --> 00:04:45,720 他にも、インスタンス変数の値が 初期化されているかどうかを 71 00:04:45,880 --> 00:04:49,280 確認するための nil チェックも 見かけます。 72 00:04:49,600 --> 00:04:54,800 変数がまだ nil であれば 何らかの反応をするようなコードは 73 00:04:54,960 --> 00:04:59,200 すぐに初期化して 74 00:04:59,360 --> 00:05:01,960 あらゆる場合に対応できる値に したほうが良いです。 75 00:05:02,120 --> 00:05:03,120 つまりここでは 76 00:05:03,600 --> 00:05:06,680 members は コレクションを格納しますが 77 00:05:06,840 --> 00:05:10,600 nil ではなく空のコレクションで 初期化します。 78 00:05:10,920 --> 00:05:13,520 これもまた うまくいく場合が多くあります。 79 00:05:13,960 --> 00:05:18,000 変数に初期値を与えたいけれども 80 00:05:18,400 --> 00:05:22,680 その初期値を計算するのに 大きなコストがかかるようならば 81 00:05:22,840 --> 00:05:26,560 初期値を計算するのをギリギリまで 遅らせることができます。 82 00:05:26,720 --> 00:05:30,520 計算しなくて済む場合もあるので 実行時間の節約になります。 83 00:05:31,320 --> 00:05:35,040 そんな場合には 遅延初期化を使います。 84 00:05:35,200 --> 00:05:38,280 値が必要とされた時に初期化をします。 85 00:05:38,680 --> 00:05:42,400 もし値がまだ nil であれば 値を代入します。 86 00:05:42,560 --> 00:05:47,240 もし値がもう nil でなければ その値をそのまま返します。 87 00:05:48,000 --> 00:05:52,600 ここに nil と if がありますが 1 つしかありません。 88 00:05:53,160 --> 00:05:57,920 この変数を使う人は この decent メソッドを使うことで 89 00:05:58,520 --> 00:06:00,720 nil かどうかチェックする 必要がありません。 90 00:06:01,320 --> 00:06:03,960 時々あるのですが 91 00:06:04,520 --> 00:06:09,840 反応する必要があるかどうか チェックする必要があります。 92 00:06:10,320 --> 00:06:12,160 この例題のような場合です。 93 00:06:12,800 --> 00:06:15,840 ここに ToolPalette があります。 94 00:06:16,120 --> 00:06:19,440 もしツールが選択されていたら 反応します。 95 00:06:19,600 --> 00:06:22,960 もし選択されていなければ 反応しないでおきます。 96 00:06:23,640 --> 00:06:26,480 もし selectedTool が nil を返したら 97 00:06:26,720 --> 00:06:31,600 ツールが選択されていないので 何もする必要がありません。 98 00:06:31,800 --> 00:06:35,760 selectedTool が何かを返したら 99 00:06:36,000 --> 00:06:40,080 それに対して何かするように 頼みます。 100 00:06:41,040 --> 00:06:42,920 これの代わりになる良い方法は 101 00:06:43,360 --> 00:06:45,720 NullObject パターンを使うことです。 102 00:06:45,880 --> 00:06:50,400 ツールのあるなしで 2 つの場合に分けるのではなく 103 00:06:50,600 --> 00:06:54,440 ツールの 1 つは何もしないツールにして 1 つの場合にします。 104 00:06:54,600 --> 00:06:57,120 このツールは デフォルトで選択されています。 105 00:06:57,360 --> 00:07:01,840 アクションの依頼に対して 何もしないツールを作るのです。 106 00:07:03,120 --> 00:07:09,000 ツールを選ばないのではなく 何もしないツールを選ぶのです。 107 00:07:10,360 --> 00:07:14,800 NullObject についてもっと知りたければ これらの文献を読んでください。 108 00:07:15,680 --> 00:07:16,840 まとめます。 109 00:07:17,000 --> 00:07:19,640 メッセージは if よりも有効です。 110 00:07:19,840 --> 00:07:22,520 if を使う場合もあるでしょうが 111 00:07:22,680 --> 00:07:27,680 if を避けて代わりにメッセージを送る ことができる場合も多くあります。 112 00:07:28,840 --> 00:07:34,160 nil を返すのは避けましょう。 返り値が nil かチェックするための 113 00:07:34,360 --> 00:07:38,280 if を入れなければならなくなります。 114 00:07:39,440 --> 00:07:44,720 初期化は生成時に行うか あるいは遅延初期化を使いましょう。 115 00:07:45,680 --> 00:07:50,760 デフォルトまたは何もしない振る舞いを 表すオブジェクトを作りましょう。 116 00:07:50,960 --> 00:07:54,960 これは Pharo だけでなく あらゆるオブジェクト指向言語で有効です。 117 00:07:55,200 --> 00:08:00,840 どんな言語を使うにせよ、これらの ポイントを覚えておくことが大事です。