写在后面

  • 记录学习设计模式的笔记
  • 进步对设计模式的灵活运用

学习地址

https://www.bilibili.com/vide...

https://www.bilibili.com/vide...

参考文章

http://c.biancheng.net/view/1...

我的项目源码
https://gitee.com/zhuang-kang/DesignPattern

16,组合模式

16.1 组合模式的定义和特点

组合(Composite Pattern)模式的定义:有时又叫作整体-局部(Part-Whole)模式,它是一种将对象组合成树状的层次结构的模式,用来示意“整体-局部”的关系,使用户对单个对象和组合对象具备统一的拜访性,属于结构型设计模式。

组合模式个别用来形容整体与局部的关系,它将对象组织到树形构造中,顶层的节点被称为根节点,根节点上面能够蕴含树枝节点和叶子节点,树枝节点上面又能够蕴含树枝节点和叶子节点,树形结构图如下

由上图能够看出,其实根节点和树枝节点实质上属于同一种数据类型,能够作为容器应用;而叶子节点与树枝节点在语义上不属于用一种类型。然而在组合模式中,会把树枝节点和叶子节点看作属于同一种数据类型(用对立接口定义),让它们具备统一行为。

这样,在组合模式中,整个树形构造中的对象都属于同一种类型,带来的益处就是用户不须要分别是树枝节点还是叶子节点,能够间接进行操作,给用户的应用带来极大的便当。

组合模式的次要长处有:

  1. 组合模式使得客户端代码能够统一地解决单个对象和组合对象,毋庸关怀本人解决的是单个对象,还是组合对象,这简化了客户端代码;
  2. 更容易在组合体内退出新的对象,客户端不会因为退出了新的对象而更改源代码,满足“开闭准则”;

其次要毛病是:

  1. 设计较简单,客户端须要花更多工夫理清类之间的档次关系;
  2. 不容易限度容器中的构件;
  3. 不容易用继承的办法来减少构件的新性能;

16.2 组合模式的构造与实现

16.2.1 组合模式的构造

  1. 形象构件(Component)角色:它的次要作用是为树叶构件和树枝构件申明公共接口,并实现它们的默认行为。在通明式的组合模式中形象构件还申明拜访和治理子类的接口;在平安式的组合模式中不申明拜访和治理子类的接口,管理工作由树枝构件实现。(总的抽象类或接口,定义一些通用的办法,比方新增、删除)
  2. 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现形象构件。
  3. 树枝构件(Composite)角色 / 两头构件:是组合中的分支节点对象,它有子节点,用于继承和实现形象构件。它的次要作用是存储和治理子部件,通常蕴含 Add()、Remove()、GetChild() 等办法。

16.2.2 代码实现

如下图,咱们在拜访别的一些管理系统时,常常能够看到相似的菜单。一个菜单能够蕴含菜单项(菜单项是指不再蕴含其余内容的菜单条目),也能够蕴含带有其余菜单项的菜单,因而应用组合模式形容菜单就很失当,咱们的需要是针对一个菜单,打印出其蕴含的所有菜单以及菜单项的名称。

MenuComponent MenuComponent定义为抽象类,因为有一些共有的属性和行为要在该类中实现,Menu和MenuItem类就能够只笼罩本人感兴趣的办法,而不必搭理不须要或者不感兴趣的办法,举例来说,Menu类能够蕴含子菜单,因而须要笼罩add()、remove()、getChild()办法,然而MenuItem就不应该有这些办法。这里给出的默认实现是抛出异样,你也能够依据本人的须要改写默认实现。

package com.zhuang.combination;/** * @Classname MenuComponent * @Description 菜单组件 不论菜单还是菜单项,都应该继承该类  抽象类 * @Date 2021/3/24 17:02 * @Created by dell */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();    }}

Menu类曾经实现了除了getName办法的其余所有办法,因为Menu类具备增加菜单,移除菜单和获取子菜单的性能

package com.zhuang.combination;import java.util.ArrayList;import java.util.List;/** * @Classname Menu * @Description 菜单类 继承菜单组件 * @Date 2021/3/24 17:05 * @Created by dell */public class Menu extends MenuComponent {    private List<MenuComponent> menuComponentList;    public Menu(String name, int level) {        this.name = name;        this.level = level;        menuComponentList = new ArrayList<MenuComponent>();    }    @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 = 0; i < level; i++) {            System.out.print("--");        }        System.out.println(name);        for (MenuComponent menuComponent : menuComponentList) {            menuComponent.print();        }    }}

MenuItem是菜单项,不能再有子菜单,所以增加菜单,移除菜单和获取子菜单的性能并不能实现。

package com.zhuang.combination;/** * @Classname MenuItem * @Description 菜单选项 继承菜单组件 * @Date 2021/3/24 17:10 * @Created by dell */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);    }}

在应用组合模式时,依据形象构件类的定义模式,咱们可将组合模式分为通明组合模式和平安组合模式两种模式。

  • 通明组合模式

    通明组合模式中,形象根节点角色中申明了所有用于治理成员对象的办法,比方在示例中 MenuComponent 申明了 addremovegetChild 办法,这样做的益处是确保所有的构件类都有雷同的接口。通明组合模式也是组合模式的规范模式。

    通明组合模式的毛病是不够平安,因为叶子对象和容器对象在实质上是有区别的,叶子对象不可能有下一个档次的对象,即不可能蕴含成员对象,因而为其提供 add()、remove() 等办法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些办法可能会出错(如果没有提供相应的错误处理代码)

  • 平安组合模式

    在平安组合模式中,在形象构件角色中没有申明任何用于治理成员对象的办法,而是在树枝节点 Menu 类中申明并实现这些办法。平安组合模式的毛病是不够通明,因为叶子构件和容器构件具备不同的办法,且容器构件中那些用于治理成员对象的办法没有在形象构件类中定义,因而客户端不能齐全针对形象编程,必须有区别地看待叶子构件和容器构件。

16.3 组合模式的利用场景

  1. 在须要示意一个对象整体与局部的层次结构的场合。
  2. 要求对用户暗藏组合对象与单个对象的不同,用户能够用对立的接口应用组合构造中的所有对象的场合。

写在最初

  • 如果我的文章对你有用,请给我点个,感激你!
  • 有问题,欢送在评论区指出!