1 00:00:00,680 --> 00:00:01,800 こんにちは。 2 00:00:01,960 --> 00:00:04,640 今日はメッセージの話をしようと思います。 3 00:00:04,800 --> 00:00:08,800 大量のメッセージと小さなメソッドの 利点をお見せします。 4 00:00:08,960 --> 00:00:11,520 多くの開発者は 反対なことを信じているようですが。 5 00:00:12,640 --> 00:00:16,120 このスライドはこの講義での 設計に関することを示しています。 6 00:00:16,640 --> 00:00:18,920 Pharo だけでなく 7 00:00:19,080 --> 00:00:22,280 あらゆるオブジェクト指向言語での 設計で有効なことです。 8 00:00:23,800 --> 00:00:27,240 知っての通り、メッセージ送信は 一種のフックで 9 00:00:27,400 --> 00:00:30,640 その振る舞いを定義する 場所になっています。 10 00:00:30,840 --> 00:00:35,680 開発者はよく大きなメソッドを好みます。 11 00:00:35,840 --> 00:00:37,760 そのほうが理解しやすいと言います。 12 00:00:37,920 --> 00:00:41,480 行を読んでコードを理解します。 13 00:00:41,640 --> 00:00:44,320 しかし、この講義では 14 00:00:44,480 --> 00:00:46,720 それは褒められたものではない ということをお見せします。 15 00:00:46,880 --> 00:00:50,080 一般に大きなメソッドは悪い設計です。 16 00:00:51,280 --> 00:00:53,120 クラス階層や 17 00:00:53,280 --> 00:00:56,280 複数クラスでの同じメソッドの定義で 18 00:00:56,440 --> 00:00:58,480 場合分けを定義することができます。 19 00:00:59,000 --> 00:01:02,960 もし、あるクラスに大量の操作が 定義されて、肥大化していて 20 00:01:03,120 --> 00:01:07,320 状態に応じて適切な操作を 選ばなければならないとしたら 21 00:01:07,480 --> 00:01:11,240 「この状態の場合にはこの操作を実行して 22 00:01:11,400 --> 00:01:13,960 この状態のときにはあの操作を実行する」 のようなコードを書くことになります。 23 00:01:14,120 --> 00:01:16,680 結果、長大なコードに 24 00:01:16,840 --> 00:01:19,360 if があちこちに溢れかえります。 25 00:01:19,520 --> 00:01:22,520 つまり、新しい場合分けが必要な時に 26 00:01:22,680 --> 00:01:25,040 色々なメソッドのあちこちを 修正しなければならないということです。 27 00:01:25,400 --> 00:01:28,400 右側の設計では 28 00:01:29,200 --> 00:01:33,880 それぞれの場合についてクラスが対応付けられて 操作が定義されています。 29 00:01:34,240 --> 00:01:38,040 操作をおこなうメッセージを オブジェクトに送るだけで 30 00:01:38,320 --> 00:01:41,280 条件分けがされます。 31 00:01:41,440 --> 00:01:45,320 プログラマが if を書く必要はありません。 32 00:01:46,640 --> 00:01:50,480 多態性の原理によって 自動的に行われます。 33 00:01:50,640 --> 00:01:55,920 次のスライドでは、1 つのプログラムを 34 00:01:56,080 --> 00:01:58,120 小さく分割することで改善する例を お見せします。 35 00:01:58,640 --> 00:02:01,760 ここに巨大メソッドがあります。 理解しにくくて 36 00:02:01,920 --> 00:02:03,480 多くのことを行います。 37 00:02:03,640 --> 00:02:05,560 私がやりたいのは、これです。 38 00:02:06,280 --> 00:02:07,640 サブクラスで 39 00:02:07,800 --> 00:02:11,840 この変数に異なる値を与えます。 40 00:02:12,800 --> 00:02:14,720 つまりこういうことです。 41 00:02:14,880 --> 00:02:18,000 サブクラスを作って 42 00:02:18,160 --> 00:02:20,920 全てのコードをコピーして 同じコードを持たせて 43 00:02:21,080 --> 00:02:24,080 それぞれについて修正を加えます。 44 00:02:24,920 --> 00:02:27,480 Java のような 45 00:02:27,640 --> 00:02:29,160 private というキーワードを使う言語では 46 00:02:29,320 --> 00:02:32,560 メソッドで使われる属性が private の場合 47 00:02:32,720 --> 00:02:34,760 こうするのは不可能です。 48 00:02:35,000 --> 00:02:39,040 そのメソッドが private な インスタンス変数を使っていると 49 00:02:39,200 --> 00:02:42,520 そのサブクラスは同じコードを使えません。 50 00:02:42,880 --> 00:02:46,960 いずれにせよコードの複製は 基本的に良くないのです。 51 00:02:47,480 --> 00:02:50,120 バグも複製してしまいます。 52 00:02:50,280 --> 00:02:54,560 元のメソッドにバグがあったら その複製にもバグができることになります。 53 00:02:55,800 --> 00:03:00,640 そして一方を修正したら 全ての複製について修正する必要があります。 54 00:03:01,520 --> 00:03:03,760 開発者としては 余計な仕事をすることになります。 55 00:03:03,920 --> 00:03:07,200 しかも、色々なコピーの箇所を 覚えておく必要があるわけです。 56 00:03:08,760 --> 00:03:11,960 正しい解決策はメッセージ送信です。 57 00:03:12,320 --> 00:03:16,560 対応するメソッドの中身を 直接埋め込んで書くのではなく 58 00:03:16,720 --> 00:03:20,160 メッセージを送ることで 59 00:03:20,320 --> 00:03:22,920 サブクラスがその振る舞いを オーバーライドすることができます。 60 00:03:24,160 --> 00:03:26,640 bar メソッドを見ると 61 00:03:26,800 --> 00:03:28,680 self に foo を送っています。 62 00:03:28,840 --> 00:03:30,960 クラス A では foo は 10 を返しますが 63 00:03:31,120 --> 00:03:33,880 サブクラスはこの値をオーバーライドして 64 00:03:34,040 --> 00:03:36,600 例えば 42 に置き換えることができます。 65 00:03:37,240 --> 00:03:40,720 今お見せしたコードを改良して 66 00:03:40,880 --> 00:03:44,360 複製ではなく継承機構を使うには 67 00:03:44,520 --> 00:03:46,320 どうしたら良いでしょう? 68 00:03:46,480 --> 00:03:50,520 この部分を抽出して メッセージ送信にします。 69 00:03:50,680 --> 00:03:54,200 メソッドの抽出という リファクタリング機能でできます。 70 00:03:54,360 --> 00:03:58,080 ツールがこのコードをこのコードに 変換してくれます。 71 00:03:59,200 --> 00:04:01,960 前のスライドで選択したコードが 72 00:04:02,120 --> 00:04:06,240 ratio という新しいメソッドとして 括り出されています。 73 00:04:07,800 --> 00:04:12,320 そしてコードが書かれていた部分は メッセージ送信になっています。 74 00:04:13,240 --> 00:04:15,880 つまり、サブクラスでは 75 00:04:17,000 --> 00:04:20,360 この振る舞いを変えることができる ということになります。 76 00:04:20,520 --> 00:04:22,440 完全に変えてしまっても良いですし 77 00:04:22,600 --> 00:04:26,640 スーパークラスの振る舞いを利用して 修正するのでも良いです。 78 00:04:26,800 --> 00:04:28,560 今やったことは 79 00:04:28,720 --> 00:04:30,720 super に ratio メッセージを送って 80 00:04:30,880 --> 00:04:34,760 スーパークラスにあるこのコードを実行して 81 00:04:34,920 --> 00:04:37,720 その結果に10を加えます。 これで元の目的を達成できます。 82 00:04:39,880 --> 00:04:44,760 もう 1 つの方法は この部分を抽出して 83 00:04:44,920 --> 00:04:49,440 サブクラスがその振る舞いを 変更できるようにすることです。 84 00:04:50,360 --> 00:04:53,160 このコード片を 85 00:04:53,320 --> 00:04:55,640 メソッドにします。 86 00:04:56,200 --> 00:04:59,440 そして元のメソッドでは メッセージを送ります。 87 00:05:02,120 --> 00:05:03,240 この場合 88 00:05:03,480 --> 00:05:07,520 クラスがハードコードされています。 89 00:05:07,680 --> 00:05:11,400 つまり、サブクラスが UINode クラスの サブクラスなど 90 00:05:11,560 --> 00:05:14,160 別のクラスを使いたい場合に 91 00:05:14,320 --> 00:05:17,280 メソッド全体を複製しなければ ならなくなるでしょう。 92 00:05:17,440 --> 00:05:19,600 ここで同じ方法で 93 00:05:19,760 --> 00:05:23,440 クラスをメソッドに括り出して 94 00:05:23,600 --> 00:05:27,040 サブクラスがそれを 変更できるようにします。 95 00:05:27,200 --> 00:05:28,840 それがこれです。 96 00:05:29,000 --> 00:05:32,440 メソッドにしたい部分を括り出して 97 00:05:34,720 --> 00:05:36,760 メッセージを送ります。 98 00:05:36,920 --> 00:05:40,560 メッセージを送ることで サブクラスがその振る舞いを変えることができます。 99 00:05:40,920 --> 00:05:43,760 前に言ったように プログラマーの中には 100 00:05:43,920 --> 00:05:46,440 このやり方に同意しない人たちもいます。 101 00:05:47,120 --> 00:05:51,520 細かく散らばったメソッドを読むのは 難しいと言います。 102 00:05:51,680 --> 00:05:54,280 それよりも大きなメソッドを 1 行ずつ読むのうが良いと言います。 103 00:05:54,440 --> 00:05:58,200 これは実践として良いやり方ではありません。 アプリケーションが大きくなると 104 00:05:58,360 --> 00:06:01,400 1 行ずつ読んでも解らなくなるからです。 105 00:06:01,560 --> 00:06:05,640 1 行ずつ読んでも全体で何が起こるのか 把握することはできません。 106 00:06:05,800 --> 00:06:10,440 これこそ抽象を使うべきところであり 107 00:06:10,600 --> 00:06:13,040 メソッドから一部を括り出すことの 意味があります。 108 00:06:13,200 --> 00:06:17,320 個々の詳細を理解しなくても メソッド全体としての意味を理解できます。 109 00:06:18,040 --> 00:06:21,520 したがって、小さなメソッドは優れています。 いつでもそうすべきなのです。 110 00:06:22,400 --> 00:06:24,920 では続けましょう。 111 00:06:26,160 --> 00:06:30,240 ここに 55 という値が 112 00:06:30,400 --> 00:06:31,800 メソッドにハードコードされています。 113 00:06:31,960 --> 00:06:36,000 そのためにサブクラスは 例えば 100 に変更できません。 114 00:06:36,160 --> 00:06:40,480 そこで前にやったように この値を括り出して 115 00:06:41,800 --> 00:06:46,600 別のメソッドにすることで サブクラスが値を変えることができます。 116 00:06:47,440 --> 00:06:50,120 このやり方のもう 1 つの利点は 117 00:06:50,280 --> 00:06:52,520 ここに 55 という値が 書いてありましたが 118 00:06:52,680 --> 00:06:54,720 それを self averageRatio とすることで 119 00:06:54,880 --> 00:06:58,560 数値ではなく名前を示していることです。 120 00:06:58,720 --> 00:07:02,200 これで 55 という値が何なのか 知ることができます。 121 00:07:02,360 --> 00:07:07,440 これで 55 は averageRatio だと コードを読んで理解できるわけです。 122 00:07:07,600 --> 00:07:11,240 このように、小さなメソッドは コードの読解の助けになります。 123 00:07:11,680 --> 00:07:14,800 55 を返す averageRatio メソッドを 定義することで 124 00:07:14,960 --> 00:07:17,720 サブクラスがその値を変えることが できるようになります。 125 00:07:17,880 --> 00:07:21,640 では、クラスのユーザーがその値を 変えたい時はどうしたら良いでしょう? 126 00:07:22,000 --> 00:07:23,360 考えられる方法としては 127 00:07:23,520 --> 00:07:25,840 インスタンス変数を使います。 128 00:07:26,640 --> 00:07:30,600 前のスライドの averageRatio メソッドが 129 00:07:30,760 --> 00:07:33,920 インスタンス変数の値を 返すようにします。 130 00:07:34,080 --> 00:07:38,480 インスタンス変数に値が設定されていなければ デフォルト値を返します。 131 00:07:38,800 --> 00:07:40,920 デフォルト値は 55 です。 132 00:07:41,640 --> 00:07:45,120 これでのオブジェクトのユーザーは 133 00:07:45,280 --> 00:07:48,080 好きなように値を設定できます。 134 00:07:48,240 --> 00:07:49,680 この設計では 135 00:07:49,840 --> 00:07:54,520 サブクラスは defaultAverageRatio をオーバーライドして 136 00:07:55,000 --> 00:07:56,320 値を変更することができます。 137 00:07:56,480 --> 00:08:01,120 そしてこのクラスのユーザーは 値を設定することができます。 138 00:08:01,640 --> 00:08:04,360 この手法はグリュイエール(チーズ) 指向プログラミングと呼ばれます。 139 00:08:05,400 --> 00:08:09,440 というのも、オブジェクト指向の プログラムやメソッドは 140 00:08:09,600 --> 00:08:12,200 フックという穴を持っていて 141 00:08:12,360 --> 00:08:15,960 その穴をサブクラスが 埋めることができるからです。 142 00:08:16,800 --> 00:08:18,320 まとめると 143 00:08:18,480 --> 00:08:22,280 コードはサブクラスで再利用され 再定義されます。 144 00:08:22,440 --> 00:08:25,280 メッセージを送るたびに 145 00:08:25,440 --> 00:08:29,240 サブクラスにはスーパークラスの振る舞いを 変える機会を得ます。 146 00:08:29,400 --> 00:08:31,600 修正しても完全に変えても構いません。 147 00:08:32,560 --> 00:08:37,760 メソッドを小さくすることは良いことです。 それによって細かな式に名前を与え 148 00:08:38,080 --> 00:08:42,560 サブクラスにその式を変更する自由を 与えるからです。