1 00:00:00,480 --> 00:00:04,600 このビデオでは Pharo の先進的な 側面に注目します。 2 00:00:04,800 --> 00:00:10,800 ライブプログラミングに必要な リフレクティブな操作です。 3 00:00:11,680 --> 00:00:16,680 クラスを再コンパイルする時に 何が起きるのかを見ていきましょう。 4 00:00:17,040 --> 00:00:20,080 そしてライブプログラミングを 実践するために 5 00:00:20,200 --> 00:00:24,720 必要なリフレクティブな操作を 説明します。 6 00:00:24,920 --> 00:00:26,560 ここに典型例があります。 7 00:00:26,720 --> 00:00:30,320 我々は通常はまずクラスを定義して 8 00:00:30,600 --> 00:00:34,640 それからメソッドを追加して そのクラスのインスタンスを作ります。 9 00:00:35,160 --> 00:00:40,400 そしてクラスを再定義して 新しいインスタンス変数を追加します。 10 00:00:41,200 --> 00:00:45,640 では、属性が 1 つ減ったら 11 00:00:46,160 --> 00:00:48,440 既に存在しているインスタンスは どうなるのでしょう? 12 00:00:48,680 --> 00:00:53,920 元のインスタンスを新しいクラスに マイグレーションする 13 00:00:54,280 --> 00:00:57,360 メカニズムが必要です。 14 00:00:57,640 --> 00:00:59,160 それで動き続けます。 15 00:00:59,600 --> 00:01:03,200 クラスの動的な再定義や メソッドの再コンパイルを可能にして 16 00:01:04,440 --> 00:01:09,160 インスタンスを新しいバージョンのクラスに 17 00:01:09,600 --> 00:01:11,040 自動的かつ透過的に 18 00:01:11,400 --> 00:01:15,160 マイグレーションさせるための 19 00:01:15,360 --> 00:01:18,200 操作を見ていきます。 20 00:01:18,520 --> 00:01:22,560 そのためにはまず、特定のクラスの 全てのインスタンスを得る必要があります。 21 00:01:22,840 --> 00:01:28,280 それから、それらのオブジェクトを 指していたポインタを 22 00:01:28,840 --> 00:01:31,920 新しいオブジェクトを指すように 全て置き換えていきます。 23 00:01:32,400 --> 00:01:33,400 いいですか? 24 00:01:34,160 --> 00:01:38,920 まずは、あるクラスの全てのインスタンスを どうやって集めてくるかです。 25 00:01:39,400 --> 00:01:43,560 クラスに allInstances メッセージを送ると 26 00:01:43,800 --> 00:01:47,320 その全てのインスタンスが得られます。 27 00:01:47,640 --> 00:01:51,440 通常のオブジェクトのコレクションです。 28 00:01:51,640 --> 00:01:57,800 Window クラスに送れば Window オブジェクトのコレクションが得られます。 29 00:01:58,280 --> 00:02:02,760 最初の要素を取り出して close メッセージを送ることができます。 30 00:02:02,920 --> 00:02:06,840 close メソッドは Window クラスで 定義されているので。 31 00:02:07,360 --> 00:02:11,840 リフレクティブなプリミティブには pointersTo もあります。 32 00:02:12,160 --> 00:02:16,920 このメッセージをオブジェクトに送ると そのオブジェクトへの参照を持つ 33 00:02:17,400 --> 00:02:21,000 全てのオブジェクトのコレクションを 得ることができます。 34 00:02:21,200 --> 00:02:23,040 オブジェクトを入れ替えたい時には 35 00:02:23,200 --> 00:02:27,320 とても便利です。 36 00:02:29,560 --> 00:02:34,080 ポインタを入れ替えるプリミティブは become: です。 37 00:02:34,600 --> 00:02:39,760 2 つのオブジェクトを 入れ替えることができます。 38 00:02:41,160 --> 00:02:43,840 2 つのオブジェクトを対称的に入れ替えます。 39 00:02:44,200 --> 00:02:48,200 システム中のこのオブジェクトを指す全て 40 00:02:48,720 --> 00:02:51,160 この場合は ポインタが 2 つですが 41 00:02:51,360 --> 00:02:55,840 それらは、このオブジェクトを指すように 置き換えられます。 42 00:02:57,280 --> 00:03:01,720 こちら側のポインタは全て破棄されて 43 00:03:02,280 --> 00:03:04,080 オブジェクトが入れ替えられます。 44 00:03:06,360 --> 00:03:11,680 become: はシステム中のポインタを 対称的に入れ替えるということです。 45 00:03:13,280 --> 00:03:15,080 ここに例題があります。 46 00:03:15,320 --> 00:03:17,600 pt1 は 47 00:03:17,800 --> 00:03:22,320 点オブジェクト 0@0 を指して 変数 pt2 も 48 00:03:22,840 --> 00:03:25,320 その 0@0 オブジェクトを指します。 49 00:03:25,560 --> 00:03:29,560 さらに、変数 pt3 は 点 100@100 を指します。 50 00:03:29,800 --> 00:03:33,200 そして、pt1 become: pt3 と書きます。 51 00:03:34,160 --> 00:03:38,680 変数 pt1 として示されるこのオブジェクト を指していたものは 52 00:03:38,920 --> 00:03:41,440 pt3 を指すようになりました。 53 00:03:42,200 --> 00:03:44,400 つまり、pt1 への全てのポインタが pt3 を指しています。 54 00:03:44,600 --> 00:03:49,760 変数 pt2 は pt1 と同じオブジェクトを 指していましたが 55 00:03:49,920 --> 00:03:54,400 pt3 で参照されていたオブジェクトを 指しています。 56 00:03:54,760 --> 00:04:00,440 pt3 については、対称的なので pt1 が指していたオブジェクトを指します。 57 00:04:00,640 --> 00:04:01,760 一番上のものです。 58 00:04:02,800 --> 00:04:06,080 そして pt1 は pt3 だったものを 指しています。 59 00:04:06,280 --> 00:04:10,000 つまり、ポインタが 対称的に入れ替わりました。 60 00:04:11,200 --> 00:04:14,800 非対称な入れ替えは becomeForward: です。 61 00:04:15,440 --> 00:04:18,400 ポインタの入れ替えをするのですが 62 00:04:18,640 --> 00:04:23,360 このオブジェクトを指していたものは こちらのオブジェクトに置き換わり 63 00:04:24,080 --> 00:04:28,400 逆には働きません。 このオブジェクトへのポインタはそのままです。 64 00:04:31,520 --> 00:04:34,640 別の例があります。 65 00:04:35,280 --> 00:04:39,640 becomeForward: を実行すると 66 00:04:39,920 --> 00:04:45,080 pt1 と pt2 に影響が出ます。 67 00:04:45,600 --> 00:04:48,640 pt3 は becomeForard: の 影響を受けません。 68 00:04:48,840 --> 00:04:53,000 このオブジェクトを指していたものは 変化しません。 69 00:04:55,400 --> 00:04:59,840 また別のリフレクティブなプリミティブに adoptInstance: があります。 70 00:05:00,200 --> 00:05:02,640 これはオブジェクトのクラスを入れ替えます。 71 00:05:02,840 --> 00:05:07,920 引数として渡されたインスタンスを クラスに「引き取る」ように頼みます。 72 00:05:08,560 --> 00:05:13,400 クラスを変更することはとても強力な 道具ですが、制限もあります。 73 00:05:13,640 --> 00:05:17,520 そのオブジェクト 74 00:05:18,160 --> 00:05:19,840 この場合 anInstance ですが このオブジェクトの元のクラスが 75 00:05:20,040 --> 00:05:24,680 新しいクラスのフォーマットと 互換性があることが必須条件です。 76 00:05:25,440 --> 00:05:29,160 どんなオブジェクトも入れ替える というわけにはいきません。 77 00:05:29,360 --> 00:05:31,840 インデックス付き、などなど 78 00:05:32,080 --> 00:05:36,000 オブジェクトのフォーマットは とても重要です。 79 00:05:36,680 --> 00:05:40,640 クラスとは何かというと 80 00:05:40,920 --> 00:05:43,360 クラスは本質的にはフォーマットです。 81 00:05:43,640 --> 00:05:47,200 フォーマットがインスタンス変数の数や 82 00:05:47,400 --> 00:05:52,520 可変領域の型を規定します。 前の講義で説明しましたね。 83 00:05:52,840 --> 00:05:56,000 クラスはスーパークラスや メソッド辞書を持っています。 84 00:05:56,840 --> 00:06:01,680 Behavior クラスがあります。 これは Class クラスのスーパークラスで 85 00:06:02,080 --> 00:06:07,520 クラスの基本的な振る舞いを定義しています。 86 00:06:08,160 --> 00:06:11,000 クラスの最小限の基本機能です。 87 00:06:11,200 --> 00:06:14,720 クラスはスーパークラスと メソッド辞書と 88 00:06:14,840 --> 00:06:18,200 フォーマットを持っています。 89 00:06:19,200 --> 00:06:24,440 ここまで話してきた リフレクティブな振る舞いの機能を 90 00:06:25,000 --> 00:06:27,360 具体的な例を挙げながら 91 00:06:28,440 --> 00:06:33,440 まとめてみましょう。 92 00:06:34,760 --> 00:06:37,800 それに沿ってコードを説明していきます。 93 00:06:38,000 --> 00:06:42,400 これらの 3 行で Behavior クラスのインスタンスを作ります。 94 00:06:42,640 --> 00:06:44,280 いいですか、クラスですよ。 95 00:06:44,640 --> 00:06:50,080 Behavior という名前のクラスの インスタンスを作ります。 96 00:06:51,600 --> 00:06:54,400 これを behavior と呼ぶことにします。 97 00:06:55,040 --> 00:07:00,600 このオブジェクトは Model クラスを 継承するように記述します。 98 00:07:02,000 --> 00:07:03,000 こんな風に。 99 00:07:03,680 --> 00:07:08,080 この behavior オブジェクトの フォーマットを設定します。 100 00:07:08,600 --> 00:07:12,400 そして Model クラスのインスタンスを 生成します。 101 00:07:14,520 --> 00:07:16,360 このオブジェクトを model とします。 102 00:07:17,200 --> 00:07:20,080 ここ、このコードのこの行が重要です。 103 00:07:20,760 --> 00:07:23,920 model オブジェクトのクラスを 104 00:07:24,080 --> 00:07:28,920 引数として渡したクラス つまり behavior に変更します。 105 00:07:29,200 --> 00:07:34,400 それまでのインスタンス関係のリンクを切って このクラスのインスタンスにします。 106 00:07:34,800 --> 00:07:37,400 このクラスは Model クラスのサブクラスです。 107 00:07:38,800 --> 00:07:43,400 さて、ここでこのクラスの 新しいメソッドをコンパイルします。 108 00:07:43,600 --> 00:07:47,400 メソッド foo は 2 を返します。 これをコンパイルします。 109 00:07:48,840 --> 00:07:51,840 behavior オブジェクトで。 110 00:07:53,560 --> 00:07:55,280 見ての通り 111 00:07:55,560 --> 00:07:59,840 ここの model オブジェクトに foo メッセージを 112 00:08:01,000 --> 00:08:02,160 こんな風に送ると 113 00:08:02,760 --> 00:08:04,160 2 が返ってきます。 114 00:08:04,640 --> 00:08:06,720 この部分はメソッド探索についてです。 115 00:08:07,400 --> 00:08:09,040 foo メッセージを送ります。 116 00:08:09,200 --> 00:08:14,600 model オブジェクトはそのクラスつまり behavior オブジェクトで探索します。 117 00:08:14,800 --> 00:08:17,360 そしてメソッドが見つかります。 118 00:08:17,760 --> 00:08:19,160 完璧に動作します。 119 00:08:19,920 --> 00:08:24,080 しかし Model クラスのインスタンスを 120 00:08:25,200 --> 00:08:26,200 このようにして作って 121 00:08:26,560 --> 00:08:28,360 foo メッセージを送っても 122 00:08:29,360 --> 00:08:33,360 MessageNotUnderstood エラーが 発生します。 123 00:08:33,640 --> 00:08:35,800 メソッド探索が 124 00:08:36,080 --> 00:08:39,080 そのオブジェクトのクラス つまり Model クラスから探すからです。 125 00:08:39,320 --> 00:08:43,920 foo メソッドは Model クラスや スーパークラスのメソッド辞書では見つかりません。 126 00:08:45,040 --> 00:08:51,040 この高度なメカニズムを使うと Model クラスの特定のインスタンスにだけ 127 00:08:51,520 --> 00:08:54,040 振る舞いを与えることができます。 128 00:08:54,200 --> 00:08:57,840 わかりやすくするために、このやり方を Set クラスにも適用してみましょう。 129 00:08:58,040 --> 00:09:01,280 Set クラスのインスタンス set1 を 生成します。 130 00:09:01,800 --> 00:09:05,720 Set クラスのメソッド辞書には add: メソッドがあります。 131 00:09:06,400 --> 00:09:11,520 Set クラスの特定のインスタンスにだけ add: メソッドを変更したいとします。 132 00:09:11,720 --> 00:09:15,360 特定のインスタンスに 特定の振る舞いということです。 133 00:09:15,520 --> 00:09:18,400 そこで匿名のクラスを作ります。 134 00:09:18,840 --> 00:09:22,920 Set クラスを継承します。 135 00:09:23,280 --> 00:09:25,200 ここにその継承関係があります。 136 00:09:25,600 --> 00:09:31,160 set2 オブジェクトは この匿名クラスのインスタンスです。 137 00:09:31,720 --> 00:09:37,000 その匿名クラスのメソッド辞書で add: メソッドを 138 00:09:37,320 --> 00:09:40,640 set2 に特定の振る舞いで再定義します。 139 00:09:40,920 --> 00:09:45,800 ここで add: メッセージを set2 オブジェクトに送ります。 140 00:09:46,080 --> 00:09:48,400 すると本来とは異なる別の振る舞いをします。 141 00:09:48,800 --> 00:09:52,320 この set1 にメッセージを送っていたら 142 00:09:53,600 --> 00:09:58,040 このメソッドが選択されていたでしょうが それとは異なる振る舞いをします。 143 00:09:58,280 --> 00:10:03,360 つまり、これら 2 つの集合は add: メッセージに別々の反応をします。 144 00:10:03,680 --> 00:10:07,080 ここに演習問題となるコードがあります。 145 00:10:07,400 --> 00:10:11,280 さきほどと同様に Behavior クラスのインスタンスとして 146 00:10:11,640 --> 00:10:13,800 クラスを作ります。 147 00:10:14,080 --> 00:10:16,840 スーパークラスを Set クラスとします。 148 00:10:17,400 --> 00:10:18,640 フォーマットを設定します。 149 00:10:18,840 --> 00:10:23,600 この匿名クラスで add: メソッドをコンパイルします。 150 00:10:24,080 --> 00:10:29,360 これで Set クラスで定義されている add: メソッドを再定義します。 151 00:10:30,080 --> 00:10:36,080 Transcript show を使って add: メソッドの実行を表示します。 152 00:10:36,400 --> 00:10:40,680 それから super を使って Set クラスでの add: メソッドを実行します。 153 00:10:41,360 --> 00:10:45,720 では、1 つ目の集合を作って テストしてみましょう。 154 00:10:45,920 --> 00:10:48,600 Set クラスはここにあります。 155 00:10:50,280 --> 00:10:54,280 このクラスの最初のインスタンスを生成して set とします。 156 00:10:55,080 --> 00:10:57,040 そのオブジェクトに add: メッセージを送ります。 157 00:10:58,920 --> 00:11:03,760 Set クラスの add: メソッドが 使われます。 158 00:11:05,240 --> 00:11:08,720 ここで set のクラスを尋ねると Set が得られます。 159 00:11:09,400 --> 00:11:13,160 しかし、ここでこのプリミティブを 実行します。 160 00:11:13,600 --> 00:11:16,200 set オブジェクトのクラスを変更して 161 00:11:16,400 --> 00:11:21,160 前の方で Behavior クラスを使って 162 00:11:22,080 --> 00:11:23,760 Set クラスのサブクラスにします。 163 00:11:24,360 --> 00:11:28,400 これでクラスが変更されて add: メソッドの新しいバージョンを持った 164 00:11:28,680 --> 00:11:32,000 logClass のインスタンスになります。 165 00:11:32,280 --> 00:11:37,920 ここで、このオブジェクトに add: 2 メッセージを送ります。 166 00:11:38,360 --> 00:11:43,280 するとこの add: メソッドが選ばれて 167 00:11:43,560 --> 00:11:45,720 このコードが実行されます。 168 00:11:46,280 --> 00:11:48,920 Transcript を見て 169 00:11:49,760 --> 00:11:53,280 このメソッドが実行されたか確認できます。 170 00:11:53,640 --> 00:11:57,400 2 は追加されます。 171 00:11:57,640 --> 00:12:02,040 super を使って Set クラスの add: メソッドを使うからです。 172 00:12:02,840 --> 00:12:07,840 こうして特定の集合を スパイすることができます。 173 00:12:08,080 --> 00:12:09,560 この行を使って。 174 00:12:11,200 --> 00:12:12,400 まとめると 175 00:12:12,600 --> 00:12:19,200 これらのリフレクティブな操作を使って とても画期的なツールを作ることができます。 176 00:12:19,440 --> 00:12:23,840 新しい機能を実装して テストすることができます。 177 00:12:24,080 --> 00:12:29,400 これらのリフレクティブなプリミティブで 言語拡張をすることができます。 178 00:12:30,200 --> 00:12:34,080 ただし、リフレクションを使う上での 黄金律があります。 179 00:12:34,320 --> 00:12:38,800 ドメインコードで不適切な使い方を してはいけない、ということです。 180 00:12:39,040 --> 00:12:44,720 ドメインコードでリフレクティブなコードを 使うことは制限すべきです。 181 00:12:45,080 --> 00:12:49,200 リフレクションを使うと カプセル化を破ることができます。 182 00:12:49,840 --> 00:12:51,000 本当に 183 00:12:51,440 --> 00:12:54,640 オブジェクトプログラミングの規律を 踏み越えることができてしまいます。 184 00:12:54,840 --> 00:12:58,520 したがって、ツール構築に使うに 留めておくべきです。