1 00:00:00,400 --> 00:00:03,560 こんにちは。この講義では リフレクションを見ていきます。 2 00:00:03,720 --> 00:00:08,640 Pharo 自身の中を見て リフレクションの利用法を見ていきましょう。 3 00:00:09,600 --> 00:00:11,880 リフレクションは 4 00:00:12,040 --> 00:00:15,960 大きく 2 つに分類できます。 5 00:00:16,120 --> 00:00:18,000 イントロスペクションは 6 00:00:18,160 --> 00:00:21,240 プログラムが自身の状態を観察する 能力のことです。 7 00:00:21,400 --> 00:00:25,880 インターセッションはプログラムが 自分自身を修正する能力です。 8 00:00:26,040 --> 00:00:29,040 レイフィケーションは 9 00:00:30,640 --> 00:00:34,560 自身の状態や実行を変更するために 10 00:00:34,720 --> 00:00:39,760 通常は暗黙になっているものを 明示化する概念です。 11 00:00:39,920 --> 00:00:43,920 例えば、Pharo では 12 00:00:44,080 --> 00:00:49,400 実行スタックは明示的です。 古典的なオブジェクトとして表現されます。 13 00:00:49,920 --> 00:00:55,280 Pharo ではクラスも 完全に古典的なオブジェクトです。 14 00:00:55,480 --> 00:00:58,960 他の言語には クラスはオブジェクトではないものもあります。 15 00:01:00,040 --> 00:01:02,160 リフレクションのあるシステムでは 16 00:01:02,320 --> 00:01:06,600 それ自身の表現を得ることができます。 17 00:01:06,760 --> 00:01:09,600 それ自身を表現することができ 18 00:01:09,760 --> 00:01:14,840 その表現を使ってそれ自身を変更できます。 19 00:01:15,000 --> 00:01:19,600 表現を変更した時 そのシステムの状態を変更します。 20 00:01:19,760 --> 00:01:22,600 それを因果関係と呼びます。 21 00:01:22,760 --> 00:01:25,520 表現と状態の間の因果関係です。 22 00:01:25,680 --> 00:01:27,720 状態を変えると 23 00:01:27,880 --> 00:01:32,520 システムの状態だけでなく システムの表現も変更されます。 24 00:01:33,000 --> 00:01:37,080 何がうれしいのかと言うと 25 00:01:37,480 --> 00:01:41,160 オブジェクトの中身を見るために 26 00:01:41,320 --> 00:01:43,240 このイントロスペクションと インターセッションを使うのです。 27 00:01:43,400 --> 00:01:48,800 コレクションを 1 つ定義します。 OrderedCollection です。 28 00:01:49,680 --> 00:01:54,680 そして Pharo のインスペクターという 道具を使って 29 00:01:54,840 --> 00:01:57,960 オブジェクトの内側を見ることができます。 30 00:01:58,120 --> 00:02:02,200 OrderedCollection クラスの インスタンスオブジェクトが 31 00:02:02,360 --> 00:02:05,840 ここにありますが 中に、インスタンス変数があります。 32 00:02:06,000 --> 00:02:07,720 array と firstIndex と lastIndex です。 33 00:02:07,880 --> 00:02:10,600 このオブジェクトのインスタンス変数の値は 34 00:02:10,760 --> 00:02:15,400 1 と 12 と そして 12 要素の配列があります。いいですね? 35 00:02:15,560 --> 00:02:19,400 このインスペクターというツールは 36 00:02:19,560 --> 00:02:22,000 一体どうやってオブジェクトの内側を みることができるのでしょう? 37 00:02:22,160 --> 00:02:25,120 オブジェクトの内部状態を どうやって見るのでしょう? 38 00:02:26,600 --> 00:02:31,160 インスペクターはイントロスペクション メソッドを使ってオブジェクトの内側を見ます。 39 00:02:31,320 --> 00:02:34,360 イントロスペクションメソッドは Object で沢山定義されています。 40 00:02:34,520 --> 00:02:37,800 instVarAt: でインスタンス変数を 41 00:02:37,960 --> 00:02:40,960 整数インデックスで読むことができます。 42 00:02:41,120 --> 00:02:44,480 これでこのオブジェクトの インスタンス変数 1 番を見れます。 43 00:02:44,640 --> 00:02:49,600 このオブジェクトのインスタンス変数 1 番に 新しい値を与えて変更することができます。 44 00:02:49,760 --> 00:02:52,160 あるいはインスタンス変数名でアクセスしたり 45 00:02:52,320 --> 00:02:56,000 その値を変えることもできます。 instVarNamed:put: です。いいですね? 46 00:02:58,320 --> 00:03:01,160 ここに例があります。 通常の方法で点を生成して 47 00:03:01,320 --> 00:03:04,880 その点というのは 10@3 ですが 48 00:03:05,040 --> 00:03:07,360 instVarNamed: 'x' として 49 00:03:07,520 --> 00:03:11,760 この点のインスタンス変数 x の値を得ます。 50 00:03:11,920 --> 00:03:14,080 得られる値は 10 です。 51 00:03:14,240 --> 00:03:17,960 そして instVarNamed: 'x' put: 33 で 52 00:03:18,120 --> 00:03:21,840 この点のインスタンス変数 x の値を 53 00:03:22,000 --> 00:03:25,680 10 から 33 に変更します。 54 00:03:25,840 --> 00:03:30,080 イントロスペクションと インターセッションを使って 55 00:03:30,240 --> 00:03:34,120 オブジェクトの内部状態を見てきました。 56 00:03:34,280 --> 00:03:38,200 instVarNamed:put: はインターセッションです。 いいですね? 57 00:03:38,360 --> 00:03:40,800 ここで重要なことは カプセル化が破られているということです。 58 00:03:40,960 --> 00:03:45,560 外部のオブジェクトがこのオブジェクトの 内部状態を変更しました。 59 00:03:45,720 --> 00:03:49,600 これはカプセル化に違反しています。 しかし、とても有用です。 60 00:03:49,760 --> 00:03:52,960 ツールを作ったり、それを使って 開発するのに便利です。 61 00:03:53,120 --> 00:03:56,840 アプリケーションの通常のコードで 使うようなものではありませんが 62 00:03:57,000 --> 00:04:00,160 開発ツールを構築する上では 強烈にパワフルなのです。 63 00:04:00,320 --> 00:04:02,320 コードインスペクターが良い例です。 64 00:04:03,680 --> 00:04:07,360 イントロスペクションの別の例を お見せします。 65 00:04:07,520 --> 00:04:11,240 オブジェクトのクラスを得る class メソッドです。Object で定義されています。 66 00:04:11,400 --> 00:04:14,760 この文字列にクラスを尋ねると String クラスが得られます。 67 00:04:14,920 --> 00:04:17,880 この点にクラスを尋ねると Point クラスが得られます。 68 00:04:18,040 --> 00:04:21,600 Smalltalk class Class クラスのクラスを尋ねることもできます。 69 00:04:21,760 --> 00:04:23,840 などなど 70 00:04:24,000 --> 00:04:26,560 基本的にオブジェクトに class メッセージを送ることで 71 00:04:26,720 --> 00:04:30,880 そのクラスを知ることができます。 72 00:04:31,320 --> 00:04:34,040 システムに問い合わせるメソッドは 沢山あります。 73 00:04:34,200 --> 00:04:38,360 ここで、OrderedCollection に 沢山の問い合わせメッセージを送っています。 74 00:04:38,520 --> 00:04:41,200 全てのスーパークラスを得たり 75 00:04:41,360 --> 00:04:45,520 全てのセレクターを得たり 76 00:04:45,680 --> 00:04:50,680 全てのインスタンス変数名を得たり 77 00:04:50,840 --> 00:04:54,120 全てのサブクラスを得たり 78 00:04:54,280 --> 00:04:56,160 そのコードの全ての行を得るなど… 79 00:04:57,320 --> 00:05:00,920 これらの問い合わせメッセージを使って システムナビゲーションなどの 80 00:05:01,080 --> 00:05:04,840 トップレベルツールが作られています。 81 00:05:05,000 --> 00:05:09,600 システムナビゲーションは システムをブラウズするためのツールです。 82 00:05:09,760 --> 00:05:14,360 例えば , メソッドを実装している 83 00:05:15,440 --> 00:05:18,880 全てのクラスを見ることができます。 84 00:05:19,040 --> 00:05:21,720 こんな風にウィンドウが開いて 85 00:05:21,880 --> 00:05:24,600 , の全てのインプリメンターが得られます。 86 00:05:24,760 --> 00:05:29,200 AbstractFileReference クラスが , メソッドを実装しています。 87 00:05:29,360 --> 00:05:32,280 このメソッドを実装している 全てのクラスのリストが得られます。 88 00:05:33,880 --> 00:05:37,520 もう 1 つ例があります。 89 00:05:38,280 --> 00:05:41,240 メニューやボタンを 90 00:05:41,400 --> 00:05:45,520 クリックすると、背後にあるオブジェクトに メッセージが送られるようにしたいとします。 91 00:05:45,680 --> 00:05:48,680 メッセージ名はボタンの名前を使います。 ここで 92 00:05:48,840 --> 00:05:53,760 文字列をオブジェクトに送るメッセージに 変換するにはどうしたらいいでしょう? 93 00:05:54,560 --> 00:05:59,480 そのためのインターセッションメソッド があります。Object の perform: です。 94 00:05:59,640 --> 00:06:03,320 引数としてシンボルを渡して 送りたいメッセージの名前とします。 95 00:06:03,480 --> 00:06:06,360 するとこのオブジェクトに このメッセージが送られます。 96 00:06:06,520 --> 00:06:10,560 同種のメッセージに perform:with: があります。 97 00:06:10,720 --> 00:06:14,960 実行するメソッド名をシンボルとして与えて 98 00:06:15,120 --> 00:06:16,800 引数を渡します。 99 00:06:16,960 --> 00:06:19,400 例えば 古典的なメッセージ送信での 5 factorial を 100 00:06:19,560 --> 00:06:23,280 リフレクションメッセージで行うためには 101 00:06:23,440 --> 00:06:28,960 5 perform: #factorial とします。 オブジェクト 5 に 102 00:06:29,280 --> 00:06:34,680 #factorial メッセージを実行してください とお願いをしています。 103 00:06:34,880 --> 00:06:39,600 通常のメソッド探索が行われて実行されます。 つまりこれら 2 つは同じです。 104 00:06:40,240 --> 00:06:41,920 もう 1 つ例があります。 105 00:06:42,120 --> 00:06:44,480 コードインスペクタで 106 00:06:44,640 --> 00:06:49,600 OrderedCollection クラスの内側を 見ることができます。 107 00:06:49,760 --> 00:06:54,480 このクラスには属性があります。 108 00:06:54,640 --> 00:06:57,000 例えば、 インスタンス変数 methodDict があります。 109 00:06:57,160 --> 00:07:01,680 OrderedCollection の methodDict は 110 00:07:01,840 --> 00:07:05,960 Behaviour からきています。 この methodDict には 111 00:07:06,120 --> 00:07:10,360 MethodDictionary のインスタンスがきて CompiledMethod を持っています。 112 00:07:10,520 --> 00:07:14,360 そういうわけで、このメソッド辞書に 113 00:07:14,520 --> 00:07:17,360 このコンパイル済みメソッドがあって 別のコンパイル済みメソッドもあり 114 00:07:17,520 --> 00:07:20,360 沢山のコンパイル済みメソッドが あることがわかります。 115 00:07:20,840 --> 00:07:25,480 これらのコンパイル済みメソッドに ソースコードを尋ねることができます。 116 00:07:25,920 --> 00:07:28,280 self getSource とすると 117 00:07:28,440 --> 00:07:30,600 そのコンパイル済みメソッドの ソースコードが得られます。 118 00:07:30,880 --> 00:07:32,200 更に 119 00:07:32,400 --> 00:07:35,800 コンパイル済みメソッドを 直接実行することもできます。 120 00:07:35,960 --> 00:07:39,200 valueWithReceiver:arguments: メッセージを使います。 121 00:07:39,360 --> 00:07:42,200 ただし、注意してください。 この方法はメソッド探索は行われません。 122 00:07:42,360 --> 00:07:45,920 コンパイル済みメソッドがあれば 123 00:07:46,080 --> 00:07:48,600 メッセージを介さずに直接実行できるのです。 124 00:07:48,760 --> 00:07:53,520 ここで、Integer クラスから 125 00:07:53,680 --> 00:07:56,240 コンパイル済みメソッド factorial を得て 126 00:07:56,400 --> 00:07:59,320 それにメッセージを送ります。 valueWithReceiver:arguments: 127 00:07:59,480 --> 00:08:03,200 引数として、レシーバーに 5 を指定して 128 00:08:03,360 --> 00:08:06,320 引数は空です。単項メッセージなので。 129 00:08:06,480 --> 00:08:08,800 すると結果が得られます。 130 00:08:09,760 --> 00:08:14,880 メソッド探索なしにコンパイル済み メソッドを実行しました。 131 00:08:15,240 --> 00:08:18,360 まとめると リフレクションは極めて強力です。 132 00:08:18,560 --> 00:08:22,360 イントロスペクションで 133 00:08:22,840 --> 00:08:25,240 システム全体を見てみました。 システムがそれ自身の中で 134 00:08:25,400 --> 00:08:26,880 オブジェクトとして表現されます。 135 00:08:27,040 --> 00:08:29,440 そしてシステムの状態を変更 することもできます。 136 00:08:29,600 --> 00:08:33,240 これを使って全てのオブジェクトに 汎用的な方法でツールを構築できます。 137 00:08:33,960 --> 00:08:36,400 全てのオブジェクトに共通する 構造を持つプロトコルで 138 00:08:36,560 --> 00:08:40,000 オブジェクトと対話することができます。 139 00:08:40,160 --> 00:08:43,800 ただし、カプセル化に違反していることに 注意してください。 140 00:08:43,960 --> 00:08:48,080 アプリケーションコードに使うものではなく ツール作りに使うためのものです。 141 00:08:48,240 --> 00:08:52,840 コードインスペクターは リフレクションを使って作られています。 142 00:08:53,000 --> 00:08:56,560 また、Pharo でリフレクションが どう実現されているか見ることができます。