前言:
视频教程:黑马程序员Java设计模式详解,全网最全23种Java设计模式
什么是设计模式?
- 设计模式(Design Pattern)是前辈们对代码开发经验的总结,是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
- 1995年,GoF(Gang of Four,四人组)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了23种设计模式,人称 【GoF设计模式】
设计模式分类 具体模式 创建型模式:
它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。⌛单例模式、⌛工厂模式、⌛抽象工厂模式、⌛建造者模式、⌛原型模式 结构型模式:
结构型模式描述如何将类或对象按某种布局组成更大的结构。⌛适配器模式、⌛桥接模式、⌛装饰模式、⌛代理模式、⌛外观模式、⌛组合模式、享元模式、 行为型模式:
这些设计模式特别关注对象之间的通信。模板方法模、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式
组合模式:
概述:
组合模式(Composite):
将对象组合成树形结构以表示“部分-整体“的层次结构。组合模式使得用户对单个对象和组合对象
的使用具有一致性.[DP]
引入:
对于上图的文件管理系统,是一种非常常见的树型结构:
- 在树形结构中可以通过调用某个方法来遍历整个树,当我们找到某个叶子节点后,就可以对叶子节点进行相关的操作。
可以将树型结构理解成一个大的容器,容器里面包含很多的成员对象,这些成员对象即可是容器对象也可以是叶子对象。
但是由于容器对象和叶子对象在功能上面的区别,使得我们在使用的过程中必须要区分容器对象和叶子对象,但是这样就会给客户带来不必要的麻烦,作为客户而已,它始终希望能够一致的对待容器对象和叶子对象。
定义:
组合模式又名”部分–整体模式“,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
结构:
组合模式主要包含三种角色:
- 抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性。
- 树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。
- 叶子节点(Leaf):叶子节点对象,其下再无分支,是系统层次遍历的最小单位。
实例:
接下来就以菜单条目,一种非常典型的树型结构为例子,实现“组合模式”:
[如下图]一个菜单可以包含菜单项(菜单项是指不再包含其他内容的菜单条目),也可以包含带有其他菜单项的菜单。
我们的需求是针对一个菜单,打印出其包含的所有菜单以及菜单项的名称。
实例类图:
-
菜单组件:
// 抽象根节点(Component):菜单组件:定义 菜单单项/复合菜单 共有的属性 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 index){ throw new UnsupportedOperationException(); } //获取菜单或者菜单项的名称:提高代码复用性 public String getName() { return name; } //打印打印菜单名称的方法(包含 菜单单项 和 复合菜单) public abstract void print(); }
-
复合菜单:
// 树枝节点(Composite):复合菜单类: public class Menu extends MenuComponent { //定义菜单集合: private List<MenuComponent> menuComponentList = new ArrayList<MenuComponent>(); //定义菜单: public Menu(String name,int level){ this.name = name; this.level = level; }; @Override public void add(MenuComponent menuComponent) { menuComponentList.add(menuComponent); } @Override public void remove(MenuComponent menuComponent) { menuComponentList.remove(menuComponent); } @Override public MenuComponent getChild(int index) { return menuComponentList.get(index); } @Override public void print() { //打印层级: for (int i = 0; i < level; i++) { System.out.print("--"); } System.out.println(name); for (MenuComponent menuComponent : menuComponentList) { menuComponent.print(); } } }
-
菜单单项:
//叶子节点:菜单单项 public class MenuItem extends MenuComponent { public MenuItem(String name,int level){ this.name = name; this.level = level; } @Override public void print() { //打印层级: for (int i = 0; i < level; i++) { System.out.print("--"); } System.out.println(name); } }
-
输出结果:
小结:
组合模式类型:
-
透明方式
Component 中声明所有用来管理子对象的方法,其中包括add,remove等(在MenuItem用不着的方法),这样实Component 接口的所有子类都具备了add和remove方法,只有需要用到的时候才需要重写:
- 这样做的好处就是叶节点和枝节点对于外界没有区别,它们具备完全一致的行为接口。
- 但问题也很明显:不够安全,因为叶子对象和容器对象在本质上是有区别的,叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供 add()、remove() 等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)。
-
安全方式
Component 接口中不去声明Add和Remove 方法,那么子类的Leaf也就不需要去实现它,而是在Composite 声明所有用来管理子类对象的方法,这样做就不会出现刚才提到的问题。
- 不过由于不够透明,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件,所以树叶和树枝类将不具有相同的接口,客户端的调用需要做相应的判断,带来了一定不便。
优点:
- 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
- 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
- 在组合模式中增加新的树枝节点和叶子节点都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
- 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子节点和树枝节点的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。
应用案例:
-
在JDK以及MyBatis中都有涉及
MyBatis中有一个SqlNode接口,下面很多一级标签:
然后一级标签下面又有二级标签(这就是组合模式的体现):
-
Java设计模式:23种设计模式全面解析(超级详细)以及在源码中的应用
组合模式在源码中的应用, 比如HashMap, 点进去后可以看到: 抽象类是Map 接口, 叶子节点是 Node(也实现了 Map接口), 子节点是 HashMap(也实现了Map接口,并在HashMap中维护了一个存储方式 node的数组:Node<K,V>[] table),对应的UML类图如下: