javaでのCompositeパターン

Compositeパターンは、入れ物のクラスと中身のクラスを1つの抽象クラスでまとめ、同一視できるようにするパターンです。
このパターンを取り入れることで、クラスを使う側から見て入れ物のクラスなのか中身のクラスなのかを意識する必要がなくなります。

ファイルシステムの例が多いですが、理解を深めるために今回はあえて別の例を出して説明します。
袋と本・ゲームソフトの例を出して説明します。
単に袋の中に本やゲームソフトを入れるだけでなく、袋が破れないように袋を二重にする(袋の中に袋を入れる)こともありますし、袋の中に本用の袋とゲームソフト用の袋を入れて分類することもあります。
このような場合、Compositeパターンを用いることで、袋でも本・ゲームソフトでも同じように処理することができるようになります。

【サンプルコード】

・Entry.java

・Book.java

・GameSoft.java

・Bag.java

・EntryMain.java

【実行結果】


いかがでしたでしょうか。

今回紹介したデザインパターンは、ファイルシステムを扱う時等に役に立つのではないかと思います。
このような再帰的なパターンを自力で思いつくのは難しいと思うので、デザインパターンから学んでおくのが良いと思います。

これからも、役に立つデザインパターンを紹介していこうと思います!

javaでのBridgeパターン

Bridgeパターンは、機能のクラス階層と実装(処理の中身)のクラス階層を橋渡しするパターンです。
機能のクラス階層と実装のクラス階層を分けることで、実装毎に機能を書かなくて良くなりますし、その逆に機能毎に実装を書かなくて良くなります。
結果として、重複した記述を減らすことができ、保守性を向上させることができます。

今回は、RPGのダメージ計算を模したサンプルコードを作成してみました。
機能のクラス階層ではダメージの与え方を定義し、実装のクラス階層では具体的なダメージ計算式を定義しています。
Bridgeパターンの適用により、ダメージの与え方毎にダメージ計算式を書かなくて良くなっている点、逆にダメージ計算式毎にダメージの与え方を書かなくて良くなっている点に注目です。

【サンプルコード】

・DamageCalc.java

・MultipleDamageCalc.java

・DamageCalcImpl.java

・NormalDamageCalcImpl.java

・IgnoreDefenceDamageCalcImpl.java

・DamageCalcMain.java

【実行結果】


いかがでしたでしょうか。

先日記事にさせていただいたBuilderパターンと似たような話ですが、こちらのパターンは、機能や処理の中身が少しずつ違う場合に冗長性を排除できるパターンです。
同じ記述をコピーすると保守性が低下しますので、同じ記述が続いてると思ったらこのパターンの適用を検討した方が良いと思います。

まだ紹介できていないデザインパターンがありますので、これからも記事にしていきたいと思います!

javaでのBuilderパターン

Builderパターンは、処理内容を定義するBuilderクラスと、処理順番を定義するDirectorクラスの2つに分けることで、柔軟に処理を変更できるようにするパターンです。
Builderクラスの定義により処理内容が変わっても都度処理内容を記述する必要が無くなり、Directorクラスの定義により処理順番が変わっても都度処理順番を記述する必要が無くなります。

今回は、RPGのダメージ計算を模したサンプルコードを作成してみました。
ゲームが変わると処理内容が変わるのですが、Builderクラスを定義することで処理内容が変更されても利用者側で処理内容を都度記述する必要が無くなります。
また、ダメージを与える手段によってはダメージが可変になったり固定になったりするのですが、Directorクラスを定義することで利用者側で都度ダメージを可変にしたり固定にしたりするための具体的な記述をする必要が無くなります。

【サンプルコード】

・DamageBuilder.java

・DamageDto.java

・DamageBuilderGameA.java

・DamageBuilderGameB.java

・DamageDirectorVariable.java

・DamageDirectorFixed.java

・DamageCalcMain.java

【実行結果】


いかがでしたでしょうか。

