简明 组合模式(4.3)

桌面上太多东西,会显得杂乱,于是人们使用各种容器将东西加以整理。将一系列基本对象(叶子对象)组合到一个容器中,容器又可以进一步放到另一个容器中。

组合模式(Composite Pattern)通过组合将构件最终构成一个树形结构。在计算机领域,树形结构广泛存在,如文件系统、菜单系统、GUI、Java(数据结构)容器、XML文件等等。组合模式的要点是:叶子对象和各种容器能够统一地处理。封装容器和叶子对象的通用操作的概念,通常称为构件/组件/Component。

程序员通常喜欢用树形结构的词汇介绍组合模式,如Component的通用替代词是Node,composite/合成物称为中间结点或branch,基本对象称为树叶等。

容器等同叶子

假设Client复制/ copy()文件系统的元素,此时不论是文件夹还是文件,可以使用抽象类型Node封装对容器Folder和叶子File的通用操作copy(),Node是Folder和File父类型。容器Folder需要存储和管理它所保存的Node(可以是文件夹或文件)。组合模式下,Client完全不用关心将要处理的文件系统元素是否叶子,“对组合对象的操作与对单一对象的操作具有一致性”。

例程 6 2容器
package structure.composite;
public interface Node{
    public void copy();  //定义统一的接口
}
package structure.composite;
import java.util.ArrayList;
public class Folder implements Node{
    private String folderName;
    //用于存储本Folder下的文件夹或文件
    private ArrayList<Node> nodeList = new ArrayList<>();

    public Folder(String folderName){
        this.folderName = folderName;
    }
    public void add(Node node){  //增加文件或文件夹
        nodeList.add(node);
    }
    /**
     * 递归的复制自己和所有子元素
     */
    @Override public void copy(){
        System.out.println("复制文件夹:" + folderName);
        for(Node x : nodeList){
            x.copy();
        }
    }
}

标准的组合模式结构图如图所示。包括3个角色:构件Component如Node、叶子结点Leaf和中间结点Composite。通常使用ArrayList容纳其他构件,管理子结点需要增加(add)和删除等操作。

再看一个例子。在日常的组织结构中,员工/Employee组成部门,部门组成公司。假设需要统计公司的全部薪水,此时,并不需要位置等同Node的公司类、等同Folder的部门类。所有元素都是Employee对象,而公司CEO、部门经理的属性ArrayList<Employee>容纳其下属/subordinates。在此结构中,Employee可以有一个指向父结点的引用,以便于操作。

例程 6-4容器
package structure.composite;
import java.util.ArrayList;
public class Employee {
    String name;
    int salary;
    ArrayList<Employee> subordinates;
    boolean isLeaf;
    private Employee parent = null;

    public Employee(String name, int salary) {
        this(null,name,salary);
    }
    public Employee(Employee parent, String name, int salary) {
        this.parent = parent;
        this.name = name;
        this.salary = salary;
        subordinates = new ArrayList<>();
    }

    public void setLeaf(boolean b) {
        isLeaf = b; //if true, do not allow children
    }

    public int getSalary() {        return salary;    }
    public String getName() {        return name;  }

    /************************************************/
    public boolean add(Employee e) {
        if (!isLeaf)   subordinates.add(e);
        return isLeaf;
    }

    public void remove(Employee e) {
        if (!isLeaf) subordinates.remove(e);
    }
    //树的查找
    public Employee getChild(String name) {
        Employee newEmp = null;
        if (this.getName().equals(name)){
            return this;
        }
        for(Employee x :subordinates){
            newEmp = x.getChild(name);
            if(newEmp!=null)break;
        }
        return newEmp;
    }

    public int getSalaries() {
        int sum = salary;
        for(Employee x :subordinates){
            sum += x.getSalaries();
        }
        return sum;
    }
}

使用组合模式时,一个费事的工作是将各种组件构成一个树形结构。正如GUI编程(一个组合模式的众所周知的应用)时所做的,大量的编写parent.add(child)。构建数据树的代码可以放在客户代码中,也可以封装到一个单独的类如Model中。客户代码中通常保存一个数据树的根元素。

构件Component的接口

一个较为理论性的话题,[GoF]在介绍组合模式时将它凸显出来:退化继承

构件/Component是仅包含容器/Composite和叶子共同的操作——基本操作,还是包含容器用于管理其子元素的各种操作如add(),getChildren()?

如果构件仅包含基本操作,容器需要对Component扩展继承,当客户以Component声明元素时,容器扩展的方法不能够直接使用,必须将Component向下造型为Composite。换言之,客户将所有构件对象视为叶子。

另一方面,如果构件包含了容器所需的管理方法如add(),则叶子对象此的继承成为了限制继承/退化继承。客户以Component声明元素时,客户将所有构件对象视为容器。而叶子对象替代到应用环境中后,可能出现异常。这一种实现方式在[GoF]中称为透明性——“你可以一致地使用所有的组件”。事实上,它和前者在“透明性”上没有本质区别,前者将所有构件对象视为叶子,后者将所有构件对象视为容器。前者避免了退化继承从而使整个系统符合LSP。后者避免了向下造型或强制类型转换。这一方式,我们称之为大接口组合模式。

大接口组合模式以容器看待所有构件,在实际编程中提供了更多的方便,例程6-4较为典型。其适用场合是程序员有合理的理由将所有构件视为容器,并在Component中为叶子定义合理的add()、getChild()的默认代码以避免退化继承。叶子改写Component的方法,不管是空实现还是抛出异常,通常是不可取的。而且Java中子类的override方法不能够抛出比父类型更大的异常。换言之,如果父类型的getChild()没有抛出异常,子类的代码也不得抛出检查性异常。

