vieweditattachhistoryswikistopchangessearchhelp

Chain of Responsibility

GoFデザインパターンのひとつ。



2 さん、GNUE(鵺)さんへ、
上の edit ボタンで編集専用画面に移動する他に、下の入力欄から追記が可能です。文中の最後に --名前 と入れるときに --名前%red などとすると色を変えられます。この文章は最後に --sumim とだけ入っていますが入力時は --sumim%maroon と入れています。--sumim


さてこれで文量を気にせずに、突っ込んだやりとりができそうです(^_^;)。2 さんが「問題」にされていることがどんなことなのかがちょっと、把握しかねています。誰が誰にどんな種類のメッセージを送るのか(送りたくないのか)といった局面を想定しておられるのかを明確にしていただけると、適切な回答が可能かと思います。たとえば、button に送信するが、その後継者達には関係ないメッセージ送信を明示的にしたい、ということでしたら、おっしゃるとおり、button:message() ではなく、button.message() を使えば可能です。

ありがとうございます。--2

SelfやNewtonScriptの継承(委譲?)機構使ったChain of Responsibilityに関する疑問

実質的には問題が無い気もするが。。例が思い付かない。
例: HMDTのボタンってちょっと例が悪いと思いますので、変えてみます。

次のオブジェクトに渡したくないメッセージ、渡したくない場合はどうするのか。

あまりいい例は思い付かないが、とりあえず例。

NewtonScriptの解決法
view.close()とするんだとか。メッセージを受けつけるかをちゃんと調べる時はview.close exists、なのかな?

Selfの解決法
???

一つのオブジェクトに対して二つのChainが繋がっている時はどうするのか。

これも状況そのものがなかなか厳しい物ではありますが。例は思い付きません。^^;

あと、View -> Windowで、Windowがメッセージを受け取った時、selfはViewになるのでしょうか。--2


まず簡単なので、2つ以上のチェインがつながっている場合を論じましょう。

まず訂正しなければいけないのですが、実は Self ではチェインを増やすこと自体は、すでに書いたとおり簡単にできるのですが、同じメッセージをそれぞれのチェインに委譲することはできません。なぜなら、Self の親スロットに連なるチェインには NewtonScript の _proto と _parent にあるような優先順位はないので、そうした状況においてどちらのメソッドを優先して起動すればよいかの判断がシステムにできず、結局、コンパイル時にメソッド(スロット)が継承チェイン中で重複している旨を伝えるエラーが生じてしまいます。従って、_parent チェインしかこの用途には使えない NewtonScript は言うに及ばず、Self であっても2つの後継者(とそれに連なるチェイン)を有する Chain of Responsibility パターンを多重継承システムを活用して実装することは(簡単には)できないことになります。

ということで、な〜んだという結論なのですが、2つ以上のチェインを多重継承機構のみを利用してあつかうこと自体が言語仕様上困難なため、そもそもそこで生じうる問題を想定する必然性があまりない…ということでよろしいでしょうか?--sumim


>view.close()とするんだとか。
これで _parent 継承パスにメッセージを流さないことはできます。

>メッセージを受けつけるかをちゃんと調べる時はview.close exists、なのかな?
しかし、このようにパス中のオブジェクトがメッセージを受け付けるかどうかを判断することはできません。_parent 継承パスにある次のオブジェクトへの委譲は、doesNotUnderstand: のオーバーライドと違って、自動的に行なわれるからです。ただ、送られたメッセージが自分に存在しなければ、そのままスルーしてより上位に位置するオブジェクトの検索も、また、自動で行なわれるので、自分が close メソッドスロットを持っているか、持っていないかは問題にはならないはずです。逆に、起動されては困る close メソッドがあるにもかかわらず、チェインに参加している場合はたしかに問題かもしれません。--sumim

http://bbs.infoseek.co.jp/Board01?user=dentom

重要でないならやめましょう。なんか設計の段階で気をつければいいだけって気もしますし。>2 さん--sumim


そうですね。もともと意地悪な発想からの疑問でしたし、
プロトタイプベースのオブジェクト指向言語には興味が
あるのですが、これからちょくちょく見て行けば十分な
気がします。--2


どこからコメントしていいのかわかりませんが、まず NewtonScript では
button.message();

ということはできません。もしかしたら
call button.message with();

をやりたいのでしょうか。それだったら可能ですがレシーバが nil になってしまい _proto の継承もできなくなってしまうのであまりうれしくないかも。--GNUE(鵺)


NewtonScript でメッセージをうけつけるかどうかのチェックは view.close exists ではなく view:close exists を使います。--GNUE(鵺)

>あと、View -> Windowで、Windowがメッセージを受け取った時、selfはViewになるのでしょうか。--2
継承機構でチェインをたどっている限りは self(レシーバ)は view のままです。なざなら継承なのですから。
逆に継承からはずれるような処理をする場合には当然レシーバが変わってしまいますので注意が必要です。--GNUE(鵺)


