はじめに
「C言語でトライ! デザインパターン」
今回はMediatorパターンです。
概要を見ても面白そうでしたが、深堀するうちに思っていた以上によくできた構成だなと感じました。
こういった管理者的な役割のクラスがいると、結合度が低くなるのもどうですが、親のドキュメントだけちゃんと書いてあればそれだけでシステムの理解に繋がったり、仕事の担当分けもしやすかったりと思わぬメリットは多い印象なんですよね。
デザインパターン一覧
作成したライブラリパッケージの説明
公開コードサンプルはこちら, ライブラリパッケージはこちら
その6. Mediatorパターン
wikipegiaより抜粋。
Mediator パターン は、ソフトウェアのデザインパターンの一つで、統一されたインタフェース のセットを提供するパターンである。
中略
通常、プログラムは複数の(時には多数の)クラスからなり、ロジックと計算がクラスに配置される。しかし、プログラムでクラスの数が増えるに従い、特に保守やリファクタリングをする際、クラス間の通信の問題が複雑になり、プログラムを読んだり保守したりすることが困難になる。さらに、他のクラスに影響を与える可能性があるため、変更も難しくなる。Mediator パターンを用いると、オブジェクト間の通信は mediator によってカプセル化され、オブジェクト同士で直接通信せず、mediator を介して行うようになる。これにより通信するオブジェクト同士の依存関係を削減し、結合の度合いを下げることができる。
Managerのような仲介管理者を作って、煩雑になりそうな処理をまとめようって感じですね。
と思いながら色々な方の説明を見ていると何か違和感。
@hikaoさんの記事に書かれているクラス図の例をお借りします。
このクラス図での想定としては、
- ユーザーは
Mediator
を作成 -
Mediator
に利用したいColleague
クラスを追加。この例ではColleague
が親のMediator
を保持します。通知用。 -
Mediator
のconsultation
メソッドにより、各Colleague
は自分の状態を通知する。 -
Mediator
は通知に合わせて必要な状態に各Colleague
を変更する(ここではadvice発行先を変える)
Observer
にちょっと近いけど、こちらのポイントはColleague->Mediatorのコール後の処理はMediator内が判断して行うという点ですね。
画面描画のコントロールや管理モジュールの状態管理等を行うのによさそうですね。
組み込みで言うなら、最小限のやり取りだけなのに依存関係が生まれてしまっているような、煩雑なシステムを整理するのに使えるかも。
###似たようなケース? MVPパターン
UIに強いパターンということで、少ない知識の中から何か似たようなものがないかな~と探ったところ、1つ見つけました。
MVP(Model-View-Presenter)という、表示を司るView, Viewを管理するPresenter, 画面表示の元ネタ取得や操作を行うModelという3つの構成からなる、UI向けのデザインの1つです。Androidに触った際に出会いました。
このViewとPresenterの関係の考え方も、ちょっとMediatorパターンと似ているなと思いました。
UI界隈でよく耳にするMVCはViewが入力、Controllerが出力を行いますが、MVPは違います。画面からの入力はViewから各Viewを管理しているPresenterに転送。PresenterがModelを利用しながらViewの表示/非表示を切り替えます。
Mediator=Presenter, Colleague=Viewの構図に近い気がしませんか?
####AndroidとMVP
Androidのざっくりとした動作はこんな感じです。
- LayoutにViewを配置する。(xmlにレイアウト記載 + コード上でもViewの追加)
- 画面生成時に作られるクラス(Activityクラスと呼ばれます)内の処理で、Viewのタッチイベントを登録する。
- ユーザー操作時にタッチイベントを拾い、画面を切り替えるなどの操作を行う。
Webページと比較するとこんな違いが見受けられますね。
- 画面生成: WebはHTMLが行うのに対し、Androidはxml + Activityクラスが行う。
- ボタン操作等: WebはHTML⇒javascriptで行うのに対し、AndroidはViewクラスに登録されたメソッドで行う。
Androidは入口がクラスになっています。これで何も考えずに実装すると、画面を描写するActivityクラスにViewがわんさか。そのイベントもViewと紐づけるのでActivityクラスに定義。と、Activityクラスが凄いことに。
そこで、入力最初は仕様上しょうがないとして、Viewの管理はPresenter、Presenterの指示のもとViewは画面に集中!
のようなMVPもといMediator
パターンチックなデザインが相性いいのかも。
サンプル
Mediator内で閉じた動作をしたいということで、ライブラリ化は断念。凄い抽象化しまくれば実現出来るかもしれませんが、そんなの意味がない。
ということでサンプルにしました。これも悩みました。
CはUI苦手だし、状態監視が面白いサンプルは構成が複雑だし。。。
振る舞いの変化が表現できるRPGの戦闘AI風プログラムにしました。
Ubuntu 18.04で動作確認。
"mediator"=>戦闘AI的な感じ。メンバーたちを使って攻撃したり、敵からのダメージはmediator_damage
で伝えます。
HPが0になったら離脱。mediator_status_e
の作戦名によってちょっと振る舞いが変わったり
#include "colleague.h"
struct mediator_t;
typedef struct mediator_t *Mediator;
typedef enum mediator_status_e {
GANGAN_IKOZE,
INOCHI_DIJINI,
} mediator_status_e;
Mediator mediator_new(mediator_status_e status);
//colleagueの追加
void mediator_add_colleague(Mediator this, Colleague colleague);
//自身パーティーの攻撃
int mediator_ownturn(Mediator this);
//メンバーのピンチ報告
void mediator_member_warning(Mediator this);
//敵からの攻撃
void mediator_damage(Mediator this, int value);
//メンバーチェック
int mediator_is_member(Mediator this);
void mediator_free(Mediator this);
"colleague"=>キャラクター。JOBによって出来ることが違ったりします。
"mediator"がColleague
のIFを介して、ATTACK, DEFENSE, HEALの指示をしてきます。
typedef enum action_e {
ATTACK,
DEFENSE,
HEAL,
} action_e;
typedef enum job_e {
WARRIOR,
PRIEST,
WITCH,
} job_e;
struct colleague_t;
typedef struct colleague_t *Colleague;
struct colleague_t {
job_e (*getjob)(Colleague this);
int (*gethp)(Colleague this);
int (*getcurhp)(Colleague this);
int (*action)(Colleague this, action_e action);
void (*damage)(Colleague this, int value);
};
colleague
はHPが低くなったらmediator_member_warning
でピンチであることを伝えることがあります。
それによってmediatorが回復指示を出したりします。
コード
一応ここ
ゲーム自体はオート戦闘だし、あほみたいにつまらないです。
感想
デザインは目的があってのものだなと強く感じました。
解決したい問題があってこそ輝くわけだし。
でもすごく自然な発想のいいパターンなので、コードがごちゃごちゃしがちなC言語にも合いそうな気がします。
参考:
Mediatorパターンの参考: 図もお借りしました。
https://qiita.com/hikao/items/71fb454ed9b1164daa5b
他の参考サイト
http://www.techscore.com/tech/DesignPattern/Mediator.html/
http://marupeke296.com/DP_Mediator.html
Android MVPパターンに関する参考サイト
http://konifar.hatenablog.com/entry/2015/04/17/010606
Android MVPパターンに関するわかりやすい説明
https://www.slideshare.net/yudaiyokoyama3/ss-41283302
特にP.15