デザインパターンMediatorについて勉強した
mediatorパターンとは
mediatorは仲介人の意味です。
あっちの人とそっちの人が、さらにこっちの人が自分勝手にお互いに指示を出し合っていては混乱が起こります。
それでは全員の考えをまとめて最適な指示を出すリーダーを一人用意しようというのがこのmediatorパターンです。
主にGUIを使ったプログラムに用いられるパターンのようです。
今回は簡単なパズルゲームを作ってみます。
8*8のパネルが並んでおり、そのうち一つのパネルをクリックするとクリックしたパネルとその4方のパネルがon/offでき、全てのパネルをonにするとクリアというあのゲームです。
コード例
Colleagueインタフェース
public interface Colleague { void setMediator(Mediator mediator); void setPosition(int x,int y); void setSelected(boolean select); boolean getSelected(); }
「あの人」や「この人」を表すインタフェースです。
mediatorパターンとして必要なのはsetMediatorメソッドただ一つです。
setMediatorメソッドは仲介人を保持するためのメソッドで、自身に変化があった場合は何も考えずにとりあえずmediatorに自身の変化を連絡します。
mediator側は連絡を受けると全体の状態を考慮して、残りのメソッド(ここではsetPosition、setSelected、getSelected)を駆使して各Colleagueに指示を出します。
setPositionメソッドはこの例固有のメソッドです。
パネルが8*8ありますが、自身がどこに位置しているのかを教えてもらうためのセッターです。
setSelectedメソッドはこの例固有のメソッドです。
クリックされた周囲のパネルはon/offを切り替える必要がありますが、このon/offを指示するのがこのメソッドです。
getSelectedメソッドはこの例固有のメソッドです。
現在のon/off状態を返します。
Mediatorインタフェース
public interface Mediator { void operating(int x,int y); }
仲介役を表すインタフェースです。
Colleagueは何か変化があった場合、「何か変化がありました」と仲介役に連絡する必要があります。
このメソッドはその連絡用です。
引数は自身の位置、つまり右上のパネルなのか中央のパネルなのかの情報です。
この引数もこの例固有のもので、引数がいらない場合や文字列を引数とする場合、operating2,operating3のように複数種類の連絡用メソッドを作ることもできます。
実装
Cellクラス
public class Cell extends JLabel implements Colleague { private Mediator mediator ; private boolean select; private int x; private int y; public Cell(Mediator mediator,int x,int y) { setMediator(mediator); setPosition(x,y); addMouseListener( new MouseListener() { @Override public void mouseClicked(MouseEvent e) { Cell.this.mediator.operating(x,y); } @Override public void mousePressed(MouseEvent e) {} @Override public void mouseReleased(MouseEvent e) {} @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} }); setPreferredSize(new Dimension(50,50)); setHorizontalTextPosition(JLabel.CENTER); setSelected(false); } @Override public void setMediator(Mediator mediator) { this.mediator = mediator; } @Override public void setSelected(boolean select) { this.select = select; if(select) { setBackground(Color.WHITE); setText("O"); }else { setBackground(Color.LIGHT_GRAY); setText("X"); } } @Override public boolean getSelected() { return select; } @Override public void setPosition(int x, int y) { this.x = x; this.y = y; } }
Colleagueインタフェースを実装したクラスです。
medoatorフィールドは仲介役を保持、selectフィールドは現在のon/off状態を、x,yフィールドは自身のパネル座標をそれぞれ保持しておきます。
コンストラクタではMediatorと座標を受け取り、保持しています。
またパネルがクリックされたという情報が欲しいので、MouseListenerからクリックイベントを受け取っています。
パネルのサイズ、文字の位置、そしてデフォルトのon/off状態を設定しています。
setMediatorメソッドではmediatorを保持しています。
setSelectedメソッドではon/off状態を保持します。
それと同時に背景色とO/Xを表示します。
getSelectedメソッドは現在のon/off状態を返します。
setPositionメソッドはこのパネル自身の位置を保持します。
GamePanelクラス
public class GamePanel extends JPanel implements Mediator { private Colleague[][] cells ; private int width = 8; private int height = 8; public GamePanel() { setLayout(new GridLayout(width,height)); cells = new Colleague[width][height]; for(int i=0;i<cells.length;i++) { for(int j=0;j<cells[i].length;j++) { Cell cell = new Cell(this,i,j); cells[i][j] = cell; add(cell); } } } @Override public void operating(int x,int y) { turnover(x,y); turnover(x-1,y); turnover(x+1,y); turnover(x,y-1); turnover(x,y+1); } private void turnover(int x,int y) { if(x<0 || width<=x) return; if(y<0 || height<=y) return; cells[x][y].setSelected(!cells[x][y].getSelected()); } }
仲介役です。
仲介役はそれぞれのColleague達の状態を見てそれぞれに何をすべきかを指示していきます。
cellsフィールドはColleague達を保持するフィールドです。
while,heightフィールドはパネルが8*8であることを設定します。
コンストラクタでは8*8個のパネルを配置していきます。
operatingメソッドはColleagueから受ける連絡用メソッドです。
このメソッドが呼ばれると、引数の座標を中心に、4方向のパネルのon/offを反転します。
実際に反転できるか(パネルの範囲外かどうか)をチェックするのはturnoverメソッドの仕事です。
実行
実際に動かしてみましょう。
とは言ってもまだウィンドウを作っていないので、まずはそこからです。
DefaultFrameクラス
public class DefaultFrame extends JFrame implements WindowListener{ public DefaultFrame() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); addWindowListener(this); } @Override public void windowOpened(WindowEvent e) { pack(); setLocationRelativeTo(null); } @Override public void windowClosing(WindowEvent e) {} @Override public void windowClosed(WindowEvent e) {} @Override public void windowIconified(WindowEvent e) {} @Override public void windowDeiconified(WindowEvent e) {} @Override public void windowActivated(WindowEvent e) {} @Override public void windowDeactivated(WindowEvent e) {} }
簡単なコードなので説明は省略します。
public class Main { public Main() { JFrame frame = new DefaultFrame(); frame.add(new GamePanel()); frame.setVisible(true); } public static void main(String[] args) { new Main(); } }
特徴
主にGUIなどで、あちらこちらかでマウスやキーボードによる外部入力があるときに活躍するパターンです。
さらに複雑なプログラムだと仲介役の仲介役などが出てきたりしそうですね。