﻿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
どんな言語を使うにせよ、これらの
ポイントを覚えておくことが大事です。