デザインパターン入門 | Facade(ファサード)パターン

「 Facade(ファサード)」パターンは、「処理の依頼」を行うことができる「窓口」となるクラスを作り、「窓口」を介してさまざまな処理を行うことができるデザインパターンです。

自分で行うと大変な処理でも「窓口」に依頼するだけで処理を実行することができます。

それでは、「 Facade(ファサード)」パターンの仕組みや作り方について見ていきたいと思います。

「Facade(ファサード)」パターンの構造と事例

今回は、シンプルな構造のパターンを事例に「Facade(ファサード)」パターンについてお話をしていきたいと思います。

今回登場するのは、「商品」「商品の管理者」「商品リスト」を表すクラスです。

お店や工場など、何かの「商品・製品」を扱う事業では、商品情報の「取得・保存・利用」を行うことが必要になりますが、これらの処理を一任する「商品管理者」を任命し、商品を一括管理するというケースを事例に「Facade(ファサード)」パターンについて学んでいきたいと思います。

クラス構造

今回作成するクラスは、下図のように非常にシンプルなものとなっています。

ファサード1

「ProductManager」クラスは、商品に関する「情報の取得・新規登録・検索」などの処理のリクエストを受け付けます。

「ProductManager」クラスの中には「商品情報のリスト」を持っていますが、このリストを表すクラスが「ProductList」クラスです。

そして、一つ一つの商品を表すクラスは「Product」クラスとなります。

一番シンプルな「Product」クラスは、下記のようになります。(プログラム言語はJavaを利用しています。)

public class Product {
	private String name;
	private int price;

	public Product(String name, int price) {
		super();
		this.name = name;
		this.price = price;
	}

	public String getName() {
		return name;
	}

	public int getPrice() {
		return price;
	}

	@Override
	public String toString() {
		return "商品名:"  + this.name + " 価格:" + this.price;
	}
}

「コンストラクタ」と「ゲッター・セッター」「toStringメソッドオーバーライド」のみとなっていますね。

「Product」クラスのインスタンスは、「ProductList」クラスで管理されます。

「ProductList」クラスは、下記のようになっています。

public class ProductList{
	private ArrayList productList = new ArrayList();

	public ArrayList getProducts(){
		return productList;
	}

	public void addProduct(Product pd) {
		productList.add(pd);
	}
}

「ProductList」クラスの中には、「Product」クラスのインスタンスをArrayListを使って管理するためのフィールドが用意されています。

他にも、「ProductList」を取得するためのメソッドや、「ProductList」に商品を登録するためのメソッドが用意されています。

それでは、この「ProductList」クラスを持っているのは誰なのかというと・・・「商品の管理者」です。

商品の管理者がこのリストに商品の情報を追加したり、商品の情報の参照を行ったりします。

この「商品の管理者」を表すクラスが、「ProductManager」クラスです。

「ProductManager」クラスは下記のようになっています。

public class ProductManager {
	private ProductList pl = new ProductList();

	// 「商品リスト」に商品を追加
	public void addProduct(Product pd) {
		pl.addProduct(pd);
	}

	// 「商品名」から商品を検索
	public void searchProduct(String name) {
		ArrayList results = new ArrayList();
		List productList = (List) pl.getProducts();

		for (Product pd : productList) {
			if (pd.getName().contains(name)) {
				results.add(pd);
			}
		}

		if (results.size() == 0) {
			pln("商品はありません。");
		} else {
			List resultsList = (List) results;
			for (Product pd : resultsList) {
				if (pd.getName().contains(name)) {
					pln(pd.toString());
				}
			}
		}
	}

	// 指定予算内の商品ペアを取得
	public void getProductPairWithinBudget(int minPrice, int maxPrice) {
		List productList = (List) pl.getProducts();
		List pairNames = new ArrayList();
		int sumPrice = 0;

		for (Product pd : productList) {
			for (Product pdin : productList) {
				if (!(pd.getName().equals(pdin.getName()))) {
					if (checkSamePair(pairNames, pd.getName(), pdin.getName())) {
						sumPrice = pd.getPrice() + pdin.getPrice();
						displayPairProduct(pairNames, minPrice, maxPrice, sumPrice, pd, pdin);
					}
				}
			}
		}
	}

	// 選択商品ペアの重複チェック
	private boolean checkSamePair(List pairNames, String pdName, String pdinName) {
		if (pairNames.size() != 0) {
			for (String[] names : pairNames) {
				if (pdName.equals(names[1]) && pdinName.equals(names[0])) {
					return false;
				}
			}
		}
		return true;
	}