时间: 2024-10-12 20:56:38

简明 组合模式(4.3)的相关文章

[js高手之路]设计模式系列课程-组合模式+寄生组合继承实战新闻列表

所谓组合模式,就是把一堆结构分解出来,组成在一起,现实中很多这样的例子,如: 1.肯德基套餐就是一种组合模式, 比如鸡腿堡套餐,一般是是由一个鸡腿堡,一包薯条,一杯可乐等组成的 2.组装的台式机同理,由主板,电源,内存条,显卡, 机箱,显示器,外设等组成的 把一个成型的产品组成部件,分成一个个独立的部件,这种方式可以做出很多灵活的产品,这就是组合模式的优势 比如:家用台式机电脑,要求配置比较低, 这个时候只需要主板+电源+内存条+机箱+显示器+外设就可以了,不需要配置独立显卡 鸡腿堡+鸡翅+紫薯

组合模式(Composite)

一.组合模式介绍 组合模式:将对象组合成树形结构以表示:部分--整体 的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性. java中的组合是指:在A类里定义一个B类的引用,A拥有了B,叫组合.只是单纯的组合而已,而不是一种设计模式. 组合和组合模式不是一回事! 基本上见到的树形结构都使用到了组合模式. 组合模式结构图: 组合模式中有几个核心的部分: Leaf(叶子):表示该节点下面没有其他子节点了,就称为叶子 Compostie(容器构件):容器构件,该节点下还有其他子节点,理解

10.设计模式_组合模式

转载自   http://www.cnblogs.com/zhili/p/CompositePattern.html 一.引言 在软件开发过程中,我们经常会遇到处理简单对象和复合对象的情况,例如对操作系统中目录的处理就是这样的一个例子,因为目录可以包括单独的文件,也可以包括文件夹,文件夹又是由文件组成的,由于简单对象和复合对象在功能上区别,导致在操作过程中必须区分简单对象和复合对象,这样就会导致客户调用带来不必要的麻烦,然而作为客户,它们希望能够始终一致地对待简单对象和复合对象.然而组合模式就是

Java设计模式应用——组合模式

组合模式实际上是一种树形数据结构.以windows目录系统举例,怎么样用java语言描述一个文件夹? 定义一个文件夹类,文件夹类中包含若干个子文件类和若干个文件类. 进一步抽象,把文件夹和文件都看做节点,于是一个文件夹就可以描述为一个节点类,包含若干个子节点. 我们看看组合模式的代码 // 抽象节点 public abstract class Node { protected String name; abstract void add(Node node); abstract void rem

设计模式 -- 组合模式(Composite)

写在前面的话:读书破万卷,编码如有神--------------------------------------------------------------------主要内容包括: 初识组合模式,包括:定义.结构.参考实现 体会组合模式,包括:场景问题.不用模式的解决方案.使用模式的解决方案 理解组合模式,包括:认识组合模式.安全性和透明性.父组件引用.环状引用.组合模式的优缺点 思考组合模式,包括:组合模式的本质.何时选用 参考内容: 1.<研磨设计模式> 一书,作者:陈臣.王斌 --

11 组合模式

组合模式(Composite)定义:将对象组合成树形结构以表示"部分-整体"的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性. UML类图如下: 比如<大话>中举的例子,公司总部在北京,然后在南京.杭州设有办事处,总公司和分支办事处都有相似的组织结构,比如都有人力资源部.财务部等.如下图: 再有HeadFirst举的关于菜单的例子,如下图: 如果程序需要表达上面例子中的公司层级组织或是菜单层级的结构,就可以采用组合模式. 组合模式能让我们用树形方式创建对象的

php中的组合模式

刚看完了<深入php面向对象.模式与实践>一书中组合模式这块内容,为了加深理解和记忆,所以着手写了这篇博客. 为方便后续理解,此处先引入两个概念,局部对象和组合对象. 局部对象:无法将其他对象组合到自身内部属性上的对象.即不能组合其他对象的对象. 组合对象:可以将其他对象组合到自身内部属性上的对象.即可以组合其他对象的对象. 注:将对象A的某个属性中存储着对象B对象的引用,则表示A与B有组合关系,其中A将B组合到了自身内部. 首先我们通过给出下面的业务需求,来引入组合模式: 业务部门想要开发一

设计模式学习笔记(十五:组合模式)

1.1概述 将对象组合成树形结构以表示"部分-整体"的层次结构.组合(Composite)使用户对单个对象和组合对象的使用具有一致性.这就是组合模式的定义. 如果一个对象包含另一个对象的引用,称这样的对象为组合对象.如果将当前组合对象作为一个整体的话,那么它所包含的对象就是该整体的一部分.如果一个对象不含有其他对象的引用,称这样的对象为个体对象.在编写程序时,我们希望将许多个体对象和组合对象组成树形结构,以此表示"部分-整体"的层次结构,并借助该层次结构使得用户能用

javascript设计模式学习之十——组合模式

一.组合模式定义及使用场景 组合模式将对象组合成树形结构,用以表示“部分—整体”的层次结构,除了用来表示树形结构之外,组合模式还可以利用对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性. 实现组合模式的关键: 在java等静态语言中,需要单个对象和组合对象都实现同样的抽象接口,这可以通过抽象类或者接口来实现. 在javascript中,对象的多态性是与生俱来的,没有编译器去检查对象的类型,因此实现组合模式的要点是保证组合兑现个单个对象用友同样的方法,这通常需要使用“鸭子类型”的思想