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类中同时具备两种类型的操做。由于客户有机会对一个元素作一些不恰当或是没有意义的操做,因此咱们失去了一些安全性。     以上即是组合模式的一些内容。

相关文章
相关标签/搜索