メディエータは、調停という名の一元管理
このワークシートはMath by Codeの一部です。
1.調停という名の一元管理
今回はメディエータ、仲介者パタンだ。
合言葉は、困ったらは仏の相談役に丸投げしよう。
たとえば、A,B,Cが会社の同じ部署の同僚で仲が悪いとする。
Dはとなりの部署にいる人で利害関係はない。
価値観は十人十色だから、相性は様々だ。
たとえば、AさんはBさんの言動でムカつくことがよくある。
そういうときはAさんはCさんに八つ当たりする。
Cさんは鈍感なので怒らないが、気分のむらがありBさんに強くでることがよくある。
BさんはCさんに強く言われると反発して、言い返す。
言い返しても問題は解決しないので、Bさんのストレスはさらにたまる。
そのストレスでBさんの言動は荒くなり、Aさんをカッとさせる。
書くのも嫌になるA,B,Cの人間関係だ。
まとめると、C→B→A→Cの負のストレス連鎖がある。
ここに仏のDさんがいると少しは平和になる。
Dさんはみんなの不満を聞いてあげてもストレスがたまらない。
だから、よけいまわりの人はDに愚痴を言う。
この状況を改善するにはどうしたらよいだろうか。
Dさんが愚痴を聞くだけでなく、場の全体を察知して行動変容を促したらどうだろう。
DがAの相談をうけたら、Bに、AがBの言動で怒ることがあるようだと「伝える」。
DがBとCがもめている場面を見たら、Cが気分しだいの強い口調がBを傷つけたりすることをCに「伝える」。
BにはCに悪気がないけれど、気分屋だからいちいちかっとしない方がよいと「伝える」。
これをそのままプログラミングにするのはあまり意味がないので、ただのイメージの話としよう。
実際は、OOPプログラミングをして、画面にコントロールを貼り付けていくと、
条件分岐が複雑になることがよくある。
コントロールA,B,Cの振る舞いの条件分岐が複雑になりすぎたりする。
チェックボックスAがonならば、ドロップダウンリストBはリストXを表示し、テキスト入力Cは非表示にする。
AがoffならばBは非表示でテキスト入力Cを表示する。
ドロップダウンリストBのリストXで選択されたものがYならば、テキスト入力CにはYが入力されて表示される。
BのリストXで選択されたものがZならば、テキスト入力Cに空欄で入力待ちで表示される。
テキスト入力CにもしPが入力されるとチェックボックスAは非表示となる。
こんな相互依存を2つのオブジェクトの通信結果で書いたり、if文でそれぞれの振る舞いをそれぞれの責任で作ると
とても大変になる。
そこで、A,B,Cの状況をすべてDに通知して、Dが一括して、
A,B,Cにどうふるまえばよいかを指令すればよい。
コントロールたちは互いに会話をせずともことはスムーズに進むようになる可能性が高い。
このように、情報を局所かしてひそひそ解決するのでなく、
情報を大局的に一元的に管理するのがメディエータだね。
メディエータパタンとは、「調停という名の一元管理」。
調停者の手の平の上にのっかる同僚たち、部品たちという感じだね。
2.一元管理を実装しよう。
<調停という名の一元管理を実装しよう>
# ==========================================
# 【クラスMediator】仏のDさんの契約書
# ==========================================
class MediatorContract:
"""手の平に乗せる契約の仕方は、名前のサインと対象イベントだけでOK"""
def consult(self, component_name, event_type, value=None):
raise NotImplementedError()
# ==========================================
# 【クラスControl or Colleague:画面の部品】互いに会話しない部品たち
# ==========================================
class ControlComponent:
"""部品同士は会話しない。分断されている"""
def __init__(self, name: str, mediator: MediatorContract):
self.name = name
self.mediator = mediator
self.visible = True
self.value = None
"""ユーザーの要求が来たら、自分の変化をDさんに伝えるだけ"""
def changed(self, event_type, value=None):
self.value = value
self.mediator.consult(self, event_type, value)
# ==========================================
# 【カスタマイズ】 ユーザが詳細すべてを実装する。
# ==========================================
class BuddhaD(MediatorContract):
def __init__(self):
# 調停する対象のオブジェクトたちを紐づける
self.A = ControlComponent("CheckboxA", self)
self.B = ControlComponent("DropdownB", self)
self.C = ControlComponent("TextInputC", self)
"""【Dは「調停」という名のもとに、A,B,Cを手の平にのせて一元管理する】"""
def consult(self, component, event_type, value=None):
# 1. チェックボックスAが動いたとき
if component.name == "CheckboxA" and event_type == "TOGGLE":
if self.A.value == "ON":
print("[Dさん調停] AさんがONになりましたね。BさんはリストXを表示してください。Cさんは出番待ちです。")
self.B.visible = True
self.B.value = "リストX表示中"
self.C.visible = False
elif self.A.value == "OFF":
print("[Dさん調停] AさんがOFFになりました。Bさんは身を隠し、Cさん(テキスト入力)が表に出てください。")
self.B.visible = False
self.C.visible = True
# 2. ドロップダウンリストBが動いたとき
elif component.name == "DropdownB" and event_type == "SELECT":
if self.B.value == "Y":
print("[Dさん調停] Bさんで'Y'が選ばれました。Cさんの入力欄に'Y'を自動代入します。")
self.C.value = "Y"
elif self.B.value == "Z":
print("[Dさん調停] Bさんで'Z'が選ばれました。Cさんは空欄にして入力待ちにしてください。")
self.C.value = ""
# 3. テキスト入力Cに文字が打ち込まれたとき
elif component.name == "TextInputC" and event_type == "INPUT":
if self.C.value == "P":
print("[Dさん調停] Cさんに特異点'P'が入力されました!大局的判断により、Aさんを非表示にします。")
self.A.visible = False
# 仏のDさん(場)が誕生。手の平にのる部品に略名をつける。
d_san = BuddhaD()
A=d_san.A
B=d_san.B
C=d_san.C
# ここから、仏のDさんの大活躍がスタート!
print("--- 1. ユーザーがチェックボックスAを『ON』にした ---")
A.changed("TOGGLE", "ON")
print(f" -> [その結果] Bの表示:{B.visible}, Cの表示:{C.visible}\n")
print("--- 2. ユーザーがドロップダウンBで『Y』を選択した ---")
B.changed("SELECT", "Y")
print(f" -> [その結果] Cの自動入力値: '{C.value}'\n")
print("--- 3. ユーザーがチェックボックスAを『OFF』にした ---")
A.changed("TOGGLE", "OFF")
print(f" -> [その結果] Bの表示:{B.visible}, Cの表示:{C.visible}\n")
print("--- 4. テキストCに、禁断の文字『P』が入力された ---")
C.changed("INPUT", "P")
print(f" -> [その結果] Aの表示:{A.visible}")
[OUT]
--- 1. ユーザーがチェックボックスAを『ON』にした ---
[Dさん調停] AさんがONになりましたね。BさんはリストXを表示してください。Cさんは出番待ちです。
-> [その結果] Bの表示:True, Cの表示:False
--- 2. ユーザーがドロップダウンBで『Y』を選択した ---
[Dさん調停] Bさんで'Y'が選ばれました。Cさんの入力欄に'Y'を自動代入します。
-> [その結果] Cの自動入力値: 'Y'
--- 3. ユーザーがチェックボックスAを『OFF』にした ---
[Dさん調停] AさんがOFFになりました。Bさんは身を隠し、Cさん(テキスト入力)が表に出てください。
-> [その結果] Bの表示:False, Cの表示:True
--- 4. テキストCに、禁断の文字『P』が入力された ---
[Dさん調停] Cさんに特異点'P'が入力されました!大局的判断により、Aさんを非表示にします。
-> [その結果] Aの表示:False
3.振り返り
コントロール(部品)を画面に貼り付けるときは、
どうしても、1つ1つのコントロールに目がいき、
局所的な視点になりがちだ。
しかし、コントロールが連動することをコントロールの責任にする必要はない。
コントロールはもくもくと働き、ただ、仏様や場に報告すればよい。
コントロールどうしの「会話もなく、喧嘩もない。」
静寂と平和の世界だ。
会話が大事とはいうけれども、どれだけ「大局的な視点での会話」が可能なのだろうか。
小市民的な人間は、自分も含めて、身近な人との会話で一喜一憂する。
もちろん、遠く離れた「有名人の活躍」を自分の活躍のように重ねて喜んだり、
「押し」の活動を応援して楽しんだりする日々も悪くない。
そうでもしないと、
社会の変化が速すぎて、劇的過ぎてついていけないストレスに飲まれかねないからね。
恐ろしい「会話のない労働、仏という名の一元管理」は物だから許される。
自由や会話が大事なはずだけれど、
民主主義や会話や自由が衝突して互いのエネルギーをそぎ落とすこともありうる。
国家だって自由なやりとりが理想だといわれていたが、独裁や障壁や専制の方が
管理がしやすいのだろうか。
そんな疑問を持ってしまうほど、平和で無駄のないシステムだと感じる。
「極端な例を見ると思考や感情が動く」という意味で、
デザインパタンは鏡なんだと思う。
数学の感動と同じ種類の刺激だね。
課題:geogebraで調停という名の一元管理を見るためにはどうしたらよいでしょうか。
画面上に、ユーザーが操作できるコントロールをA,Bの2つ置きます。
Cは状態を表示するコントロールです。
AとBの会話をAとB自体に埋め込まなくても、
仏のDが外から一元管理してます。
AとBは思考も他のコントロールとの会話もしません。
Cは仏Dの操作結果を見ているだけの鏡です。オブザーバかもしれません。
A = Checkbox()
B = Slider(1, 3, 1)
C = "表示中: A="+A+"B="+B
ボタンを貼り付けて、見出しを「仏のDに相談する」
スクリプトを
If(A==true,SetValue(B,2),SetValue(B,1))
If(B==3,setValue(A,false))
にします。
ユーザーへのメッセージを貼ります。
text1="仏Dの行動はこうです。\\
If(A==true,SetValue(B,2),SetValue(B,1))\\
If(B==3,setValue(A,false))\\
上から順に実行されるので、\\
仏でも2番目の願いは聞けません。"
にしてみましょう。
AとBが会話してないのに、DがAの状態からBを変えます。
しかし、上から順に実行しているので、
B=3でAがTrueになっていても、仏に相談するとAをfalseにはできません。
仏様の限界でしょうか?
論理パズルみたくなってきましたね。
オブジェクトの状態が変化するたびに仏様が呼ばれるようにして、
Aの変化ならA変化のスクリプトを実行し、
Bの変化ならB変化のスクリプトを実行するという分岐文にしないといけません。
それが1つのスクリプトで可能になるためには、
Pythonでやったように、オブジェクトの登録が必要になりますね。
つまり、アプリが開いた瞬間に仏様のスクリプトは実行されます。
1.アプリ内のオブジェクトをスキャンしてリスト化します。
2.リスト化したオブジェクトに状態の変更があれば、そのオブジェクトが何かによって反応を変えるようにします。そうすると、上から順に実行するのではなく、変化したオブジェクトに合う条件文だけが
実行されるでしょう。
こうすれば、仏Dのボタンを非表示にしても予想どうりに動くでしょう。
イベントに応じてスクリプトが実行される、イベントドリブンですね。
つまり、この論理パズルのように見えるバグが生じた原因は
geogebraのシステムにあるのではなく、
記述の仕方にあることがわかりますね。
大がかりになるので、実装はしません。
でも、原因と対策がわかるというのは楽しいですね。
興味のある人は、日本語マニュアルやAPI仕様(registerUpdateListener )あたりから
探索してみましょう。