デザインパターン入門 | Factory Method(ファクトリメソッド)パターン
「Factory Method(ファクトリメソッド)」パターンは、インスタンスの生成に「Template Method(テンプレートメソッド)」を利用したパターンです。
インスタンス生成の処理の流れは「親クラス」で決定し、継承先の子クラスで具体的なインスタンスの生成処理などを実装していきます。
「Template Method(テンプレートメソッド)」パターンを学んでいない方は、先に「Template Method(テンプレートメソッド)」の学習を行ってから学んでいきましょう。
→「Template Method(テンプレートメソッド)パターン」
「Factory Method(ファクトリメソッド)」パターンの仕組み
今回作成するクラスの事例は「銀行口座」をテーマにしていきたいと思いますが、クラスの構造は下図のようになります。
抽象クラスが2つと具象クラスが2つ作られていますが、抽象クラスの「Factory」「Product」が「Factory Method(ファクトリメソッド)」パターンの理解に大切なクラスとなります。
この2つの抽象クラスは、
- Factoryクラス
- インスタンス生成の処理の流れを定義したメソッドを持つクラス
- Productクラス
- Factoryクラスから生成されたインスタンスの設計が定義されているクラス
「Factory」は英語で「工場」という意味がありますが、「Product」は「製品・商品」という意味があります。
「工場で製品を生産」した結果作られるものが「製品・商品」です。
これと同じように「Factory」クラスで生成されたインスタンスが「Product」クラスのインスタンスです。
この関係性を忘れるとこれから説明する内容の理解が進まなくなってしまいますので、まず、この関係性をしっかりと覚えておきましょう。
「Factory」クラスの内容は下記のようになります。
package SampleFactoryMethod; public abstract class Factory { /* テンプレートメソッド */ // 製品を作成 public final Product create(String name,String gender) { Product ap = createProduct(name, gender); addProduct(ap); return ap; } /* 抽象メソッド */ // 製品を作成 abstract Product createProduct(String name,String gender); // 製品を追加する abstract void addProduct(Product product); // 現在の登録製品を表示 abstract void displayProducts(); }
このクラスには1つの非抽象メソッドと3つの抽象メソッドが含まれています。
- create
- インスタンスを作成するための「テンプレート」メソッド
- createProduct
- インスタンスの生成方法を定義する抽象メソッド
- addProduct
- 商品を商品リストに追加するための方法を定義する抽象メソッド
- displayProducts
- 追加した商品を表示するための方法を定義する抽象メソッド
この4つのメソッドの中で「Factory Method(ファクトリメソッド)パターン」の「柱」とも言えるメソッドが非抽象メソッドの「create」メソッドです。
このメソッドの中に「インスタンスの生成手順」が書かれていて、「final」宣言されているため、継承先のクラスでオーバーライドすることはできません。
つまりこのメソッドがインスタンス生成の「テンプレート」の役割を担っているわけです。
public final Product create(String name,String gender) { /*「createProduct」メソッドでインスタンスを生成 具体的なインスタンスの生成方法は継承先のクラスで定義 */ Product ap = createProduct(name, gender); /*「addProduct」メソッドで商品を追加 具体的なインスタンスの追加方法は継承先のクラスで定義 */ addProduct(ap); return ap; }
次に実際にこれらのクラスを利用する「AccountFactory」「Account」クラスについて見ていきましょう。
「AccountFactory」クラスは「Account(口座)」を作るクラスです。
現実世界で「口座」を作るのは「銀行・証券会社」などですが、今回作成する対象を「銀行の普通口座」と考えると、「AccountFactory」クラスは「銀行」をイメージしてもらえればと思います。
「Account」クラスは「銀行口座」を表しています。
これらのクラスは、それぞれ抽象クラスの「Factory」「Product」を継承しています。
それでは、「銀行」を表す「AccountFactory」クラスの中身を見てみましょう。
package SampleFactoryMethod; import java.util.ArrayList; import java.util.Iterator; public class AccountFactory extends Factory{ // 作成した口座を管理 private ArrayList accounts = new ArrayList(); // 作成した口座の「口座番号」を管理 private int accountNumber = 1; @Override void addProduct(Product product) { accounts.add((Account)product); accountNumber++; } @Override Product createProduct(String name, String gender) { return new Account(accountNumber, 0, name, gender); } @Override void displayProducts() { Iterator it = accounts.iterator(); while ( it.hasNext() ) { System.out.println((Account) it.next()); } } }
「Factory」クラスを継承し、抽象メソッドの内容を実装して、口座管理用のフィールドを追加しているのがわかりますね。
そして、「口座」を表す「Account」クラスは、下記のようになります。
package SampleFactoryMethod; import java.math.BigDecimal; public class Account extends Product { private int accountNumber; private BigDecimal balance = BigDecimal.valueOf(0); private String name; private String gender; public Account(int accountNumber, int price, String name, String gender) { super("銀行口座"); this.accountNumber = accountNumber; this.name = name; this.gender = gender; } // 口座からお金を引き出す public void withDrawMoney(int withDrawdAmount) { if ((this.balance.subtract(BigDecimal.valueOf(withDrawdAmount))) .compareTo(BigDecimal.valueOf(0)) < 0 ) { System.out.println("預金額が不足しているため、お金を引き出せません。"); } else { this.balance = this.balance.subtract(BigDecimal.valueOf(withDrawdAmount)); } } // 口座へお金を預け入れる public void deposit(int depositAmount) { this.balance = this.balance.add(BigDecimal.valueOf(depositAmount)); } @Override public String toString() { return "口座番号:" + this.accountNumber + " 残高:" + this.balance + " 口座名義人:" + this.name + " 性別:" + this.gender; } }
「Product」クラスを継承し、「口座番号」「残高」口座名義人」「性別」を管理するフィールドを作成し、「預入」「引き出し」といった、口座関連の処理が実装されています。
これらのクラスを実装した「main」メソッドは次のようになります。
package SampleFactoryMethod; public class Main { public static void main(String args[]) { Factory factory = new AccountFactory(); // 口座作成 Account account1 = (Account) factory.create("田中 一郎", "男"); Account account2 = (Account) factory.create("今井 加奈子", "女"); Account account3 = (Account) factory.create("横山 岬", "女"); Account account4 = (Account) factory.create("立岡 純也", "男"); Account account5 = (Account) factory.create("遠山 洋二郎", "男"); // 口座へ預入 account1.deposit(9000); System.out.println("■口座名義人出力1"); // 登録口座情報を表示y factory.displayProducts(); // 口座から引き出し account1.withDrawMoney(2000); System.out.println(""); System.out.println("■口座名義人出力2"); // 登録口座情報を表示y factory.displayProducts(); } }
実行結果は次のようになります。
■口座名義人出力1 口座番号:1 残高:9000 口座名義人:田中 一郎 性別:男 口座番号:2 残高:0 口座名義人:今井 加奈子 性別:女 口座番号:3 残高:0 口座名義人:横山 岬 性別:女 口座番号:4 残高:0 口座名義人:立岡 純也 性別:男 口座番号:5 残高:0 口座名義人:遠山 洋二郎 性別:男 ■口座名義人出力2 口座番号:1 残高:7000 口座名義人:田中 一郎 性別:男 口座番号:2 残高:0 口座名義人:今井 加奈子 性別:女 口座番号:3 残高:0 口座名義人:横山 岬 性別:女 口座番号:4 残高:0 口座名義人:立岡 純也 性別:男 口座番号:5 残高:0 口座名義人:遠山 洋二郎 性別:男
今回は、「銀行」と「口座」がテーマでしたが、このように「何かのインスタンスを作る部分」の処理を抽象化し、処理をテンプレート化できる仕組みが「Factory Method(ファクトリメソッド)」パターンです。
インスタンス生成はアプリケーション開発では欠かせない要素ですので、「Factory Method(ファクトリメソッド)」パターンの活用ができるようになっていきましょう。