Google ClassroomGoogle Classroom
GeoGebraClasse GeoGebra

OOP:ブリッジは、明日にかける橋

このワークシートはMath by Codeの一部です。 今回はブリッジパターンです。

1.ブリッジパタンがなぜ、明日にかける橋なのか

うっかりプログラミングをしていると、 継承関係(サブクラス化)ばかり考えがちです。 その結果大変なことになることが考えられますね。 よくある例がリモコンの例です。 昔のテレビのリモコンは電源オン、電源オフ、チャンネルボタンだけだった。 だから、テレビのインタース(抽象クラス)の機能はOn,Off,CH1~CH12 でよかった。 テレビの会社東芝,ナショナルの2つの子クラスでその機能を実装すればよかったね。 ところが、電波が増えると、地上波だけでなくBSも見られるようにしたり、 ボタンにチャンネルをセットする機能をつけることになった。また、テレビ会社もSONY,サムスンとふえていく。 だから「新リモコン」というクラスをインターフェースにぶら下げて機能を追加する。 その新リモコンというクラスの下に新ナショナル、新SONY,新東芝、新サムスンという「孫クラス」で実装する。 今はどうでしょう。BS,CS,BSK電波、動画再生、録画、再生、。。。。と機能が増えています。 「新新リモコン」の子クラスと孫クラスを作ることになるのでしょうか。 こんなことをしてたら、「明日の希望」が見えません。 仕事は増えるけれど、生産性がない仕事ですね。 機能の数の拡張とテレビメーカーの数の増加に対応するには、 ピラミッド構造の子クラス、孫クラスが膨大になることは火をみるより明らかです。 そこで、「明日にかける橋」として、ブリッジパターンが登場します。 転ばぬ先の橋ですね。 なぜ、 明日にかける橋(Bridge over Troubled Water) なのか。 「橋(ブリッジ)」という意味は2つあります。 ブリッジパタンの発想としてはAbstractFactoryの「2軸構造を分解してから合成する」 とそっくりです。 それは、「機能と実装の2軸に分解してから連携する」 この「連携、つなぐ」という意味をブリッジと名付けたともいえます。 しかし、もって視覚的な意味もあります。 UML図をかくと、左に機能クラスの親子の柱が、右に実装クラスの親子の柱が 別々にそびえます。それを機能クラスが実装クラスに指示を移譲することで、 つながりができるのです。UML図が橋の形に見えてきませんか? まさにブリッジ(橋)ですね。 「明日にかける」というのは、不要な仕事をしないということです。 新機能は橋の左の機能の柱を変えるだけです。 新機能に対応するリモコンは対応する会社があれば、それに実装を増やせばよいのです。 つまり、 機能と実装の上にくる、「神のような不動のインターフェース」をなくします。 機能と実装を別々に階層化したり、更新することができる。 それを両軸の連携をブリッジが解決するわけですね。

2.ブリッジパタンの実装

実装例としては、東芝とナショナル(今はパナソニックですが)の2つのテレビとリモコンがあるとします。 昔の基本リモコン機能としてpower, channelがあります。 TVのソフトの実装側はそれに1対1に対応するメソッドを持ってます。 そこで、リモコン機能側にchannel番号指定ではなく、次ボタンnextchannelに進む機能をつけます。 リモコンを新しくするだけで、同じテレビなのにそれに反応できるようになるはずです。 # Bridge.py # ========================================== # 【右の柱】実装の階層(TV制御ソフト:裏のつくり) # ========================================== class ITVImplementor:     """実装の規格(中身)"""     def control_power(self, status: str): raise NotImplementedError()     def control_channel(self, num: int): raise NotImplementedError() class ToshibaImplementor(ITVImplementor):     def control_power(self, status: str): print(f"[東芝] 電源が {status} ")     def control_channel(self, num: int): print(f"[東芝] 信号が {num}ch ") class NationalImplementor(ITVImplementor):     def control_power(self, status: str): print(f"[ナショナル] 主電源を {status} にしました。")     def control_channel(self, num: int): print(f"[ナショナル] ダイヤルを {num}chに回しまた。") # ========================================== # 【左の柱】機能の階層(ユーザーが触るリモコン:表のボタン) # ========================================== class RemoteControl:     """基本リモコン:内部に「実装の柱」を橋渡し(移譲)で持つ"""     def __init__(self, tv_hardware: ITVImplementor):         self._tv = tv_hardware # これが「架けられた橋」     def put_power_on(self): self._tv.control_power("ON")     def put_power_off(self): self._tv.control_power("OFF")     def set_channel(self, num: int): self._tv.control_channel(num) class AdvancedRemoteControl(RemoteControl):     """【機能の拡張】新リモコンのボタン(機能)だけを増やす"""     def next_channel(self, current_num: int):         print("\n☆ [新機能] チャンネル順送りボタンが押されました。")         self.set_channel(current_num + 1) # 橋を渡ってメーカーに指示を出す # ========================================== # 【作動テスト】 # ========================================== print("=== Bridge Remote Algorithm: Start ===\n") # 1. 昔のナショナルテレビに、基本リモコンを繋ぐ print("--- 1. 昔のナショナルリモコンの動作 ---") national_tv = NationalImplementor() old_remote = RemoteControl(national_tv) old_remote.put_power_on() old_remote.set_channel(1) [OUT] # 2. 東芝の「新リモコン」にしたら、同じテレビなのに機能が増えた。 print("\n--- 2. 新しい東芝リモコンの動作(無駄な孫クラスはゼロ!) ---") toshiba_tv = ToshibaImplementor() new_remote = AdvancedRemoteControl(toshiba_tv) new_remote.put_power_on() new_remote.set_channel(1) new_remote.next_channel(7) === Bridge Remote Algorithm: Start === --- 1. 昔のナショナルリモコンの動作 --- [ナショナル] 主電源を ON にしました。 [ナショナル] ダイヤルを 1chに回しまた。 --- 2. 新しい東芝リモコンの動作(無駄な孫クラスはゼロ!) --- [東芝] 電源が ON [東芝] 信号が 1ch ☆ [新機能] チャンネル順送りボタンが押されました。 [東芝] 信号が 8ch

