HeadFirst設計模式(十一) - 組合模式

餐廳需求

    給予以前的迭代模式,餐廳的菜單管理系統須要有煎餅屋菜單和披薩菜單。如今但願在披薩菜單中可以加上一份餐後甜點的子菜單。java

    在迭代模式中,披薩菜單是用數組維護的,咱們須要讓披薩菜單持有一份子菜單,可是不能真的把他賦值給菜單項數組,由於類型不一樣,因此不能這麼作。數組

    因此,須要從新實現煎餅屋菜單和披薩菜單了。事實是,咱們已經到達了一個複雜級別,若是如今不從新設計,就沒法容納將來增長的菜單或子菜單的需求。咱們須要一下改變:安全

  • 須要某種樹形結構,能夠容納菜單、子菜單和菜單項;
  • 須要肯定可以在每一個菜單的各個項之間遊走,並且至少像用迭代器同樣方便;
  • 須要可以更有彈性地在菜單項之間遊走。比方說,可能只須要遍歷甜點菜單,或者能夠便利整個菜單;

    咱們要使用組合模式來解決這個問題,但並無放棄迭代器模式,它仍然是解決方案中的一部分,然而管理菜單的問題已經到了一個迭代器沒法解決的新維度。因此,咱們將倒退幾步,使用組合模式來解決。this

    組合模式讓咱們能用樹形方式建立對象的結構,樹裏面包含了組合以及個別的對象。使用組合結構,咱們能把相同的操做應用在組合的個別對象上,換句話說,在大多數狀況下,咱們能夠忽略對象組合和個別對象之間的差異。spa

組合模式的類圖

    咱們要如何將組合模式利用在菜單上呢?一開始,咱們須要建立一個組件接口來做爲菜單和菜單項的共同接口,讓咱們可以用贊成的作法來處理菜單和菜單項。來看看設計的類圖:.net

    菜單組件MenuComponent提供了一個接口,讓菜單項和菜單共同使用。由於咱們但願可以爲這些方法提供默認的實現,因此咱們在這裏能夠把MenuComponent接口換成一個抽象類。在這個類中,有顯示菜單信息的方法getName()等,還有操縱組件的方法add(),remove(),getChild()等。設計

    菜單項MenuItem覆蓋了顯示菜單信息的方法,而菜單Menu覆蓋了一些對他有意義的方法。code

    具體來看看代碼實現:component

package cn.net.bysoft.composite;

public abstract class MenuComponent {
	
	// add,remove,getchild
	// 把組合方法組織在一塊兒,即新增、刪除和取得菜單組件
	
	public void add(MenuComponent component) {
		throw new UnsupportedOperationException();
	}
	
	public void remove(MenuComponent component) {
		throw new UnsupportedOperationException();
	}
	
	public MenuComponent getChild(int i) {
		throw new UnsupportedOperationException();
	}
	
	// 操做方法:他們被菜單項使用。
	
	public String getName() {
		throw new UnsupportedOperationException();
	}
	
	public String getDescription() {
		throw new UnsupportedOperationException();
	}
	
	public double getPrice() {
		throw new UnsupportedOperationException();
	}
	
	public boolean isVegetarian() {
		throw new UnsupportedOperationException();
	}
	
	public void print() {
		throw new UnsupportedOperationException();
	}
}
package cn.net.bysoft.composite;

public class MenuItem extends MenuComponent {
	String name;
	String description;
	boolean vegetarian;
	double price;

	public MenuItem(String name, String description, boolean vegetarian, double price) {
		this.name = name;
		this.description = description;
		this.vegetarian = vegetarian;
		this.price = price;
	}

	public String getName() {
		return name;
	}

	public String getDescription() {
		return description;
	}

	public boolean isVegetarian() {
		return vegetarian;
	}

	public double getPrice() {
		return price;
	}

	public void print() {
		System.out.println(" " + getName());
		if (isVegetarian()) {
			System.out.println("(V)");
		}
		System.out.println(", " + getPrice());
		System.out.println(" -- " + getDescription());
	}
}
package cn.net.bysoft.composite;

import java.util.ArrayList;
import java.util.Iterator;

public class Menu extends MenuComponent {
	ArrayList<MenuComponent> menuComponents = new ArrayList<MenuComponent>();
	String name;
	String description;
	
	public Menu(String name, String description) {
		this.name = name;
		this.description = description;
	}
	
	public void add(MenuComponent menuComponent) {
		menuComponents.add(menuComponent);
	}
	
	public void remove(MenuComponent menuComponent) {
		menuComponents.remove(menuComponent);
	}
	
	public MenuComponent getChild(int i) {
		return menuComponents.get(i);
	}
	
	public String getName() {
		return name;
	}
	
	public String getDescription() {
		return description;
	}
	
	public void print() {
		System.out.println("\n" + getName());
		System.out.println(", " + getDescription());
		System.out.println("----------------------");
		
		Iterator<MenuComponent> iterator = menuComponents.iterator();
		while(iterator.hasNext()) {
			MenuComponent menuComponent = iterator.next();
			menuComponent.print();
		}
	}
}
package cn.net.bysoft.composite;

public class Waitress {
	MenuComponent allMenus;

	public Waitress(MenuComponent allMenus) {
		this.allMenus = allMenus;
	}

	public void printMenu() {
		allMenus.print();
	}
}
package cn.net.bysoft.composite;

public class Client {

	public static void main(String[] args) {
		// 建立菜單對象
		MenuComponent pancakeHouseMenu = new Menu("煎餅屋菜單", "提供各類煎餅。");
		MenuComponent pizzaHouseMenu = new Menu("披薩屋菜單", "提供各類披薩。");
		MenuComponent cafeMenu = new Menu("咖啡屋菜單", "提供各類咖啡");
		// 建立一個頂層的菜單
		MenuComponent allMenus = new Menu("All Menus", "All menus combined");
		// 把全部菜單都添加到頂層菜單
		allMenus.add(pancakeHouseMenu);
		allMenus.add(pizzaHouseMenu);
		allMenus.add(cafeMenu);
		// 在這裏加入菜單項
		pancakeHouseMenu.add(new MenuItem("蘋果煎餅", "香甜蘋果煎餅", true, 5.99));
		pizzaHouseMenu.add(new MenuItem("至尊披薩", "意大利至尊咖啡", false, 12.89));
		cafeMenu.add(new MenuItem("美式咖啡", "香濃美式咖啡", true, 3.89));
		
		Waitress waitress = new Waitress(allMenus);
		waitress.printMenu();
	}

}

    組合博士以單一責任設計原則換取透明性。經過讓組件的接口同時包含一些管理子節點和葉節點的操做,客戶就能夠將組合和葉節點一視同仁。也就是說,一個元素到底是組合仍是葉節點,對客戶是透明的。對象

    如今,咱們在MenuComponent類中同時具備兩種類型的操做。由於客戶有機會對一個元素作一些不恰當或是沒有意義的操做,因此咱們失去了一些安全性。     以上即是組合模式的一些內容。

相關文章
相關標籤/搜索