このワークシートは[url=https://www.geogebra.org/m/twxxx3yq]Math by Code[/url]の一部です。[br][br]今回はブリッジパターンです。[br]
うっかりプログラミングをしていると、[br][b]継承関係(サブクラス化)[/b]ばかり考えがちです。[br]その結果大変なことになることが考えられますね。[br][br]よくある例がリモコンの例です。[br][br]昔のテレビのリモコンは電源オン、電源オフ、チャンネルボタンだけだった。[br]だから、テレビのインタース(抽象クラス)の機能はOn,Off,CH1~CH12 でよかった。[br]テレビの会社東芝,ナショナルの2つの子クラスでその機能を実装すればよかったね。[br][br]ところが、電波が増えると、地上波だけでなくBSも見られるようにしたり、[br]ボタンにチャンネルをセットする機能をつけることになった。また、テレビ会社もSONY,サムスンとふえていく。[br]だから「新リモコン」というクラスをインターフェースにぶら下げて機能を追加する。[br]その新リモコンというクラスの下に新ナショナル、新SONY,新東芝、新サムスンという「孫クラス」で実装する。[br][br]今はどうでしょう。BS,CS,BSK電波、動画再生、録画、再生、。。。。と機能が増えています。[br]「新新リモコン」の子クラスと孫クラスを作ることになるのでしょうか。[br]こんなことをしてたら、「明日の希望」が見えません。[br]仕事は増えるけれど、生産性がない仕事ですね。[br]機能の数の拡張とテレビメーカーの数の増加に対応するには、[br]ピラミッド構造の子クラス、孫クラスが膨大になることは火をみるより明らかです。[br][br]そこで、「明日にかける橋」として、ブリッジパターンが登場します。[br]転ばぬ先の橋ですね。[br][br]なぜ、[br][br][b][size=200][color=#0000ff]明日にかける橋(Bridge over Troubled Water)[br][/color][/size][/b][br]なのか。[br]「橋(ブリッジ)」という意味は2つあります。[br][br]ブリッジパタンの発想としてはAbstractFactoryの「2軸構造を分解してから合成する」[br]とそっくりです。[br]それは、「[b]機能と実装の2軸に分解してから連携する[/b]」[br][br]この「[b][color=#0000ff]連携、つなぐ[/color][/b]」という意味をブリッジと名付けたともいえます。[br][br]しかし、もって視覚的な意味もあります。[br]UML図をかくと、左に[b]機能クラスの親子の柱[/b]が、右に[b]実装クラスの親子の柱[/b]が[br]別々にそびえます。それを[b]機能クラスが実装クラスに指示を移譲する[/b]ことで、[br]つながりができるのです。UML図が橋の形に見えてきませんか?[br][br]まさにブリッジ(橋)ですね。[br][br]「[b][color=#0000ff]明日にかける[/color][/b]」というのは、不要な仕事をしないということです。[br]新機能は橋の左の機能の柱を変えるだけです。[br]新機能に対応するリモコンは対応する会社があれば、それに実装を増やせばよいのです。[br]つまり、[br][color=#0000ff][b][size=150]機能と実装の上にくる、「神のような不動のインターフェース」をなくします。[br][/size][/b][/color][br]機能と実装を別々に階層化したり、更新することができる。[br]それを両軸の連携をブリッジが解決するわけですね。
実装例としては、東芝とナショナル(今はパナソニックですが)の2つのテレビとリモコンがあるとします。[br]昔の基本リモコン機能としてpower, channelがあります。[br]TVのソフトの実装側はそれに1対1に対応するメソッドを持ってます。[br]そこで、リモコン機能側にchannel番号指定ではなく、次ボタンnextchannelに進む機能をつけます。[br]リモコンを新しくするだけで、同じテレビなのにそれに反応できるようになるはずです。[br][br]# Bridge.py[br][color=#0000ff]# ==========================================[br]# 【右の柱】実装の階層(TV制御ソフト:裏のつくり)[br]# ==========================================[br][/color]class ITVImplementor:[br] """実装の規格(中身)"""[br] def control_power(self, status: str): raise NotImplementedError()[br] def control_channel(self, num: int): raise NotImplementedError()[br][br]class ToshibaImplementor(ITVImplementor):[br] def control_power(self, status: str): print(f"[東芝] 電源が {status} ")[br] def control_channel(self, num: int): print(f"[東芝] 信号が {num}ch ")[br][br]class NationalImplementor(ITVImplementor):[br] def control_power(self, status: str): print(f"[ナショナル] 主電源を {status} にしました。")[br] def control_channel(self, num: int): print(f"[ナショナル] ダイヤルを {num}chに回しまた。")[br][br][color=#0000ff]# ==========================================[br]# 【左の柱】機能の階層(ユーザーが触るリモコン:表のボタン)[br]# ==========================================[br][/color]class RemoteControl:[br] """基本リモコン:内部に「実装の柱」を橋渡し(移譲)で持つ"""[br] def __init__(self, tv_hardware: ITVImplementor):[br] self._tv = tv_hardware # これが「架けられた橋」[br] def put_power_on(self): self._tv.control_power("ON")[br] def put_power_off(self): self._tv.control_power("OFF")[br] def set_channel(self, num: int): self._tv.control_channel(num)[br][br]class AdvancedRemoteControl(RemoteControl):[br] """【機能の拡張】新リモコンのボタン(機能)だけを増やす"""[br] def next_channel(self, current_num: int):[br] print("\n☆ [新機能] チャンネル順送りボタンが押されました。")[br] self.set_channel(current_num + 1) # 橋を渡ってメーカーに指示を出す[br][br]# ==========================================[br]# 【作動テスト】[br]# ==========================================[br]print("=== Bridge Remote Algorithm: Start ===\n")[br][br]# 1. 昔のナショナルテレビに、基本リモコンを繋ぐ[br]print("--- 1. 昔のナショナルリモコンの動作 ---")[br]national_tv = NationalImplementor()[br]old_remote = RemoteControl(national_tv)[br]old_remote.put_power_on()[br]old_remote.set_channel(1)[br][OUT][br][br][br]# 2. 東芝の「新リモコン」にしたら、同じテレビなのに機能が増えた。[br]print("\n--- 2. 新しい東芝リモコンの動作(無駄な孫クラスはゼロ!) ---")[br]toshiba_tv = ToshibaImplementor()[br]new_remote = AdvancedRemoteControl(toshiba_tv)[br]new_remote.put_power_on()[br]new_remote.set_channel(1)[br]new_remote.next_channel(7)[br][br]=== Bridge Remote Algorithm: Start ===[br][br]--- 1. 昔のナショナルリモコンの動作 ---[br][ナショナル] 主電源を ON にしました。[br][ナショナル] ダイヤルを 1chに回しまた。[br][br]--- 2. 新しい東芝リモコンの動作(無駄な孫クラスはゼロ!) ---[br][東芝] 電源が ON [br][東芝] 信号が 1ch [br][br]☆ [新機能] チャンネル順送りボタンが押されました。[br][東芝] 信号が 8ch
[b][size=150]<振り返り>[br][/size][/b][br]最近でも、過去に逆行する発想を、残念ながら見聞きします。[br][br]学習者には学習機能がない。[br]すべてコピーできるように教え込まないと、覚えない。[br]学習する方も、学習は知識のインストールすることであり、「写経」と「暗記」が一番だ。[br]数学は暗記だ。受験は学習量で決まる。[br]そんな風潮が今でも蔓延している気がします。[br][b]どんな実装(脳内)かを問わずにボタンの反応を見ているだけの問題[/b]が多いと、[br]入試問題や資格試験問題は[b][color=#ff0000]劣化[/color][/b]するでしょう。[br][br][b][color=#0000ff][size=150]ブリッヂパターンに出会ったことで、そんな不安が増大します。[br][/size][/color][/b][br][b]なぜだかわかりますか。[br][/b][br]ブリッジパターンで機能は、テレビに対して「正しく反応できますか?」[br]という課題ともいえます。[br]機能が増えるということは、テレビにとっては反応すべき課題が増えるわけですね。[br]さっきのコードは、テレビはそのままで、リモコンを賢くしてテレビが反応できるようにしたのです。[br]リモコンに教え込んだわけです。テレビが賢くなったわけではありません。[br][br]機能ボタンを押すのは「教授者」が問いかけで、反応するテレビは「学習者」です。[br][br]それぞれ別系統で、別階層に作られています。[br]だから、互いのことは、中身・裏はわからないのです。[br][br]しかし、言い方をかえると、[br][b][color=#0000ff][size=200]不安は希望にも変えられます。[/size][/color][/b][br][br]このブリッジ構造は絶望ではなく、本来は「希望」です。[br][br]なぜなら、教授者はB君の脳内(実装)を「普通の子」へと矯正・支配しなくても、[br][b]ただ問いのインターフェースを美しく拡張してあげるだけで、[br]B君の個性をそのまま生かしたまま、新しい世界(わり算)へと導くことができた[/b]からです。[br][br]正解していることだけからは、相手の「中身」は何もわかりません。[br]しかし、[br][b]「中身がわからないからこそ、お互いの個性を尊重したまま、[br]表の関係だけで共に未来へと成長していける」[/b]。[br][br][b]また、学習者が機械ではなく人間やAIならば、[br]教授者に教えてもらわなくても、自分で問い(新機能)を作り出し、[br]その答え方を作ってしまうことさえ可能なはずです。[br][/b][br]ブリッジパターンは「[b][color=#0000ff]明日にかける橋[/color]、[color=#0000ff]未来にかける橋[/color][/b]」なんだという気持ちになりました。[br][br][color=#0000ff][b][size=200]人間もAIもこのどっちにもいける自由の価値を[br]見失わなければ、明るくなる未来はあるでしょう。[br][/size][/b][/color][br][br][color=#9900ff][b][u][size=150]課題:geogebraでブリッジパターンのイメージをつかめるものを作ってみよう。[br][/size][/u][/b][/color][br]回答者系があり、A君とB君がいます。[br]機能の系列があり、たし算、ひき算、かけ算です。[br]ここで新機能「わり算」をA君とB君がその実装に関係なくできるように教えたい。[br]ところで、A君はいわゆるふつうにできる子で、教授者の教えた通りにふつうに演算します。[br]B君は考えることを楽しみすぎる子です。[br]たし算(S,T)といわれたら、(S×S-T×T)÷(S-T)を計算して、たし算せずに答えを出します。[br]引き算(S,T)といわれたら、S+□=Tとなる□を探すことも考えますが、[br]一発で出せる(S×S-T×T)÷(S+T)で計算します。[br]かけ算(S,T)ならば、SをT個たし算することも思いつきますが、(S+T)/(1/S+1/T)を計算します。[br][br]新機能を教える教授者はたし算、ひき算、かけ算を使って教えなければなりませんね。[br]わり算(S,T)なら、SからTを引き続け、引いた回数に対する残りの数列を作り、負の数が初めて出た[br]数列の直前の番号を答えるように伝えるかもしれません。[br]まだ、余りのない割り算を教えているのならば、[br]余りが0ピッタリになるときの引き算回数が商になります。[br][br]アプレットのタイトルは「学習者が正解していることからわかること」[br]S=n[br]T=i[br]Badd=(S*S-T*T)/(S-T)[br]BdiffS(x)=(S*S-x*x)/(S+x)[br]BmT(x)=(x+T)/(1/x+1/T)[br]#B君の実装でも割り算ができるように教授します。[br]Bdiv=IndexOt(0,Sequence(BdiffS(BmT(k)), k,1,S)) [b]#割り切れない割り算は知らないので?と答えます。[/b][br]Bdiff=BdiffS(T)[br]Bmulti=BmT(S)[br]text1=[br]"B君の回答[br]\\"+S+"+"+T+"="+Badd+"[br]\\"+S+"-"+T+"="+Bdiff+"[br]\\"+S+"×"+T+"="+Bmulti+"[br]\\"+S+"×"+T+"="+Bdiv+"\\"[br][br]n=Slider(10,100,1) 見出し[S][br]i=Slider(1,n-1,1) 見出し[T][br]S,Tのスライダーを動かすと、B君の四則計算は正しくできます。[br]教授者はわり算を教えただけです。[br][br][size=150][b][color=#0000ff]正解していることだけから、何がわかるのでしょう?????[/color][/b][/size]