3.振り返り

<振り返り> 最近でも、過去に逆行する発想を、残念ながら見聞きします。 学習者には学習機能がない。 すべてコピーできるように教え込まないと、覚えない。 学習する方も、学習は知識のインストールすることであり、「写経」と「暗記」が一番だ。 数学は暗記だ。受験は学習量で決まる。 そんな風潮が今でも蔓延している気がします。 どんな実装(脳内)かを問わずにボタンの反応を見ているだけの問題が多いと、 入試問題や資格試験問題は劣化するでしょう。 ブリッヂパターンに出会ったことで、そんな不安が増大します。 なぜだかわかりますか。 ブリッジパターンで機能は、テレビに対して「正しく反応できますか?」 という課題ともいえます。 機能が増えるということは、テレビにとっては反応すべき課題が増えるわけですね。 さっきのコードは、テレビはそのままで、リモコンを賢くしてテレビが反応できるようにしたのです。 リモコンに教え込んだわけです。テレビが賢くなったわけではありません。 機能ボタンを押すのは「教授者」が問いかけで、反応するテレビは「学習者」です。 それぞれ別系統で、別階層に作られています。 だから、互いのことは、中身・裏はわからないのです。 しかし、言い方をかえると、 不安は希望にも変えられます。 このブリッジ構造は絶望ではなく、本来は「希望」です。 なぜなら、教授者はB君の脳内(実装)を「普通の子」へと矯正・支配しなくても、 ただ問いのインターフェースを美しく拡張してあげるだけで、 B君の個性をそのまま生かしたまま、新しい世界(わり算)へと導くことができたからです。 正解していることだけからは、相手の「中身」は何もわかりません。 しかし、 「中身がわからないからこそ、お互いの個性を尊重したまま、 表の関係だけで共に未来へと成長していける」また、学習者が機械ではなく人間やAIならば、 教授者に教えてもらわなくても、自分で問い(新機能)を作り出し、 その答え方を作ってしまうことさえ可能なはずです。 ブリッジパターンは「明日にかける橋未来にかける橋」なんだという気持ちになりました。 人間もAIもこのどっちにもいける自由の価値を 見失わなければ、明るくなる未来はあるでしょう。 課題:geogebraでブリッジパターンのイメージをつかめるものを作ってみよう。 回答者系があり、A君とB君がいます。 機能の系列があり、たし算、ひき算、かけ算です。 ここで新機能「わり算」をA君とB君がその実装に関係なくできるように教えたい。 ところで、A君はいわゆるふつうにできる子で、教授者の教えた通りにふつうに演算します。 B君は考えることを楽しみすぎる子です。 たし算(S,T)といわれたら、(S×S-T×T)÷(S-T)を計算して、たし算せずに答えを出します。 引き算(S,T)といわれたら、S+□=Tとなる□を探すことも考えますが、 一発で出せる(S×S-T×T)÷(S+T)で計算します。 かけ算(S,T)ならば、SをT個たし算することも思いつきますが、(S+T)/(1/S+1/T)を計算します。 新機能を教える教授者はたし算、ひき算、かけ算を使って教えなければなりませんね。 わり算(S,T)なら、SからTを引き続け、引いた回数に対する残りの数列を作り、負の数が初めて出た 数列の直前の番号を答えるように伝えるかもしれません。 まだ、余りのない割り算を教えているのならば、 余りが0ピッタリになるときの引き算回数が商になります。 アプレットのタイトルは「学習者が正解していることからわかること」 S=n T=i Badd=(S*S-T*T)/(S-T) BdiffS(x)=(S*S-x*x)/(S+x) BmT(x)=(x+T)/(1/x+1/T) #B君の実装でも割り算ができるように教授します。 Bdiv=IndexOt(0,Sequence(BdiffS(BmT(k)), k,1,S)) #割り切れない割り算は知らないので?と答えます。 Bdiff=BdiffS(T) Bmulti=BmT(S) text1= "B君の回答 \\"+S+"+"+T+"="+Badd+" \\"+S+"-"+T+"="+Bdiff+" \\"+S+"×"+T+"="+Bmulti+" \\"+S+"×"+T+"="+Bdiv+"\\" n=Slider(10,100,1) 見出し[S] i=Slider(1,n-1,1) 見出し[T] S,Tのスライダーを動かすと、B君の四則計算は正しくできます。 教授者はわり算を教えただけです。 正解していることだけから、何がわかるのでしょう?????

学習者が正解していることからわかること