《设计模式》设计模式的基本原则
《设计模式》单例模式
《设计模式》工厂模式
《设计模式》原型模式
《设计模式》建造者模式
《设计模式》适配器模式
《设计模式》桥接模式
《设计模式》装饰者模式
《设计模式》组合模式
《设计模式》外观模式
《设计模式》享元模式
《设计模式》代理模式
《设计模式》模板方法模式
《设计模式》命令模式
定义:
组合模式主要包含三种角色:
组合模式的注意事项:
组合模式的原理类图如下所示:
现在有一个需求,针对一个管理系统的菜单,打印出其包含的所有菜单以及菜单项(菜单
项是指不再包含其他内容的菜单条目)的名称,使用组合模式来实现这个案例。
类MenuComponent
/**
* 菜单组件:不管是菜单还是菜单项都应该继承菜单组件
*/
public abstract class MenuComponent {
protected String name;
protected int level;
//添加菜单
public void add(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
//删除菜单
public void remove(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
//获取指定的子菜单
public MenuComponent getChild(int i) {
throw new UnsupportedOperationException();
}
//获取菜单名称
public String getName() {
return name;
}
public void print() {
throw new UnsupportedOperationException();
}
}
add()、remove()、getChild()
方法,但是 MenuItem 就不应该有这些方法。这里给出的默认实现是抛出异常,也可以根据自己的需要改写默认实现。类Menu
public class Menu extends MenuComponent{
private List<MenuComponent> menuComponentList;
public Menu(String name, int level) {
this.level = level;
this.name = name;
menuComponentList = new ArrayList<>();
}
@Override
public void add(MenuComponent menuComponent) {
menuComponentList.add(menuComponent);
}
@Override
public void remove(MenuComponent menuComponent) {
menuComponentList.remove(menuComponent);
}
@Override
public MenuComponent getChild(int i) {
return menuComponentList.get(i);
}
@Override
public void print() {
for (int i = 1; i < level; i++) {
System.out.println("--");
}
for (MenuComponent menuComponent : menuComponentList) {
menuComponent.print();
}
}
}
Menu 类已经实现了除了 getName()
方法的其他所有方法,因为 Menu 类具有添加菜单、移除菜单和获取子菜单的功能。
类MenuItem
public class MenuItem extends MenuComponent{
public MenuItem(String name, int level) {
this.level = level;
this.name = name;
}
@Override
public void print() {
for (int i = 1; i < level; i++) {
System.out.println("--");
}
System.out.println(name);
}
}
MenuItem 是菜单项,不能再有子菜单,所以添加菜单、移除菜单和获取子菜单的功能并不用实现。
在使用组合模式时,根据抽象构件类的定义形式,可将组合模式分为透明组合模式和安全组合模式两种形式:
透明组合模式:在透明组合模式中,抽象根节点角色中声明了所有用于管理成员对象的方法,例如在示例中 MenuComponent
声明了 add() 、 remove()
方法,这样做的好处是确保所有的构件类都有相同的接口,透明组合模式也是组合模式的标准形式。
透明组合模式的缺点是不够安全:因为叶子对象和容器对象在本质上是有区别的,叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供 add()、remove()
等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)。
安全组合模式:在安全组合模式中,在抽象构件角色中没有声明任何用于管理成员对象的方法,而是在树枝节点 Menu 类中声明并实现这些方法。安全组合模式的缺点是不够透明,因为叶子构件和容器构具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件类中定义,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件。
此外,组合模式在 JDK 源码中也有应用,集合类 HahsMap 就使用了组合模式: