asaのブログ

プログラミングの勉強まとめ

デザインパターンまとめ2(お絵かきソフト)

 前回からの続きです。下の参考書の22章でお絵かきソフトを扱っていたので詳しくやります。自分自身絵を描いているのと、pixivSketchが素晴らしかったので、こうした情報を参考にお絵かきアプリを作っていきます。

 

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

 

  

 Commandパターン

 ある処理を行ったときに、処理の結果はオブジェクトの状態に反映されます。ただ、どういう処理をしたかという履歴は残ることがありません。Commandパターンは、処理つまり命令をクラスとして扱い、インスタンスを保存することで、仕事の履歴を管理します。仕事の履歴を管理することで、同じ命令を実行するときや、複数の命令をまとめたものを新たな命令として再利用することができます。

 

 構成

 パッケージを”命令”を扱うcommandパッケージ、描画を行う"draw"パッケージ、実行用のMainを置く無名のパッケージに分けて作ります。

 

 Commandパッケージ

 このパッケージでは”命令”を扱うインタフェースとクラスを保存します。漠然とした命令をCommandインターフェースで扱い、まとまった複数の命令をMacroCommandクラスで表現します。

 

 Commandインターフェース 

gist1320acacecfc08a99cb14beed949cca2

 Commandインターフェースでは、単に命令を実行するという命令を表現するにとどめます。具体的な処理は実装クラスに記述します。

 

 MacroCommandクラス

gist2385e6ad569a3d483479be567ed92c20

 命令の集合をコレクションフレームワークのStackで表現しています。Dequeでやる場合はpushとpollになります。

 

 MacroCommandクラスは、Commandインタフェースを実装しているので定義されたメソッド(execute)をオーバーライドします。executeメソッドでは、Iteratorインターフェースを使って繰り返し処理を行っています。nextメソッドの戻り値はObject型なのでCommandでキャストした後で、executeメソッドを呼び出して命令を実行しています。

 

 続くappend、undo、clearはStackクラスのメソッドをそのまま利用しています。clearはVectorクラスからの継承されたメソッドで、全ての要素を削除します。

 

 Drawerパッケージ

 commandパッケージでは、命令と命令の集合、あと必要なメソッドを定義して終わりました。Drawerパッケージでは描画を表現します。

 

 DrawCommandクラス

gist382a7ccf6b63928231c1871b9bc00fe4

 このクラスは、Commandインターフェースを実装しています。漠然とした命令から、描画するものを描く命令へと降りています。Commndインタフェースを実装しているので、MacroCommandクラスへオブジェクトを渡すことが出来ます。

 

 Drawableは後で記載するDrawableインタフェースのことです。実際にはDrawableインタフェースを実装したクラスのインスタンスが渡されます。Pointはxとyの2値を格納するクラスです。描画場所を表しています。

 

 コンストラクタに先の2つのインスタンスを渡して初期化します。また、Commandクラスのexecuteをオーバーライドして、Drawab leクラスを実装したクラスのインスタンスからdrawメソッドを呼び出しています。

 

 Drawableインターフェース

gist62ca63355f0899f62281f751fe032e79

 何かを描くという処理を表したインターフェースです。抽象メソッドのdrawが定義されています。これを実装したクラスに具体的処理を任せます。

 

 DrawCanvasクラス  

gistd543b97f863f2b261677640c909705f5

 Canvasをextendsして、Drawableを実装したクラスです。フィールドで色、描画する点の半径、処理の履歴を持たせています。コンストラクタには描画全体の高さと幅、処理の履歴を渡して初期化します。

 

 Canvasを拡張したのは、paintメソッドをオーバーライドするためで、このメソッドはGraphicsインスタンスを引数に、履歴に保存された命令を実行します。

 

 また、Drawableインターフェースを実装しているので、drawメソッドをオーバーライドしています。Graphicsインスタンスを作成するのに、このCanvasコンポーネントを利用しています。あえて書くとthis.getGraphics()でしょうか。

 

 後はGraphicsクラスのメソッドを利用して色と描画範囲を指定しています。fillOvalは1、2番目の引数が描画する位置を表し、残りの引数が描画する大きさを表しています。

 

 Mainクラス

gist76a43fcae23469dbef1a6ad64466f769

 実行用のクラスです。なんだか一番複雑に感じました。

 JavaGUIを実現する場合、AWTとSwing、JavaFXがあります。今はJavaFXがあるので、あえてSwingで実現させる必要はないのかもしれませんが、参考書どおり行きます。

 

 Swingでは大元になるフレームにぺたぺたと部品を貼り付けていくことでGUIを構成していきます。Frameから部品を貼り付けるためには、getContentPaneで部品を載せるコンテナを取得します。

 

 今回そのコンテナに貼り付けるのは、mainBox、ボタンを置くためのButtonBox、そして描画を行うためのcanvasです。ButtonBoxはBoxLayoutを横軸にすることで、横にボタンを置くことができます。またmainBoxは縦軸に設定して縦に部品を置くようにしています。

 

 フィールドでは、履歴と領域、消去ボタンを持たせています。コンストラクタにはtitleを渡し、スーパークラスであるJFrameのコンストラクタを呼び出しています。イベント発生時のために、それぞれのコンポーネントに対応するリスナーを登録します。レイアウトの設定は上の通りです。

 

 消去ボタンが押された際には、イベントがactionPerformedメソッドに渡され、履歴が消去され、canvasが再描画されます。またマウスがドラッグされた場合には、イベントはmouseDraggedメソッドに渡り、命令が作成、履歴登録されてすぐに実行されています。後はウィンドウが閉じられると実行終了するためにexitだけ実装されています。

 

 空のメソッドはそれぞれ、リスナーを実装したためにオーバーライドする必要があるので書かれています。

 

 後はmainで実行すると、簡単なお絵かきソフトが起動します。