>button.message(); ということはできません。
よく調べてみるとたしかにそうです。_parent 継承を無視するには inherited:message() ですね。もっともこれでは button 自身も無視されてしまいますが(^_^;)。

>それだったら可能ですがレシーバが nil になってしまい
トリッキーですが、関数オブジェクトとしてスロットにメソッドを動的に持たせることができれば、関数オブジェクトが環境としてその時点での self、つまり button を保存するので、call with でもいけるかもしれませんね。--sumim


ありがとうございます。けっこうすっきりしてきました。

GNUE(鵺)さん:
>button.message(); ということはできません。
勘違いでしたか。^^;

>継承機構でチェインをたどっている限りは self(レシーバ)は view のままです。なざなら継承なのですから。
>逆に継承からはずれるような処理をする場合には当然レシーバが変わってしまいますので注意が必要です。

これは、
・メッセージが(継承のように)自動的に_parentか_protoに渡される時はレシーバはもとのまま
・メッセージを受け取り、レシーバが作業を行う中でさらに_parentや_protoにメッセージを送る時、レシーバは_parentや_protoになる
ということでしょうか。superに送るメッセージとは違う、と。

sumimさん:
...(中略)...
>ということで、な〜んだという結論なのですが、2つ以上のチェインを多重継承機構のみを利用してあつかうこと自体が言語仕様上困難なため、そもそもそこで生じうる問題を想定する必然性があまりない…ということでよろしいでしょうか?
普通は問題無いのでしょうけど、「あきらめ」といった感じもします。
最近は比較的自分の興味がかたい言語に向いているので、こういう怪しいケースを考えるのかもしれません。--2


2さん:
>_parentや_protoにメッセージを送る時、レシーバは_parentや_protoになるということでしょうか。
>superに送るメッセージとは違う、と。

そのとおりです。他言語の super に相当するものは inherited になります。--GNUE(鵺)


sumimさん:
>よく調べてみるとたしかにそうです。_parent 継承を無視するには inherited:message() ですね。
>もっともこれでは button 自身も無視されてしまいますが(^_^;)。

うーん、そもそも inherited:message() が使えるような状況では既に実行されるメソッドが決まっているわけですから _parent を無視するも何もないような。それともし _proto に実行されるメソッドが存在するのなら _parent 継承よりも _proto 継承が優先されるのでそもそも問題は発生しません。--GNUE(鵺)


