自大学课程初识设计模式以来,就越发觉得有必要系统学习一下设计模式。
刚好在实习前准备期间课比较少,抽出一点时间整理一下记一些笔记,复制粘贴比较多。
笔记比较适合学习过设计模式的同学。

Composite Pattern(组合模式)


组合模式的适用范围

(1) 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们

(2) 在一个使用面向对象语言开发的系统中需要处理一个树形结构

(3) 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型。


应用实例

Java SE 中的 AWT 和 Swing 包的设计就基于组合模式,在这些界面包中为用户提供了大量的容器构件(如 Container)和成员构件(如 Checkbox、Button 和 TextComponent等)

javadoc插件模板配置_设计模式


桥接模式如何实现

角色

Component(抽象构件)

它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。

Leaf(叶子构件)

它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。

Composite(容器构件)

它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。

类图

javadoc插件模板配置_Composite_02


但是这样的结构会出现一些问题,就是不够安全,因为叶子节点继承抽象类时也需要实现add、remove方法,但是大家也知道叶子节点是不可能使用这些方法的,所以需要在实现中使用抛出错误。

解决办法有下:

1.将叶子构件的 add()、remove() 等方法的实现代码移至 AbstractFile 类中,由 AbstractFile 提供统一的默认实现。我觉得这是比较好的方法,但是不知道为什么不经常用。

2.还有一种解决方法是在抽象构件 AbstractFile 中不声明任何用于访问和管理成员构件的方法,将访问方法都给容器节点,这样就会使得容器节点与客户端出现了耦合,不够透明。在实际应用中,安全组合模式的使用频率也非常高,在 Java AWT 中使用的组合模式就是安全组合模式。


组合模式的优缺点

主要优点

(1) 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。

(2) 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。

(3) 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”。

(4) 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

主要缺点

在增加新构件时很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂。


练习

题目

Sunny 软件公司欲开发一个界面控件库,界面控件分为两大类,一类是单元控件,例如按钮、文本框等,一类是容器控件,例如窗体、中间面板等,试用组合模式设计该界面控件库。

题目分析

题目还是比较明确的,在设计方面我多加了两个类作为单元控件的集合和容器控件的集合。

实现代码

抽象构件:Controls.java
package com.joy;

public abstract class Controls {
    public abstract void add(Controls child);
    public abstract void remove(Controls child);
    public abstract Controls getChild(int i);
    public abstract void useControl();
}
叶子构件:UnitControls.java
package com.joy;

public class UnitControls extends Controls {

    @Override
    public void add(Controls child) {
        try {
            throw new Exception("没有该方法……");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void remove(Controls child) {
        try {
            throw new Exception("没有该方法……");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public Controls getChild(int i) {
        try {
            throw new Exception("没有该方法……");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    @Override
    public void useControl() {
        System.out.println("使用了单元控件……");
    }

}
叶子构件:ButtonControl.java
package com.joy;

public class ButtonControl extends UnitControls {
    @Override
    public void useControl() {
        System.out.println("使用了按钮控件……");
    }
}
叶子构件:TextViewControl.java
package com.joy;

public class TextViewControl extends UnitControls {
    @Override
    public void useControl() {
        System.out.println("使用了文本框控件……");
    }
}
容器构件:ContainerControls.java
package com.joy;

import java.util.ArrayList;

public class ContainerControls extends Controls {

    ArrayList<Controls> childList = new ArrayList<Controls>();
    @Override
    public void add(Controls child) {
        childList.add(child);
    }

    @Override
    public void remove(Controls child) {
        childList.remove(child);
    }

    @Override
    public Controls getChild(int i) {
        return childList.get(i);
    }

    @Override
    public void useControl() {
        for(Controls obj : childList)
        {
             obj.useControl();
        }

    }

}
容器构件:FormControl.java
package com.joy;

public class FormControl extends ContainerControls {
    @Override
    public void useControl() {
        System.out.println("使用了窗体控件……");
        super.useControl();
    }
}
容器构件:MiddlewareControl.java
package com.joy;

public class MiddlewareControl extends ContainerControls {
    @Override
    public void useControl() {
        System.out.println("使用了中间件控件……");
        super.useControl();
    }
}
客户端:Clien.java
package com.joy;

public class Client {
    public static void main(String[] args) {
        Controls b,tv,f,mw;
        f=new FormControl();
        mw=new MiddlewareControl();
        b=new ButtonControl();
        tv = new TextViewControl();
        mw.add(b);
        mw.add(tv);
        f.add(mw);
        f.useControl();
    }
}

运行结果

javadoc插件模板配置_javadoc插件模板配置_03


总结

组合模式还是蛮典型的,因为它主要体现了面向抽象的原则,使得简化很多重复的工作,这让我想起了学操作系统的时候,设计文件系统的时候有思考建一个抽象的父类,但是当时并不知道自己使用了组合模式。