厚积薄发打卡Day38 :[itcast] GoF23通俗易懂的设计模式之 <组合模式>

前言:

视频教程:黑马程序员Java设计模式详解,全网最全23种Java设计模式

什么是设计模式?

  • 设计模式(Design Pattern)是前辈们对代码开发经验的总结,是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
  • 1995年,GoF(Gang of Four,四人组)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了23种设计模式,人称 【GoF设计模式】
设计模式分类 具体模式
创建型模式:
它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。
⌛单例模式、⌛工厂模式、⌛抽象工厂模式、⌛建造者模式、⌛原型模式
结构型模式:
结构型模式描述如何将类或对象按某种布局组成更大的结构。
⌛适配器模式、⌛桥接模式、⌛装饰模式、⌛代理模式、⌛外观模式、⌛组合模式、享元模式、
行为型模式:
这些设计模式特别关注对象之间的通信。
模板方法模、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式

组合模式:

概述:

组合模式(Composite):

将对象组合成树形结构以表示“部分-整体“的层次结构。组合模式使得用户对单个对象和组合对象
的使用具有一致性.[DP]

引入:

厚积薄发打卡Day38 :[itcast] GoF23通俗易懂的设计模式之 <组合模式>

对于上图的文件管理系统,是一种非常常见的树型结构

  • 在树形结构中可以通过调用某个方法来遍历整个树,当我们找到某个叶子节点后,就可以对叶子节点进行相关的操作。

可以将树型结构理解成一个大的容器,容器里面包含很多的成员对象,这些成员对象即可是容器对象也可以是叶子对象。

但是由于容器对象和叶子对象在功能上面的区别,使得我们在使用的过程中必须要区分容器对象和叶子对象,但是这样就会给客户带来不必要的麻烦,作为客户而已,它始终希望能够一致的对待容器对象和叶子对象。

定义:

组合模式又名”部分–整体模式“,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。

这种类型的设计模式属于结构型模式,它创建了对象组的树形结构

结构:

组合模式主要包含三种角色:

  • 抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性。
  • 树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。
  • 叶子节点(Leaf):叶子节点对象,其下再无分支,是系统层次遍历的最小单位。

实例:

接下来就以菜单条目,一种非常典型的树型结构为例子,实现“组合模式”:

[如下图]一个菜单可以包含菜单项(菜单项是指不再包含其他内容的菜单条目),也可以包含带有其他菜单项的菜单。

我们的需求是针对一个菜单,打印出其包含的所有菜单以及菜单项的名称。

厚积薄发打卡Day38 :[itcast] GoF23通俗易懂的设计模式之 <组合模式>

实例类图:
厚积薄发打卡Day38 :[itcast] GoF23通俗易懂的设计模式之 <组合模式>

  • 菜单组件:

    // 抽象根节点(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);
        }
    }
    
  • 输出结果:

    厚积薄发打卡Day38 :[itcast] GoF23通俗易懂的设计模式之 <组合模式>

小结:

组合模式类型:

  • 透明方式

    Component 中声明所有用来管理子对象的方法,其中包括add,remove等(在MenuItem用不着的方法),这样实Component 接口的所有子类都具备了add和remove方法,只有需要用到的时候才需要重写:

    • 这样做的好处就是叶节点和枝节点对于外界没有区别,它们具备完全一致的行为接口。
    • 但问题也很明显:不够安全,因为叶子对象和容器对象在本质上是有区别的,叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供 add()、remove() 等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)。
  • 安全方式

    Component 接口中不去声明Add和Remove 方法,那么子类的Leaf也就不需要去实现它,而是在Composite 声明所有用来管理子类对象的方法,这样做就不会出现刚才提到的问题。

    • 不过由于不够透明,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件,所以树叶和树枝类将不具有相同的接口,客户端的调用需要做相应的判断,带来了一定不便。

优点:

  • 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
  • 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
  • 在组合模式中增加新的树枝节点和叶子节点都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
  • 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子节点和树枝节点的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

应用案例:

  1. 组合模式在源码中的应用

    在JDK以及MyBatis中都有涉及

    MyBatis中有一个SqlNode接口,下面很多一级标签:

    厚积薄发打卡Day38 :[itcast] GoF23通俗易懂的设计模式之 <组合模式>

    然后一级标签下面又有二级标签(这就是组合模式的体现):

    厚积薄发打卡Day38 :[itcast] GoF23通俗易懂的设计模式之 <组合模式>

  2. 组合模式在MyBatis源码中的应用

  3. Java设计模式:23种设计模式全面解析(超级详细)以及在源码中的应用

    组合模式在源码中的应用, 比如HashMap, 点进去后可以看到: 抽象类是Map 接口, 叶子节点是 Node(也实现了 Map接口), 子节点是 HashMap(也实现了Map接口,并在HashMap中维护了一个存储方式 node的数组:Node<K,V>[] table),对应的UML类图如下:

    厚积薄发打卡Day38 :[itcast] GoF23通俗易懂的设计模式之 <组合模式>

上一篇:ssh服务器使用


下一篇:知行教育_访问咨询主题-增量采集