1 00:00:00,480 --> 00:00:03,920 今日は前回の講義の続きをします。 2 00:00:04,080 --> 00:00:07,280 前回は小さなメソッドの利点を 説明しました。 3 00:00:07,440 --> 00:00:09,160 今日はいくつかの例をお見せします。 4 00:00:09,320 --> 00:00:12,840 まずは前回の復習をしましょう。 5 00:00:13,000 --> 00:00:15,280 メッセージを送ることは 分岐をもたらすことがあります。 6 00:00:15,440 --> 00:00:19,640 つまり、実際にどの実装が使われるか 7 00:00:19,800 --> 00:00:21,560 表面上明らかでない分岐といえます。 8 00:00:22,040 --> 00:00:25,960 そして Pharo がメッセージのレシーバー に応じて、どの分岐を辿るか選択します。 9 00:00:26,480 --> 00:00:30,360 クラス階層が分岐の場合分けを定義します。 クラスが多ければ場合分けも増えます。 10 00:00:30,600 --> 00:00:34,360 サブクラスを作ることで 簡単に場合を追加できます。 11 00:00:35,760 --> 00:00:40,480 サブクラスはスーパークラスのコードを 上書きしたり修正することができます。 12 00:00:42,000 --> 00:00:43,920 そして、メッセージを送ることは 13 00:00:44,080 --> 00:00:49,360 サブクラスに振る舞いを変える機会を 与えるということです。 14 00:00:50,520 --> 00:00:54,080 今日は、テンプレートメソッドと呼ばれる デザインパターンを見てみましょう。 15 00:00:54,240 --> 00:00:56,880 デザインパターンの本に載っています。 16 00:00:57,640 --> 00:00:59,960 テンプレートメソッドとは 17 00:01:00,120 --> 00:01:04,600 あるアルゴリズムの 全体的な振る舞いを定義して、 18 00:01:04,760 --> 00:01:08,800 内部にフックを用意したものです。 19 00:01:08,960 --> 00:01:12,680 そしてフックはサブクラスによって 再定義することができます。 20 00:01:13,840 --> 00:01:16,000 これがアルゴリズムです。 21 00:01:16,320 --> 00:01:19,320 このアルゴリズムは何かをするわけですが 正確に何をするのかはわかりません。 22 00:01:20,280 --> 00:01:25,320 そして hookMethod1 で何かをします。 23 00:01:25,720 --> 00:01:27,080 そしてまた何かをやって 24 00:01:27,240 --> 00:01:29,480 そして hookMethod2 で何かをします。 25 00:01:29,640 --> 00:01:34,200 hoodMethod1 と hookMethod2 は どちらも完結したメソッドで 26 00:01:34,360 --> 00:01:37,040 サブクラスで再定義されます。 27 00:01:37,600 --> 00:01:41,320 hookMethod1 と 2 のそれぞれについて 2 つの可能性があります。 28 00:01:42,760 --> 00:01:46,520 メソッドがデフォルトの振る舞いを 29 00:01:46,680 --> 00:01:49,800 持っているかどうかです。 30 00:01:50,160 --> 00:01:53,840 ここでは、 hookMethod1 には デフォルトの振る舞いがあるとしましょう。 31 00:01:54,000 --> 00:01:57,160 つまり、サブクラスが何も定義しなければ 32 00:01:57,680 --> 00:02:03,000 hookMethod1 はデフォルトの振る舞いで 機能するということです。 33 00:02:04,240 --> 00:02:06,280 一方、hookMethod2 は 34 00:02:06,440 --> 00:02:09,960 デフォルトの振る舞いを 持たないとします。 35 00:02:10,120 --> 00:02:12,840 サブクラスが振る舞いを 定義しなければなりません。 36 00:02:13,000 --> 00:02:15,960 クラスの設計者が デフォルトの振る舞いを 37 00:02:16,120 --> 00:02:19,840 定義するかどうかを選択します。 38 00:02:20,640 --> 00:02:23,760 printString を 1 つ目の例にします。 39 00:02:23,920 --> 00:02:27,640 オブジェクトに printString メッセージを送ると 40 00:02:27,800 --> 00:02:31,120 そのオブジェクトを表現した 文字列が得られるというものです。 41 00:02:31,280 --> 00:02:33,160 ここにディレイ(Delay)があります。 42 00:02:34,400 --> 00:02:37,640 10 秒のディレイを作りました。 43 00:02:38,480 --> 00:02:42,960 このディレイに printString メッセージを送ると 44 00:02:43,120 --> 00:02:47,720 「a Delay」に加えて括弧の中に 何ミリ秒かを示す値が表示されます。 45 00:02:48,200 --> 00:02:52,840 printString メソッドは Object クラスで このように定義されています。 46 00:02:53,600 --> 00:02:56,640 printStringLimitedTo: メッセージを 送ります。 47 00:02:57,160 --> 00:02:58,560 printStringLimitedTo: の実装で 48 00:02:58,720 --> 00:03:02,920 そのオブジェクトを表現する文字列を 49 00:03:03,080 --> 00:03:04,880 得ています。 50 00:03:05,040 --> 00:03:09,360 もしその文字列が長すぎる場合には 51 00:03:10,200 --> 00:03:13,800 適切な長さで切り捨てて 52 00:03:13,960 --> 00:03:17,400 最後に「...etc...」と付け加えて 53 00:03:17,560 --> 00:03:19,880 この文字列は最後まで表示されていない ことを表します。 54 00:03:20,640 --> 00:03:23,600 ここで重要なことは 55 00:03:23,760 --> 00:03:28,120 printStringLimitedTo: は self に printOn: メッセージを送ることです。 56 00:03:28,280 --> 00:03:29,880 このメソッドが 57 00:03:30,040 --> 00:03:33,400 サブクラスで再定義されます。 (再定義されないこともあります) 58 00:03:34,040 --> 00:03:37,560 Node や Apple の printString で 59 00:03:37,720 --> 00:03:40,120 何が得られるか見てみると 60 00:03:40,280 --> 00:03:43,800 Node new は 「a Node」を返します。 61 00:03:44,200 --> 00:03:48,440 これが Node クラスの printString です。 62 00:03:49,120 --> 00:03:53,240 そしてこれが Apple クラスの printString です。 63 00:03:53,720 --> 00:03:57,000 この振る舞い デフォルトの振る舞いは 64 00:03:57,160 --> 00:03:59,920 Object クラスで定義されています。 65 00:04:00,080 --> 00:04:04,320 つまり、あらゆるオブジェクトの printString のデフォルトの振る舞いが 66 00:04:04,680 --> 00:04:06,000 これです。 67 00:04:06,160 --> 00:04:10,000 クラス名を取得します。 68 00:04:10,160 --> 00:04:12,840 この場合は Node と Apple です。 69 00:04:13,120 --> 00:04:16,720 そしてクラス名が母音で始まっていれば 70 00:04:17,640 --> 00:04:19,760 前に「an」を加えて 71 00:04:19,920 --> 00:04:23,000 子音で始まっていれば 「a」にします。 72 00:04:23,160 --> 00:04:26,320 こうすることで「a Node」や 「an Apple」が得られます。 73 00:04:27,880 --> 00:04:30,520 これがデフォルトの振る舞いです。 74 00:04:31,120 --> 00:04:34,400 このデフォルトの振る舞いは 75 00:04:34,560 --> 00:04:36,280 printOn: を再定義することで 変更することができます。 76 00:04:36,720 --> 00:04:38,480 ディレイの場合には 77 00:04:38,640 --> 00:04:41,560 Delay の printString は 78 00:04:41,720 --> 00:04:46,240 デフォルトの printOn: のもので 始まります。 79 00:04:46,400 --> 00:04:47,640 つまり「a Delay」の部分です。 80 00:04:47,800 --> 00:04:52,720 そしてそれに続いて、括弧の中に ミリ秒の値を加えています。 81 00:04:52,880 --> 00:04:56,040 printOn: メソッドがやっていることを そのまま説明すると 82 00:04:56,880 --> 00:05:01,080 まず最初にスーパークラスの デフォルトの printOn: をします。 83 00:05:02,160 --> 00:05:05,240 続いて、括弧を開いて 84 00:05:05,560 --> 00:05:08,080 設定されたミリ秒の値を表示して 85 00:05:08,880 --> 00:05:11,240 そして括弧を閉じます。 86 00:05:11,760 --> 00:05:14,720 このように再定義されています。 87 00:05:15,040 --> 00:05:18,120 Delay クラスは Object クラスの printOn: メソッドを 88 00:05:18,280 --> 00:05:21,520 修正しています。 89 00:05:21,680 --> 00:05:25,200 あるいは、振る舞いを完全に再定義 することもできます。 90 00:05:25,680 --> 00:05:29,880 例えば、真偽値がそうです。 91 00:05:30,040 --> 00:05:31,760 false を表示すると 92 00:05:31,920 --> 00:05:34,800 「false」が得られます。 93 00:05:34,960 --> 00:05:37,240 「a false」ではなく 94 00:05:37,400 --> 00:05:39,120 単に「false」です。 95 00:05:39,280 --> 00:05:43,200 こうするために、 printOn: では 96 00:05:43,360 --> 00:05:45,680 単に文字列「false」を ストリームに送っています。 97 00:05:46,200 --> 00:05:48,080 完全に上書きで再定義しています。 98 00:05:48,240 --> 00:05:51,640 上書きでの再定義のもう 1 つの例は インターバルです。 99 00:05:51,800 --> 00:05:54,120 インターバルは最小値から最大値までの 100 00:05:54,280 --> 00:05:56,920 間にある値の集合です。 101 00:05:57,680 --> 00:06:02,680 1 から 100 までのインターバルは 「(1 to: 100)」と表示されます。 102 00:06:04,320 --> 00:06:07,600 インターバル 1 to: 100 by: 3 も 103 00:06:07,840 --> 00:06:10,400 1、4、と続きますが 104 00:06:10,560 --> 00:06:14,720 by 付きで同じように表示されます。 105 00:06:15,680 --> 00:06:18,080 これがどのように 実装されているかというと 106 00:06:18,240 --> 00:06:22,640 Interval クラスが printOn: メソッドを再定義して 107 00:06:23,120 --> 00:06:26,200 ストリームに異なるメッセージを 送っています。 108 00:06:26,360 --> 00:06:28,600 まず括弧を開きます。 109 00:06:28,760 --> 00:06:31,240 括弧はここと、ここにあります。 110 00:06:31,600 --> 00:06:36,560 そしてインターバルの 最初の値を表示します。 111 00:06:36,720 --> 00:06:39,360 ここでは、この「1」と ここでは、この「1」です。 112 00:06:39,680 --> 00:06:41,440 そして「to:」を書きます。 113 00:06:44,600 --> 00:06:47,640 そして最後の値を書きます。 この「100」と 114 00:06:48,520 --> 00:06:49,800 この「100」です。 115 00:06:50,000 --> 00:06:53,720 そして「by」が必要な場合、つまり デフォルトの 1 以外の場合には 116 00:06:53,880 --> 00:06:56,800 それを書きます。 117 00:06:59,120 --> 00:07:02,320 そして最後に 括弧を閉じます。 118 00:07:04,360 --> 00:07:07,000 以上のように、printString は 119 00:07:07,160 --> 00:07:11,000 デザインパターンの テンプレートメソッドを使うことで 120 00:07:11,160 --> 00:07:15,120 それぞれのクラスが自分自身の 文字列表現を実装できるようになっています。 121 00:07:15,360 --> 00:07:19,600 もう 1 つの例を見てみましょう。 オブジェクトのコピーです。 122 00:07:19,760 --> 00:07:21,120 copy を使って 123 00:07:21,280 --> 00:07:25,960 あるオブジェクトから コピーを作ることができます。 124 00:07:26,840 --> 00:07:29,680 オブジェクトのコピーは複雑です。 125 00:07:30,880 --> 00:07:33,120 いくつかのやり方がありますが 126 00:07:33,280 --> 00:07:35,200 インスタンスのコピーが どうなるべきかを 127 00:07:35,360 --> 00:07:39,200 それぞれのクラスが決めます。 128 00:07:39,720 --> 00:07:43,400 テンプレートメソッドを使った シンプルな解決策が 129 00:07:43,560 --> 00:07:46,520 copy と postCopy を使う方法です。 130 00:07:46,880 --> 00:07:50,560 copy がテンプレートメソッドで postCopy がフックです。 131 00:07:51,360 --> 00:07:54,200 copy メソッドは Object クラスにあります。 132 00:07:54,800 --> 00:07:57,640 コメントを読んでください。 133 00:07:58,640 --> 00:08:02,920 このメソッドでは shallowCopy メッセージを 134 00:08:03,080 --> 00:08:05,240 self に送って 135 00:08:05,400 --> 00:08:08,640 続いて 結果に postCopy を送ります。 136 00:08:08,920 --> 00:08:12,360 shallowCopy は 137 00:08:12,520 --> 00:08:16,200 元のオブジェクトのインスタンス変数の 内容を共有する 138 00:08:16,360 --> 00:08:17,960 新しいオブジェクトを生成します。 139 00:08:18,200 --> 00:08:22,080 2 つのオブジェクトは 同一のインスタンス変数を持っています。 140 00:08:22,240 --> 00:08:26,640 一方のオブジェクトのインスタンス変数が 指すオブジェクトでの変更操作は 141 00:08:28,880 --> 00:08:32,320 もう一方のオブジェクトの インスタンス変数からも見えます。 142 00:08:34,320 --> 00:08:35,720 これが shallowCopy の デフォルトの振る舞いです。 143 00:08:35,880 --> 00:08:38,800 postCopy の定義次第で 144 00:08:38,960 --> 00:08:41,400 変数が同じオブジェクトを共有するかどうか が決まります。 145 00:08:41,560 --> 00:08:44,240 postCopy が何もしない場合には 全ての変数の参照先は共有されます。 146 00:08:44,400 --> 00:08:48,640 それぞれのクラスが ある変数は共有して、また別の変数は 147 00:08:48,800 --> 00:08:52,440 共有しないということを postCopy で決めます。 148 00:08:53,040 --> 00:08:55,680 postCopy のデフォルト動作では 全て共有されます。 149 00:08:55,840 --> 00:08:59,240 デフォルトの postCopy は そのオブジェクトをそのまま返します。 150 00:09:00,440 --> 00:09:04,080 しかしそれ以外のものもあります。 151 00:09:04,240 --> 00:09:06,680 例えば、Bag クラスです。 152 00:09:06,840 --> 00:09:09,600 Bag は集合の一種ですが 153 00:09:09,840 --> 00:09:13,440 postCopy メソッドはその中身を コピーします。 154 00:09:14,640 --> 00:09:17,520 Bag には インスタンス変数 contents がありますが 155 00:09:17,680 --> 00:09:21,080 Bag クラスの設計者は 156 00:09:21,240 --> 00:09:22,920 Bag のコピーを作る時には 157 00:09:23,080 --> 00:09:26,000 インスタンス変数 contents を 共有せずに 158 00:09:26,160 --> 00:09:27,920 別々のオブジェクトを指すようにしました。 双方の contents は 159 00:09:28,080 --> 00:09:31,960 同じ初期値を持っていますが 一方のみを変更することができます。 160 00:09:33,440 --> 00:09:34,720 つまり 161 00:09:34,880 --> 00:09:39,440 postCopy は 新しく作られたコピーに送られます。 162 00:09:41,880 --> 00:09:45,080 コピーのインスタンス変数は 元のオブジェクトと参照を共有しています。 163 00:09:45,240 --> 00:09:47,200 共有したくない場合には 164 00:09:47,360 --> 00:09:50,280 新しく作ります。 165 00:09:50,760 --> 00:09:52,600 ここに 変数 contents がありますが 166 00:09:52,760 --> 00:09:55,120 ここに元のオブジェクトの コピーを入れます。 167 00:09:55,280 --> 00:09:57,000 こうして共有しないようにします。 168 00:09:57,160 --> 00:10:00,080 それぞれの Bag ごとに contents の参照先を持っています。 169 00:10:00,600 --> 00:10:03,400 Dictionary(辞書)ではより複雑です。 170 00:10:03,560 --> 00:10:06,840 辞書はキーと値のペアの集合 171 00:10:07,000 --> 00:10:09,080 言い換えると、関連のコレクションです。 172 00:10:09,240 --> 00:10:12,360 辞書のコピーを作成する時には 173 00:10:12,520 --> 00:10:16,000 関連のコレクションをコピーするだけでなく 174 00:10:16,160 --> 00:10:19,440 それぞれの関連も 175 00:10:19,600 --> 00:10:21,960 それぞれの辞書で別のものに する必要があります。 176 00:10:22,120 --> 00:10:26,520 そうすることで一方の辞書を書き変えても もう一方には影響しないようにします。 177 00:10:26,920 --> 00:10:30,640 そのためには テーブルをコピーするだけでなく 178 00:10:30,800 --> 00:10:32,400 その中の各ペアもコピーします。 179 00:10:32,560 --> 00:10:34,720 これが辞書のコピーで 行われていることです。 180 00:10:34,880 --> 00:10:38,640 まとめるとデザインパターンの テンプレートメソッドはとてもよく使われます。 181 00:10:39,040 --> 00:10:41,520 良い設計の目印です。 182 00:10:41,720 --> 00:10:45,880 良いオブジェクト設計をすれば たくさんのテンプレートメソッドができます。 183 00:10:46,040 --> 00:10:48,560 それが自然で良い設計です。 184 00:10:49,080 --> 00:10:50,920 テンプレートメソッドを使うことを 躊躇することはありません。 185 00:10:51,080 --> 00:10:56,040 この技術はサブクラスが 振る舞いを定義することで 186 00:10:56,200 --> 00:10:59,640 スーパークラスが定義した振る舞いを 部分的に変更することを可能にするものです。