	// 商品ペアを表示
	private void displayPairProduct(List pairNames, int minPrice, int maxPrice,int sumPrice ,Product pd, Product pdin) {
		if (minPrice <= sumPrice && sumPrice <= maxPrice) {
			pln("■商品ペア =====");
			pln(pd.toString());
			pln(pdin.toString());
			pln("合計:" + sumPrice);
			pln("");

			String[] checkNames = { pd.getName(), pdin.getName() };
			pairNames.add(checkNames);
		}
	}
	
	private void pln(String str) {
		System.out.println(str);
	}
}

さまざまなメソッドが定義されていますが、「ProductList」クラスのインスタンスをフィールドとして持っているのがわかりますね。

「ProductManager」クラスには、さまざまな「窓口」となるメソッドを持っています。

このメソッドの中でも「getProductPairWithinBudget」メソッドは、「ある予算範囲内に納まる商品のペア」を取得する役割を持っています。

例えば、商品の福袋を作る際に「ある予算内に納まる商品のペアを探したい」というケースを想定しています。

後ほど、このメソッドの使用方法について例示していきたいと思います。

「ProductManager」クラスは、下図のように商品に関するさまざまな処理を受け付ける「窓口」となります。

ファサード2

このように「ProductManager」クラスは商品に関する処理を一括して受け取る形になります。

「Facade」パターンの実行

実際に「Facade」パターンを実行するmainメソッドは、

public class Main {
	public static void main(String[] args) {
		// ProductManagerのインスタンスを生成
		ProductManager pm = new ProductManager();

		// ProductManagerに商品登録を依頼
		pm.addProduct(new Product("ネルシャツA", 5800));
		pm.addProduct(new Product("カーディガン", 3300));
		pm.addProduct(new Product("デニムパンツ", 4500));
		pm.addProduct(new Product("ドゥエボットー二シャツ", 2300));
		pm.addProduct(new Product("ソックス2SET", 1200));
		pm.addProduct(new Product("ネルシャツB", 4300));
		pm.addProduct(new Product("ショップコート", 7100));
		pm.addProduct(new Product("テーラードジャケット", 6500));

		// ProductManagerに商品照会を依頼
		pm.searchProduct("ネル");

		// ProductManagerに予算範囲内の商品ペアの探索を依頼
		pm.getProductPairWithinBudget(8000, 10000);
	}
}

まず、「ProductManager」クラスのインスタンスを生成し、次に商品情報の登録を「ProductManager」に依頼しています。

そして、「ネル」というキーワードを含む商品の照会を行っています。

紹介結果は、

【商品の照会結果】
商品名:ネルシャツA 価格:5800
商品名:ネルシャツB 価格:4300

のように結果を取得することができます。

次に「ProductManager」に「8000円~10000円」以内に納まる商品のペアの探索を依頼しています。

探索結果は、

【商品ペアの探索結果】
■商品ペア =====
商品名:ネルシャツA 価格:5800
商品名:カーディガン 価格:3300
合計:9100

■商品ペア =====
商品名:ネルシャツA 価格:5800
商品名:ドゥエボットー二シャツ 価格:2300
合計:8100

■商品ペア =====
商品名:カーディガン 価格:3300
商品名:テーラードジャケット 価格:6500
合計:9800

■商品ペア =====
商品名:デニムパンツ 価格:4500
商品名:ネルシャツB 価格:4300
合計:8800

■商品ペア =====
商品名:ドゥエボットー二シャツ 価格:2300
商品名:ショップコート 価格:7100
合計:9400

■商品ペア =====
商品名:ドゥエボットー二シャツ 価格:2300
商品名:テーラードジャケット 価格:6500
合計:8800

■商品ペア =====
商品名:ソックス2SET 価格:1200
商品名:ショップコート 価格:7100
合計:8300

のように商品ペアを得ることができます。

他にも商品に関するさまざまな処理を「ProductManager」に持たせることで、多様な処理の依頼に対応することができるようになります。

プログラムの「修正・変更」が発生した場合でも「ProductManager」の処理を変更すれば良いため、プログラム完成後のアフターフォローも行いやすくなります。

デザインパターンの中でも比較的理解しやすいパターンだと思いますので、今後作成していくプログラムで使えるところがないかを意識してプログラミングに取り組んでみてください。

HOMEへ