1 00:00:00,720 --> 00:00:05,880 こんにちは このビデオでは Pharo の重要な機能である 2 00:00:06,080 --> 00:00:07,920 doesNotUnderstand: フックを説明します。 3 00:00:08,080 --> 00:00:13,400 もうデバッガーウィンドウで このメッセージを見ましたよね。 4 00:00:13,920 --> 00:00:16,840 それはどこから来て 何をするものでしょう? 5 00:00:17,280 --> 00:00:20,080 例を見てみましょう。 6 00:00:20,400 --> 00:00:22,280 オブジェクト node1 があります。 7 00:00:22,640 --> 00:00:27,520 このオブジェクトに coucou: stef メッセージを送ってみます。 8 00:00:28,280 --> 00:00:31,280 node1 から Object クラスまで 9 00:00:31,840 --> 00:00:33,920 メソッド探索していきます。 10 00:00:34,200 --> 00:00:37,720 coucou: メソッドは このクラスでは見つかりません。 11 00:00:37,920 --> 00:00:42,320 そこでスーパークラスへ行きます。 しかし、ここにもありません。 12 00:00:42,720 --> 00:00:45,520 仮想的に探索しても見つからないので 13 00:00:45,800 --> 00:00:51,040 そのメッセージを具体化します。 具体化(レイフィケーション)については 14 00:00:51,200 --> 00:00:56,440 イントロスペクションとリフレクション のビデオで議論しました。 15 00:00:56,920 --> 00:01:02,640 具体化とは、暗黙的な概念を オブジェクトとして表現することです。 16 00:01:02,920 --> 00:01:04,520 ここにメッセージがあります。 17 00:01:04,920 --> 00:01:07,920 このメッセージを表現する オブジェクトを生成します。 18 00:01:08,080 --> 00:01:10,400 Messageクラスのインスタンスです。 19 00:01:10,680 --> 00:01:15,000 仮想システムはオブジェクト node1 に 20 00:01:15,200 --> 00:01:17,320 メッセージを再送します。 21 00:01:17,760 --> 00:01:22,800 doesNotUnderstand: メッセージを さきほどのオブジェクトを引数として送ります。 22 00:01:22,920 --> 00:01:27,000 これでメッセージ探索を新たに実行します。 23 00:01:27,200 --> 00:01:32,440 doesNotUnderstand: メソッドは Node クラスでは見つかりません。 24 00:01:32,720 --> 00:01:37,640 そこでスーパークラスを辿って探します。 25 00:01:37,840 --> 00:01:40,920 DNU(doesNotUnderstand:)メソッド が見つかります。 26 00:01:41,160 --> 00:01:44,440 これでそのメソッドを実行できます。 27 00:01:47,080 --> 00:01:49,920 doesNotUnderstand: は 28 00:01:50,200 --> 00:01:53,840 メッセージが失敗した時に 29 00:01:54,280 --> 00:01:56,280 システムがあなたに送るメッセージです。 30 00:01:57,080 --> 00:02:00,680 どんなクラスでもこのメソッドを再定義して 31 00:02:01,280 --> 00:02:06,800 メッセージを理解できなかった時の 特殊な振る舞いを定義できます。 32 00:02:07,320 --> 00:02:09,840 このメソッドを使って色々な機能を 構築することができる 33 00:02:10,320 --> 00:02:13,800 とても重要な道具です。 34 00:02:14,080 --> 00:02:17,440 自動的な移譲(デリゲーション)や 35 00:02:17,720 --> 00:02:20,520 分散プログラミングなどに有用です。 36 00:02:21,400 --> 00:02:25,080 ここで doesNotUnderstand: の 利用例をいくつか見てみましょう。 37 00:02:25,720 --> 00:02:30,400 受け取った全てのメッセージを 何か別のオブジェクトに転送したいとします。 38 00:02:31,640 --> 00:02:37,200 単純な移譲としては メッセージを転送する対象を 39 00:02:37,400 --> 00:02:39,320 格納するオブジェクトを作ります。 40 00:02:39,520 --> 00:02:42,200 doesNotUnderstand: メソッドを 再定義します。 41 00:02:42,400 --> 00:02:45,560 引数として aMessage オブジェクト が渡されて 42 00:02:45,760 --> 00:02:49,280 その中に失敗したメッセージの セレクターが格納されています。 43 00:02:49,680 --> 00:02:53,400 そこでこのメッセージに 再送するようお願いします。 44 00:02:53,760 --> 00:02:59,920 sendTo: self target とすると 45 00:03:00,520 --> 00:03:03,320 別のオブジェクトにそのメッセージが 再送されます。 46 00:03:04,320 --> 00:03:07,400 コードの可読性を壊してしまうような 47 00:03:07,920 --> 00:03:12,400 強力な機能なので、注意してください。 48 00:03:12,760 --> 00:03:15,160 ここに説明している通りです。 49 00:03:16,000 --> 00:03:20,360 このメッセージが最終的に誰が受け取るか このコードに書いてあります。 50 00:03:20,920 --> 00:03:23,680 ツールや高度な仕組みを実現する上で 51 00:03:24,080 --> 00:03:28,080 とても有用な機能です。 52 00:03:29,720 --> 00:03:33,720 もう 1 つの例として ログ取りプロキシを見てみましょう。 53 00:03:33,920 --> 00:03:36,320 どんなものかというと 54 00:03:36,560 --> 00:03:40,400 メソッドを極少数しか持たない 小さなオブジェクトを作って 55 00:03:40,600 --> 00:03:44,360 doesNotUnderstand: メソッドを 変更します。 56 00:03:44,800 --> 00:03:48,840 そしてドメインオブジェクトの代わりに 57 00:03:49,400 --> 00:03:53,920 このプロキシオブジェクトを become: で忍び込ませます。 58 00:03:54,840 --> 00:03:58,840 まず、プロキシオブジェクトを作ります。 59 00:03:59,360 --> 00:04:02,840 プロキシオブジェクトに 元オブジェクトを与えます。 60 00:04:03,080 --> 00:04:06,560 元オブジェクトというのは このプロキシで置き換える 61 00:04:07,400 --> 00:04:08,560 対象のオブジェクトです。 62 00:04:08,840 --> 00:04:14,720 invocationCount をインクリメントして メッセージを数えます。 63 00:04:15,400 --> 00:04:20,760 まだメッセージを受け取っていないので カウンタを 0 に初期化します。 64 00:04:20,920 --> 00:04:25,400 そして元オブジェクトを与えます。 この self は 後で入れ替わります。 65 00:04:26,520 --> 00:04:29,760 ここでこのプロキシの DNU メソッドを再定義します。 66 00:04:29,920 --> 00:04:33,360 理解できないメッセージを 受け取るたびに 67 00:04:33,560 --> 00:04:38,840 それがトランスクリプトに表示され カウンタをインクリメントしていきます。 68 00:04:39,200 --> 00:04:43,400 そしてそのメッセージを 元オブジェクトに転送します。 69 00:04:44,080 --> 00:04:47,600 前の例と同様 メッセージを別のオブジェクトに再送します。 70 00:04:48,800 --> 00:04:52,920 Messageクラスが実装している sendTo: の代わりに、見ての通り単に 71 00:04:53,080 --> 00:04:57,400 perform:withArguments: メソッドを 使います。 72 00:04:57,640 --> 00:05:01,440 もうこれについては前の講義で 説明しました。 73 00:05:02,920 --> 00:05:05,160 例を見てみましょう。 74 00:05:06,200 --> 00:05:09,520 このログ取りプロキシの使い方は 75 00:05:10,000 --> 00:05:13,280 まず point オブジェクトを生成して 76 00:05:14,040 --> 00:05:16,680 それから become: して 77 00:05:17,040 --> 00:05:20,200 この point オブジェクトを指している すべてのものを 78 00:05:20,680 --> 00:05:24,280 ログ取りプロキシを生成して 79 00:05:25,520 --> 00:05:27,320 それにすり替えます。 80 00:05:27,760 --> 00:05:30,760 point オブジェクトにメッセージを送ると 81 00:05:30,920 --> 00:05:36,600 become: したので実際には ログ取りプロキシになっていて 82 00:05:36,840 --> 00:05:39,200 メッセージを送るたびに 83 00:05:39,840 --> 00:05:42,000 トランスクリプトに表示して 84 00:05:42,720 --> 00:05:45,320 カウンタがインクリメントされます。 85 00:05:46,000 --> 00:05:48,800 doesNotUnderstand: に書いた通りです。 86 00:05:49,040 --> 00:05:53,200 最後にカウンタは 3 になります。 87 00:05:54,440 --> 00:05:57,840 最後の例で見た通り このプロキシフレームワークには 88 00:05:58,000 --> 00:06:00,200 制限があります。 89 00:06:00,520 --> 00:06:06,440 例えば、そのオブジェクトが self に 送るメッセージを捕まえることはできません。 90 00:06:06,640 --> 00:06:07,840 これはちょっとトリッキーです。 91 00:06:08,040 --> 00:06:14,160 クラスには become: は使えません。 リフレクションには制限があります。 92 00:06:14,720 --> 00:06:18,720 プロキシと元オブジェクトが 同じメッセージを受け付ける場合にも 93 00:06:18,920 --> 00:06:21,400 リスクがあります。 94 00:06:21,760 --> 00:06:24,600 プロキシにメッセージを送った時に 95 00:06:24,840 --> 00:06:28,360 そのまま応答してしまい DNU で捕まえて元オブジェクトに 96 00:06:28,560 --> 00:06:31,520 転送することができません。 97 00:06:32,280 --> 00:06:37,000 Pharo には全てトラップすることができる プロキシフレームワークが別にあります。 98 00:06:37,160 --> 00:06:42,200 しかし、そういったものは この簡単な実装例よりも複雑です。 99 00:06:42,800 --> 00:06:46,760 この技術の他の応用例としては 100 00:06:47,080 --> 00:06:53,280 アクセサやメソッドを動的に生成する というものがあります。 101 00:06:53,840 --> 00:06:58,400 ここに、再定義した doesNotUnderstand: メソッドがあります。 102 00:06:58,800 --> 00:07:01,200 メッセージを受け付けると 103 00:07:01,400 --> 00:07:06,200 対応するインスタンス変数がないか確認して 104 00:07:06,400 --> 00:07:11,200 もしあれば、新しいメソッドを生成して compile: を使って 105 00:07:11,560 --> 00:07:14,040 そのクラスの新しいメソッドとします。 106 00:07:16,360 --> 00:07:19,600 これは変数の値を返します。 107 00:07:19,760 --> 00:07:23,600 ここで変数の リードアクセサを生成しています。 108 00:07:24,080 --> 00:07:28,560 もしメッセージにマッチする 109 00:07:28,920 --> 00:07:32,840 インスタンス変数がない場合には super doesNotUnderstand: を送ります。 110 00:07:33,400 --> 00:07:38,400 これを使って 実際に呼ばれたかどうかに従って 111 00:07:38,920 --> 00:07:41,840 自動的にリードアクセサを定義できます。 112 00:07:42,760 --> 00:07:45,280 まとめると 113 00:07:45,400 --> 00:07:47,920 小さなオブジェクトの使い方を 見てきました。 114 00:07:48,080 --> 00:07:51,640 Object クラスのインスタンスではなく 115 00:07:51,840 --> 00:07:54,040 protoObject クラスのインスタンスです。 116 00:07:54,200 --> 00:07:56,920 プロキシの基盤として使うことができます。 117 00:07:57,200 --> 00:08:01,400 doesNotUnderstand: を再定義することで 118 00:08:01,920 --> 00:08:07,800 メッセージの失敗を捕捉して 再送することができます。 119 00:08:08,320 --> 00:08:13,840 この強力なフックは Pharo の多くのツールの基盤になっています。 120 00:08:14,560 --> 00:08:18,000 しかし、非常に注意深く使ってください。 121 00:08:18,280 --> 00:08:21,760 本当に必要な時だけ使ってください。 122 00:08:22,080 --> 00:08:27,320 ドメインコードには使わないように。 とても高度な技術です。