プログラムを作っていると、「処理の順番は同じなのに処理の中身が少しずつ違う…」といったことや「処理の中身は同じなのに処理の順番が少しずつ違う…」といったことがあると思います。私も、そのようなケースでは、共通関数は作りつつも同じ記述を何か所にもコピーせざるを得なくなり、かなり冗長なコードを作ってしまっていました。
Builderパターンは、そのようなケースで効果を発揮します。同じ記述を何か所にもコピーすると保守する時に(テストも含めて)大変なので、中身や順番が少しずつ違うなと思ったら適用することをお勧めします。

次回も、開発の役に立つ情報を発信していきたいと思います!

java:オブジェクトの中身をコピーする方法(cloneメソッド実装)

参照型変数(主に、自分で作成したクラスのオブジェクト)をコピーする場合、単純に「=」で代入するだけでは不十分な場合があります。
参照型変数の中身は参照先(オブジェクトのメモリ領域を示すポインタ)です。
「=」で代入するだけでは、参照先だけがコピーされて、参照しているものは同じという状態になるので、コピー先の変更がコピー元に影響してしまいますし、その逆にコピー元の変更がコピー先に影響してしまいます。

これを避けたい場合は、cloneメソッドを用いて中身を丸ごとコピー(新たにメモリ領域を確保し書き込み)する必要があります。
以下の記述を行うことで、cloneメソッドを使用することができるようになります。

・cloneしたいクラスでCloneableインターフェースを実装する

・Cloneableインターフェースのclone()メソッドをオーバーライドする

・clone()メソッド内で、super.clone()メソッドを使用する

 (super=Objectクラス)

・super.clone()メソッドはObject型で返ってくるので自身の型でキャストを行う

・CloneNotSupportedExceptionが返ってくる可能性があるので例外処理する

以下、サンプルコードです。
参照先のみコピーした場合とcloneで中身をコピーした場合を比較しています。
参照先のみコピーした場合は、コピー後にコピー先を変更した際にコピー元が影響を受けていますが、cloneで中身をコピーした場合は影響を受けていません。

【サンプルコード】

・CloneableItem.java

・ItemCloneMain.java

【実行結果】


いかがでしたでしょうか。

javaでオブジェクトの中身をコピーしようとした場合、言語仕様上の問題により意外と複雑な実装になってしまいます。
そのため、サンプルコードを参考に実装するのがお勧めです。
Webには他の方が書いてくださったサンプルコードが色々とあるので、用途に応じて他のサンプルコードも参考にしてみると良いでしょう。

今回の記事がお役に立てれば幸いです。
これからもjavaに関する記事を書いていこうと思います!

java:Enumによりコード値に意味を持たせ可読性を向上させる

javaのEnum(列挙型)を使用するメリットとしては、一般的に「使用可能な定数を明確化できる」「定数を複数のクラスで使い回せる」といったメリットが挙げられます。
実際に使用していて、コード値に意味を持たせられるというメリットもありそうだったので、記事に残します。

コーディングをしていると、コード値を用いた方が便利な場合が間々あります。
例えば、RPGの道具に対して、「weakSwordにはID0、normalSwordにはID1、strongSwordにはID2を割り振る」といった具合です。
コード値を用いれば、コード値を用いて配列操作することが可能になりますし、DBでマスタ管理する際にも便利です。

しかし、コード値には「人間が理解し辛い」というデメリットがあります。
コンピュータで処理する分には効率が良いのですが、人間から見たら「IDが0ならweakSword、IDが1なら…」といった具合で、コード値が何を意味しているのかを予め知る必要が出てきてしまいます。
コード値とその意味の対応付けを人間が行うことで、読み間違い・書き間違いといったバグに繋がるミスも起こりやすくなります。

そこで、Enumでコード値とその意味の対応付けを行えば、「ソースコード上は意味のある単語、コンピュータ上はコード値」という形にできます。
そうすることで、コード値とその意味の対応付けを人間が行う必要が無くなり、読み間違い・書き間違いを未然に防ぐことができます。

以下、サンプルコードです。

【サンプルコード】

・ItemEnum.java

・ItemManagementMain.java

【実行結果】


いかがでしたでしょうか。

Enumを始めて知った時は何のために使うのか分からなかったのですが、慣れてくると便利だと思うようになりました。
今回は、実際に使って便利だった点を一つ挙げて記事にしてみました。

これからも、役に立つと思ったことがあればどんどん記事にしていきたいと思います!