組合模式容許你將對象合成樹形結構來表現「總體/部分」層次結構。組合能讓客戶以一致的方式處理組合對象以及個體對象。ide
要點:this
也可使用組件接口。code
組件中有些方法可能不適合某種對象,此時咱們能夠拋異常或者提供特定實現。component
/** * 組件抽象類 */ public abstract class Component { /** * 子組件(能夠是組合或葉節點) */ protected List<Component> childs = new ArrayList<Component>(); /** * 添加子組件 */ public void addChild(Component component) { childs.add(component); } /** * 移除子組件 */ public void removeChild(Component component) { childs.remove(component); } /** * 獲取全部子組件 */ public List<Component> getChilds() { return childs; } public String getName() { // 默認拋異常,由子類決定要不要覆蓋 throw new UnsupportedOperationException(); } }
組合能夠包含其餘組合,也能夠包含葉節點。對象
/** * 組合 */ public class Composite extends Component { private String name; public Composite(String name) { this.name = name; } @Override public String getName() { return name; } }
葉節點沒法添加、刪除、獲取子節點,所以須要對相應的方法進行特殊處理。blog
/** * 葉節點 */ public class Leaf extends Component { private String name; public Leaf(String name) { this.name = name; } @Override public void addChild(Component component) { // 葉節點不能添加子節點,能夠拋異常或者空實現 throw new UnsupportedOperationException(); } @Override public void removeChild(Component component) { // 葉節點沒有子節點可移除,能夠拋異常或者空實現 throw new UnsupportedOperationException(); } @Override public List<Component> getChilds() { // 葉節點沒有子節點,能夠拋異常或者返回空集合 throw new UnsupportedOperationException(); } @Override public String getName() { return name; } }
因爲組合及葉節點都實現了組件接口,所以可使用組件的方法來操做組合及葉節點。繼承
public class Test { public static void main(String[] args) { // 組合 Component composite1 = new Composite("composite1"); Component composite2 = new Composite("composite2"); Component composite3 = new Composite("composite3"); // 葉節點 Component leaf1 = new Leaf("leaf1"); Component leaf2 = new Leaf("leaf2"); Component leaf3 = new Leaf("leaf3"); Component leaf4 = new Leaf("leaf4"); // 組合1包含組合二、3 composite1.addChild(composite2); composite1.addChild(composite3); // 組合2包含葉節點一、2 composite2.addChild(leaf1); composite2.addChild(leaf2); // 組合3包含葉節點三、4 composite3.addChild(leaf3); composite3.addChild(leaf4); // 打印組件名稱 System.out.println(composite1.getName()); for (Component child : composite1.getChilds()) { System.out.println(" " + child.getName()); for (Component leaf : child.getChilds()) { System.out.println(" " + leaf.getName()); } } } }
對象村餐廳和對象村煎餅屋合併了,它們合併後的新公司建立了一個 Java 版本的女招待。這個 Java 版本的女招待可以打印使用 ArrayList 存儲的餐廳菜單和煎餅屋菜單。接口
如今它們打算加上一份餐後甜點的「子菜單」。也就是說,這個 Java 版本的女招待不只要支持打印多個菜單,還要支持打印菜單中的菜單。rem
因爲菜單可能包含菜單和菜單項,所以咱們能夠建立一個樹形結構,這個樹形結構由菜單和菜單項組成。get
經過將菜單和菜單項放在相同的結構中,咱們既能夠把整個結構視爲一個「大菜單」,也能夠把這個結構的任一部分視爲一個「子菜單」,包括菜單項也能夠視爲一個「我自己就是菜單項所以不必再包含菜單項的菜單」。這樣,咱們就能夠以一致的方式來處理菜單和菜單項了。
/** * 菜單組件抽象類 */ public abstract class MenuComponent { /** * 子組件(能夠是菜單或菜單項) */ protected List<MenuComponent> childs = new ArrayList<MenuComponent>(); /** * 添加子組件 */ public void addChild(MenuComponent component) { childs.add(component); } /** * 移除子組件 */ public void removeChild(MenuComponent component) { childs.remove(component); } /** * 獲取全部子組件 */ public List<MenuComponent> getChilds() { return childs; } public String getName() { throw new UnsupportedOperationException(); } public double getPrice() { throw new UnsupportedOperationException(); } public void print() { throw new UnsupportedOperationException(); } }
/** * 菜單(組合) */ public class Menu extends MenuComponent { private String name; public Menu(String name) { this.name = name; } @Override public String getName() { return name; } @Override public void print() { System.out.println("\n" + getName());; System.out.println("---------------------");; } }
/** * 菜單項(葉節點) */ public class MenuItem extends MenuComponent { private String name; private double price; public MenuItem(String name, double price) { this.name = name; this.price = price; } @Override public void addChild(MenuComponent component) { throw new UnsupportedOperationException(); } @Override public void removeChild(MenuComponent component) { throw new UnsupportedOperationException(); } @Override public List<MenuComponent> getChilds() { return childs; } @Override public String getName() { return name; } @Override public double getPrice() { return price; } @Override public void print() { System.out.println(" " + getName() + ", " + getPrice());; } }
/** * 女招待 */ public class Waitress { MenuComponent allMenus; public Waitress(MenuComponent allMenus) { this.allMenus = allMenus; } public void printMenu() { print(allMenus); } private void print(MenuComponent menuComponent) { menuComponent.print(); for (MenuComponent child : menuComponent.getChilds()) { print(child); } } }
public class Test { public static void main(String[] args) { // 全部菜單 MenuComponent allMenus = new Menu("ALL MENUS"); // 子菜單 MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE MENU"); MenuComponent dinerMenu = new Menu("DINER MENU"); MenuComponent dessertMenu = new Menu("DESSERT MENU"); // 添加煎餅屋菜單及菜單項 allMenus.addChild(pancakeHouseMenu); pancakeHouseMenu.addChild(new MenuItem("Regular Pancake Breakfast", 2.99)); pancakeHouseMenu.addChild(new MenuItem("Blueberry Pancakes", 3.49)); pancakeHouseMenu.addChild(new MenuItem("Waffles", 3.59)); // 添加餐廳菜單及菜單項 allMenus.addChild(dinerMenu); dinerMenu.addChild(new MenuItem("BLT", 2.99)); dinerMenu.addChild(new MenuItem("Soup of the day", 3.29)); dinerMenu.addChild(new MenuItem("Hotdog", 3.05)); // 添加甜點菜單及菜單項 dinerMenu.addChild(dessertMenu); dessertMenu.addChild(new MenuItem("Apple Pie", 1.59)); dessertMenu.addChild(new MenuItem("Cheesecake", 1.99)); dessertMenu.addChild(new MenuItem("Sorbet", 1.89)); // 使用女招待打印菜單 Waitress waitress = new Waitress(allMenus); waitress.printMenu(); } }