组合模式是什么?
组合模式允许你将对象组成树形结构来表现“整体/部分”的层次结构。组合能让客户以一致的方式处理个别对象和对象的组合。
组合模式解决什么问题?
现有菜单项和菜单,Menu通过ArrayList维护MenuItem:
class MenuItem {
private String name;
private Double price;
public MenuItem(String name, Double price) {
this.name = name;
this.price = price;
}
public void print() {
System.out.println("菜名:" + name + " 价格:" + price);
}
}
class Menu {
private ArrayList<MenuItem> menuItems = new ArrayList<>();
private String name;
public Menu(String name) {
this.name = name;
}
public void add(MenuItem menuItem) {
menuItems.add(menuItem);
}
public void remove(MenuItem menuItem) {
menuItems.remove(menuItem);
}
public void print() {
System.out.println("菜单名:" + name);
for (MenuItem menuItem : menuItems) {
menuItem.print();
}
}
}
现有两家店的菜单,一家卖主食,一家卖甜点:
Menu mainMenu = new Menu("mainMenu");
mainMenu.add(new MenuItem("鱼香肉丝", 15.0));
mainMenu.add(new MenuItem("西红柿炒鸡蛋", 15.0));
mainMenu.print();
Menu dessertMenu = new Menu("dessertMenu");
dessertMenu.add(new MenuItem("布丁", 10.0));
dessertMenu.add(new MenuItem("雪糕", 10.0));
dessertMenu.print();
现两家店合并,要求把甜点的菜单加入到主食菜单中,并实现统一遍历。
即mainMenu中不仅要有MenuItem还得有dessertMenu
实现1
在Menu内部维护Menu对象的ArrayList数组,创建方法addMenu()和removeMenu(),通过print()方法打印:
class Menu {
private ArrayList<MenuItem> menuItems = new ArrayList<>();
private String name;
private ArrayList<Menu> innerMenus = new ArrayList<>();
public Menu(String name) {
this.name = name;
}
public void addItem(MenuItem menuItem) {
menuItems.add(menuItem);
}
public void removeItem(MenuItem menuItem) {
menuItems.remove(menuItem);
}
public void addMenu(Menu menu) {
innerMenus.add(menu);
}
public void removeMenu(Menu menu) {
innerMenus.remove(menu);
}
public void print() {
System.out.println("菜单名:" + name);
for (MenuItem menuItem : menuItems) {
menuItem.print();
}
for (Menu menu : innerMenus) {
menu.print();
}
}
}
- problem1:需要额外的内存开销,且方法都是类似的
- problem2:无法让Menu和MenuItem统一遍历
组合模式实现
抽象组件
MenuComponent里面方法都抛异常,让子类自行选择实现:
abstract class MenuComponent {
public void add(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public void remove(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public void print() {
throw new UnsupportedOperationException();
}
}
组件实例
让MenuItem和Menu都继承MenuComponent,MenuItem重写print()打印菜名,Menu重写add()、remove()、print()完成对MenuComponent 的增删查:
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 print() {
System.out.println("菜名:" + name + " 价格:" + price);
}
}
class Menu extends MenuComponent {
private ArrayList<MenuComponent> menuComponents = new ArrayList<>();
private String name;
public Menu(String name) {
this.name = name;
}
@Override
public void add(MenuComponent menuComponent) {
menuComponents.add(menuComponent);
}
@Override
public void remove(MenuComponent menuComponent) {
menuComponents.remove(menuComponent);
}
@Override
public void print() {
System.out.println("菜单名:" + name);
Iterator<MenuComponent> iterator = menuComponents.iterator();
while (iterator.hasNext()) {
MenuComponent menuComponent= iterator.next();
menuComponent.print();
}
}
}
组合过程
可看到mainMenu可添加MenuItem,也可添加Menu,最后通过print()打印出所有菜单和菜单项:
MenuComponent dessertMenu = new Menu("subMenu");
dessertMenu.add(new MenuItem("布丁", 15.0));
dessertMenu.add(new MenuItem("雪糕", 10.0));
MenuComponent mainMenu = new Menu("MainMenu");
mainMenu.add(new MenuItem("鱼香肉丝", 15.0));
mainMenu.add(new MenuItem("西红柿炒鸡蛋", 15.0));
mainMenu.add(dessertMenu);
mainMenu.print();