2さん:
>普通は問題無いのでしょうけど、「あきらめ」といった感じもします。
「あきらめ」というよりは「割り切り」でしょうか。NewtonScript と Self では多重継承の扱い方が違うので同列に扱うことができませんが、少なくとも NewtonScript では2つ以上のチェインを多重継承機構のみで扱うことには意味がないと思います。無理矢理に多重継承機構で扱うとトリッキーな処理が多くなりシンプルさが失われてしまいます。それよりは他の単一継承OOP のように別途チェインをたどるハンドラーを書いたほうが潔いかと(^^;

>最近は比較的自分の興味がかたい言語に向いているので、こういう怪しいケースを考えるのかもしれません。
かたい言語というのはどういう意味でしょう?--GNUE(鵺)


Self の多重継承、意外と使い方が難しいですね。それと実行中に継承チェインを変更するとコンパイルエラーも発生しないのですよね。実行時にどれが実行されるかどう決定するのでしょうか?--GNUE(鵺)

「メッセージ送信の比較」の NewtonScript サンプルを書くのに「The NewtonScript Programming Language」に目を通していたら ProtoPerform というのを発見。とりあえず ProtoPerform を使えば proto 継承のみを行うメッセージ送信をすることができます。もっとも、個人的にはこんなものは使わなくていいようにオブジェクトやメソッドを配置するようにしますが。--GNUE(鵺)

>結局、コンパイル時にメソッド(スロット)が継承チェイン中で重複している旨を伝えるエラーが生じてしまいます。
>実行時にどれが実行されるかどう決定するのでしょうか?

きちんと調べもしないで脳内環境を使って嘘ばっかりついています(^_^;)。すみません。例外が生じるのはコンパイル時ではなく実行時でした。コンパイル時にはチェックは行なわれず、重複があってもすんなりとおってしまうようです。もちろん、実行中に継承チェインを変更して場合も例外は生じません。したがって、優先度はユーザーが明示的にする必要があります。たとえば、
globals _AddSlotsIfAbsent: (| p1=(). p2=(). c=() |)
p1 _Define: (| slot=(^'p1') |)
p2 _Define: (| slot=(^'p2') |)
c  _Define: (| parent1*=p1. parent2*=p2 |)
このような構造の場合、c slot は例外を生じます。したがって、c parent1 slot もしくは c parent2 slot とメッセージを流すパスを明示的にしてやる必要があります。--sumim


なるほど、Self の多重継承は結構大変ですね。--GNUE(鵺)

>かたい言語というのはどういう意味でしょう?
関数型言語、です。とはいってもまだ初心者レベルです。

でも、結局の所「かたい言語に興味がある」というよりも
もともとあら探しをする癖があるだけの気がしてきました。^^;--2


関数型言語というと LISP, FORTH, APL ... それと Dylan といったとろこでしょうか?
でも、関数型言語って“かたい”んですか?--GNUE(鵺)


「関数型言語」自体何か怪しい表現ですが、かたいと思います。--2
http://ja.wikipedia.org/wiki/%E9%96%A2%E6%95%B0%E5%9E%8B%E8%A8%80%E8%AA%9E


うーん、「関数型言語」と「かたい」のイメージがいまいち結びつきません。
ラムダ計算だから「かたい」ということなんでしょうか。でも、LISP など厳密な意味でのラムダ計算ではないし、型に関しても C などのような静的な言語にくらべて「かたく」はないし、データとプログラムが一体というところも「かたい」とはいえません。どういうところが「かたい」のでしょう?--GNUE(鵺)


全く関係ない話になってしまっていますが。他のページに移行した方がいいでしょうか。--2

あくまで初心者レベルの感想で、間違っているかもしれませんが、よくあるML系の言語では

ML系ですか(うーん、知らない世界だ(^^;)
インターネットで調べたら ML には型推論なんてあるんですね。
変数が変更不能(代入ができない?)というのはラムダ計算でいうところの(本物の)束縛変数しかないということでしょうか。--GNUE(鵺)


>全く関係ない話になってしまっていますが。他のページに移行した方が
いえ。どうぞお気になさらずに。必要ならクタりますから。--sumim


遅レスですが。。

> 変数が変更不能(代入ができない?)というのはラムダ計算でいうところの(本物の)束縛変数しかないということでしょうか。
ラムダ計算も関数型言語もよく知らないのであくまで予想(しつこいか^^;)ですが、
感覚としては数学の関数を定義していくのに近いかもしれません。
ただ実際は大抵の関数型言語は入出力の処理などで手続き的な処理ができるようです。--2


>感覚としては数学の関数を定義していくのに近いかもしれません。
なんかピュアな関数型言語といった感じですね :-) --GNUE(鵺)







GNUE(鵺)さん、
_proto と _parent 継承パスの優先度はたしかにご指摘の通りで私が間違っていました。しかし、それだと GNUE(鵺)さんの話があわなくなります。_parent パスへの検索は _proto パスが行き詰まった暁に行なわれるので、doesNotUnderstand: のオーバーライドによる実装と同じことになるのではないでしょうか? 念のため、doesNotUnderstand: のオーバーライドによる実装は、次のようなものになりますB
!Handler methodsFor: 'request dispatching'!
doseNotUnderstand: aMessage
   "自分が扱えないリクエストは後継者に託す。"
   ^ successor perform: aMessage selector! !

!Handler class methodsFor: 'example'!
example2
   "a HelpButton、a Dialog、an Application は、preview、print、help のいずれかの
   リクエストに応答キることができる。今、a HelpButton → a Dialog → an Application
   というリクエストを委譲するための連鎖があるとき、a HelpButton に preview、print、
   help と順次リクエストして何が起こるかを観察する。"

   "Handler example2"

   | button dialog application |
   button := HelpButton new.
   dialog := Dialog new.
   application := Application new.

   button successor: (dialog successor: application).

   World findATranscript: nil.
   #(preview print help) do: [ :request |
      | handler |
      Transcript cr.
      Transcript show: button printString, ' が ', request, ' リクエストを受け取り…、'.
      handler := button perform: request.
      Transcript show: handler printString, ' が処理しました。']

"出力例:
a HelpButton が preview リクエストを受け取り…、an Application が処理しました。
a HelpButton が print リクエストを受け取り…、a Dialog が処理しました。
a HelpButton が help リクエストを受け取り…、a HelpButton が処理しました。"! !
--sumim


NewtonScript の parent/proto継承は単なる2重ループです。

Newton Formats 1.1 より
func FullLookup(start, name) begin
  local left := start;

  while left <> nil do begin
     local current := left;

     while current <> nil do begin
       if HasSlot(current, name) then
         return GetSlot(current, name); // success

       current := GetSlot(current, '_proto);
     end;

     left := GetSlot(left, '_parent);
  end;

  // failure
end;
--GNUE(鵺)


本当ですね。実装は想像していたのよりシンプルですね。情報、ありがとうございます。Newton Formats は読みでがありそうな文書です。--sumim

「Newton Formats 1.1」、昔は Apple のサイトからでもダウンロードできました(いまもダウンロードできるかは不明)。あと、かなり最後のほうの Newton Developer CD にも入っています。うーん、いま新規に手に入れようと思うとちょっと辛いものがあるけど、たぶんどっかのアーカイブサイトに残っているんじゃないかと思います。

バイドコードの仕様とかパッケージフォーマットとか載っていて参考になるドキュメントです(つうか、いろいろ参考にしています :-))--GNUE(鵺)


あ、いや、入手できています。ありがとうございます。--sumi

このページを編集 (15540 bytes)


Congratulations! 以下の 2 ページから参照されています。

This page has been visited 5855 times.