1.什么是组合模式?
组合模式允许我们将对象组合成树形结构来表现 “整体/部分” 层次结构。 组合能让客户以一致的方式处理个别对象以及对象组合。
2.通过具体实例理解组合模式
如果上一节的迭代器模式是优雅驱动设计的话(不了解的同学强烈推荐先了解下:迭代器模式),那么下面的需求就是业务驱动设计了。
在上一节的系统基础上,我们知道了,主要有以下几个类:
- MenuItem类,菜品类,用来存储菜品的名称,价格,描述等信息。
- DinerMenu类,夜晚餐厅菜单类,用来存储一个MenuItem数组以及相关操作。
- PancakeHouseMenu类,煎饼屋菜单类,用来存储一个ArrayList数组来存储MenuItem以及相关操作。
现在,产品经理接到新需求了,现在客户希望DinerMenu以及PancakeHouseMenu等菜单类拥有添加子菜单的功能。
2.1 员工A的解决方案
员工A了解到这个需求后,自信满满,对产品说:没问题,这个就交给我来做!!
两天后,员工A拿着自己的设计图找到了技术总监,并希望得到技术总监的肯定。
员工A说: 老大,我的这个方案的做法是,首先将菜单类抽象的一些公共操作抽象出来构造一个Menu抽象类,然后让菜单实体类继承它,接着在MenuItem中组合Menu即可,这样就做到了在菜单中再添加子菜单的功能了。
技术总监提问: 那你是如何保证MenuItem中组合的Menu类是动态可变的呢?
员工A回答道:反射,首先,我们规约所有菜单实体类都必须实现一个boolean类型的构造函数,提供构造方式,例如:
DinerMenu类中必须含有下面构造函数
public DinerMenu(boolean son){
menuItems = new MenuItem[MAX_ITEMS];
}
然后再Menu中添加如下方法:
public void addSonMenu(Class<?> cl) throws InstantiationException, IllegalAccessException, InvocationTargetException {
Constructor<?>[] constructors = cl.getConstructors();
for(Constructor<?> constructor:constructors){
if(constructor.getParameters().length==1){
Parameter[] parameters = constructor.getParameters();
if(parameters[0].getType().isAssignableFrom(boolean.class)){
sonMenu = (Menu) constructor.newInstance(true);
}
}
}
}
根据反射来找到具体的目标构造函数进行构造即可。
技术总监接着问道: 这个方案可以试试,你先做出来看看吧!
一天后,员工A拿着自己的实现找到产品:
设计模式/src/main/java/CompositePattern/first/FirstTest.java · 严家豆/设计模式 - 码云 - 开源中国 (gitee.com)
并提供了演示程序:
2.2 组合模式重构代码
在员工A完成代码后,技术总监review时,总觉得虽然完成了需求,但是感觉抽象的不够完全。
于是,他叫来了员工A: 小A, 你听说过组合模式吗? 也许组合模式能让你的代码更简洁,我建议你用组合模式让你的代码更优雅。
员工A收到这个任务后,开始研究组合模式,并尝试做些改变。
员工A接收到任务后,抽象的更彻底,设计了如下做法,将菜单和菜品的动态部分抽象到一个MenuComponent里面,然后通过组合的方式让Menu包含MenuItem或Menu,将如何构建菜单完全交给了用户:
package CompositePattern.second;
/**
* 组合模式允许你将对象组合成树形结构来表现 “整体/部分” 层次结构。
* 组合能让客户以一致的方式处理个别对象及对象组合。
*/
public class MenuTestDrive {
public static void main(String[] args) {
//主菜单
MenuComponent allMenus = new Menu("ALL MENUS","All menus combined");
MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE MENU","Breakfast");
pancakeHouseMenu.add(new MenuItem("土豆西红柿","就是土豆炒西红柿",true,4.77));
MenuComponent dinerMenu = new Menu("DINER MENU","lunch");
dinerMenu.add(new MenuItem("牛肉炒西红柿","西红柿炖牛肉",false,99.99));
MenuComponent cafeMenu = new Menu("CAFE MENU","Dinner");
MenuComponent dessertMenu = new Menu("DESSERT MENU","Dessert of course!");
allMenus.add(pancakeHouseMenu);
allMenus.add(dessertMenu);
allMenus.add(dinerMenu);
allMenus.add(cafeMenu);
allMenus.print();
}
}
运行如图所示:
完整代码路径如下:
设计模式/src/main/java/CompositePattern/second/MenuTestDrive.java · 严家豆/设计模式
下面请根据上面的例子再来理解下组合模式的精髓:组合模式允许我们将对象组合成树形结构来表现 “整体/部分” 层次结构。 组合能让客户以一致的方式处理个别对象以及对象组合。