Java 设计模式系列(九)组合模式

Java 设计模式系列(九)组合模式

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

一、组合模式结构

  • Component: 抽象的组件对象,为组合中的对象声明接口,让客户端可以通过这个接口来访问和管理整个对象结构,可以在里面为定义的功能提供缺省的实现。
  • Leaf: 叶子节点对象,定义和实现叶子对象的行为,不再包含其它的子节点对象。
  • Composite: 组合对象,通常会存储子组件,定义包含子组件的那些组件的行为,并实现在组件接口中定义的与子组件有关的操作。
  • Client: 客户端,通过组件接口来操作组合结构里面的组件对象。

组合模式参考实现

(1)先看看组件对象的定义,示例代码如下:

/**
 * 抽象的组件对象,为组合中的对象声明接口,实现接口的缺省行为
 */
public abstract class Component {

    /** 示意方法,子组件对象可能有的功能方法 */
    public abstract void someOperation();

    /** 向组合对象中加入组件对象 */
    public void addChild(Component child) {
        // 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
        throw new UnsupportedOperationException("对象不支持这个功能");
    }

    /** 从组合对象中移出某个组件对象 */
    public void removeChild(Component child) {
        // 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
        throw new UnsupportedOperationException("对象不支持这个功能");
    }

    /** 返回某个索引对应的组件对象 */
    public Component getChildren(int index) {
        // 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
        throw new UnsupportedOperationException("对象不支持这个功能");
    }
}

(2)接下来看看Composite对象的定义,示例代码如下:

/**
 * 组合对象,通常需要存储子对象,定义有子部件的部件行为,
 * 并实现在Component里面定义的与子部件有关的操作
 */
public class Composite extends Component {
    /**
     * 用来存储组合对象中包含的子组件对象
     */
    private List<Component> childComponents = null;

    /** 示意方法,通常在里面需要实现递归的调用 */
    public void someOperation() {
        if (childComponents != null){
            for(Component c : childComponents){
                //递归的进行子组件相应方法的调用
                c.someOperation();
            }
        }
    }
    public void addChild(Component child) {
        //延迟初始化
        if (childComponents == null) {
            childComponents = new ArrayList<Component>();
        }
        childComponents.add(child);
    }

    public void removeChild(Component child) {
        if (childComponents != null) {
            childComponents.remove(child);
        }
    }

    public Component getChildren(int index) {
        if (childComponents != null){
            if(index>=0 && index<childComponents.size()){
                return childComponents.get(index);
            }
        }
        return null;
    }
}

(3)该来看叶子对象的定义了,相对而言比较简单,示例代码如下:

/**
 * 叶子对象,叶子对象不再包含其它子对象
 */
public class Leaf extends Component {
    /** 示意方法,叶子对象可能有自己的功能方法 */
    public void someOperation() {
        // do something
    }

}

(4)对于Client,就是使用Component接口来操作组合对象结构,由于使用方式千差万别,这里仅仅提供一个示范性质的使用,顺便当作测试代码使用,示例代码如下:

public class Client {
    public static void main(String[] args) {
        //定义多个Composite对象
        Component root = new Composite();
        Component c1 = new Composite();
        Component c2 = new Composite();
        //定义多个叶子对象
        Component leaf1 = new Leaf();
        Component leaf2 = new Leaf();
        Component leaf3 = new Leaf();

        //组和成为树形的对象结构
        root.addChild(c1);
        root.addChild(c2);
        root.addChild(leaf1);

        c1.addChild(leaf2);
        c2.addChild(leaf3);

        //操作Component对象
        Component o = root.getChildren(1);
        System.out.println(o);
    }
}

二、父组件引用

(1) Component

public abstract class Component {
    /**
     * 记录父组件对象
     */
    private Component parent = null;

    /**
     * 获取一个组件的父组件对象
     * @return 一个组件的父组件对象
     */
    public Component getParent() {
        return parent;
    }
    /**
     * 设置一个组件的父组件对象
     * @param parent 一个组件的父组件对象
     */
    public void setParent(Component parent) {
        this.parent = parent;
    }
    /**
     * 返回某个组件的子组件对象
     * @return 某个组件的子组件对象
     */
    public List<Component> getChildren() {
        throw new UnsupportedOperationException("对象不支持这个功能");
    }

    /*-------------------以下是原有的定义----------------------*/

    /**
     * 输出组件自身的名称
     */
    public abstract void printStruct(String preStr);

    /**
     * 向组合对象中加入组件对象
     * @param child 被加入组合对象中的组件对象
     */
    public void addChild(Component child) {
        // 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
        throw new UnsupportedOperationException("对象不支持这个功能");
    }

    /**
     * 从组合对象中移出某个组件对象
     * @param child 被移出的组件对象
     */
    public void removeChild(Component child) {
        // 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
        throw new UnsupportedOperationException("对象不支持这个功能");
    }

    /**
     * 返回某个索引对应的组件对象
     * @param index 需要获取的组件对象的索引,索引从0开始
     * @return 索引对应的组件对象
     */
    public Component getChildren(int index) {
        throw new UnsupportedOperationException("对象不支持这个功能");
    }
}

(2) Leaf

public class Leaf extends Component {
    /**
     * 叶子对象的名字
     */
    private String name = "";

    /**
     * 构造方法,传入叶子对象的名字
     * @param name 叶子对象的名字
     */
    public Leaf(String name){
        this.name = name;
    }

    /**
     * 输出叶子对象的结构,叶子对象没有子对象,也就是输出叶子对象的名字
     * @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
     */
    public void printStruct(String preStr){
        System.out.println(preStr+"-"+name);
    }
}

(3) Composite

public class Composite extends Component{

    public void addChild(Component child) {
        //延迟初始化
        if (childComponents == null) {
            childComponents = new ArrayList<Component>();
        }
        childComponents.add(child);

        //添加对父组件的引用
        child.setParent(this);
    }

    public void removeChild(Component child) {
        if (childComponents != null) {
            //查找到要删除的组件在集合中的索引位置
            int idx = childComponents.indexOf(child);
            if (idx != -1) {
                //先把被删除的商品类别对象的父商品类别,设置成为被删除的商品类别的子类别的父商品类别
                for(Component c : child.getChildren()){
                    //删除的组件对象是本实例的一个子组件对象
                    c.setParent(this);
                    //把被删除的商品类别对象的子组件对象添加到当前实例中
                    childComponents.add(c);
                }

                //真的删除
                childComponents.remove(idx);
            }
        }
    }

    public List<Component> getChildren() {
        return childComponents;
    }

    /*-------------------以下是原有的实现,没有变化----------------------*/

    /**
     * 用来存储组合对象中包含的子组件对象
     */
    private List<Component> childComponents = null;

    /**
     * 组合对象的名字
     */
    private String name = "";

    /**
     * 构造方法,传入组合对象的名字
     * @param name 组合对象的名字
     */
    public Composite(String name){
        this.name = name;
    }

    /**
     * 输出组合对象自身的结构
     * @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
     */
    public void printStruct(String preStr){
        //先把自己输出去
        System.out.println(preStr+"+"+this.name);
        //如果还包含有子组件,那么就输出这些子组件对象
        if(this.childComponents!=null){
            //然后添加一个空格,表示向后缩进一个空格
            preStr+=" ";
            //输出当前对象的子对象了
            for(Component c : childComponents){
                //递归输出每个子对象
                c.printStruct(preStr);
            }
        }
    }
}

三、总结

(1) 观察者模式的本质

组合模式的本质: 统一叶子对象和组合对象。

组合模式通过把叶子对象当成特殊的组合对象看待,从而对叶子对象和组合对象一视同仁,统统当成了Component对象,有机的统一了叶子对象和组合对象。

正是因为统一了叶子对象和组合对象,在将对象构建成树形结构的时候,才不需要做区分,反正是组件对象里面包含其它的组件对象,如此递归下去;也才使得对于树形结构的操作变得简单,不管对象类型,统一操作。

(2) 何时选用观察者模式

  • 如果你想表示对象的部分-整体层次结构,可以选用组合模式,把整体和部分的操作统一起来,使得层次结构实现更简单,从外部来使用这个层次结构也简单。
  • 如果你希望统一的使用组合结构中的所有对象,可以选用组合模式,这正是组合模式提供的主要功能。

(3) 观察者模式的优缺点

  • 定义了包含基本对象和组合对象的类层次结构

    在组合模式中,基本对象可以被组合成更复杂的组合对象,而组合对象又可以组合成更复杂的组合对象,可以不断地递归组合下去,从而构成一个统一的组合对象的类层次结构

  • 统一了组合对象和叶子对象

    在组合模式中,可以把叶子对象当作特殊的组合对象看待,为它们定义统一的父类,从而把组合对象和叶子对象的行为统一起来

  • 简化了客户端调用

    组合模式通过统一组合对象和叶子对象,使得客户端在使用它们的时候,就不需要再去区分它们,客户不关心使用的到底是什么类型的对象,这就大大简化了客户端的使用

  • 更容易扩展

    由于客户端是统一的面对Component来操作,因此,新定义的Composite或Leaf子类能够很容易的与已有的结构一起工作,而客户端不需要为增添了新的组件类而改变

  • 很难限制组合中的组件类型

    容易增加新的组件也会带来一些问题,比如很难限制组合中的组件类型。这在需要检测组件类型的时候,使得我们不能依靠编译期的类型约束来完成,必须在运行期间动态检测。



每天用心记录一点点。内容也许不重要,但习惯很重要!

原文地址:https://www.cnblogs.com/binarylei/p/9010613.html

时间: 2024-12-09 03:30:02

Java 设计模式系列(九)组合模式的相关文章

C#设计模式之九组合模式(Composite Pattern)【结构型】

原文:C#设计模式之九组合模式(Composite Pattern)[结构型] 一.引言 今天我们要讲[结构型]设计模式的第四个模式,该模式是[组合模式],英文名称是:Composite Pattern.当我们谈到这个模式的时候,有一个物件和这个模式很像,也符合这个模式要表达的意思,那就是"俄罗斯套娃"."俄罗斯套娃"就是大的瓷器娃娃里面装着一个小的瓷器娃娃,小的瓷器娃娃里面再装着更小的瓷器娃娃,直到最后一个不能再装更小的瓷器娃娃的那个瓷器娃娃为止(有点绕,下面我会

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

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

java设计模式 GOF23 09 组合模式

一.组合模式简介 把整体和部分的关系用树形结构表示,从而可以统一处理. 二.组合模式实现细节 1)抽象构建:定义叶子和容器的共同特点. 2)叶子:没有子节点 3)容器:有容器的特征,可以包含子节点(一般实现的时候在这里添加容器存放子节点) 三.简单代码实现 package com.lz.combination; import java.util.ArrayList; import java.util.List; /* * 抽象构建 */ public interface IComponent {

Java设计模式之《组合模式》及应用场景

摘要: 原创作品,可以转载,但是请标注出处地址http://www.cnblogs.com/V1haoge/p/6489827.html 组合模式,就是在一个对象中包含其他对象,这些被包含的对象可能是终点对象(不再包含别的对象),也有可能是非终点对象(其内部还包含其他对象,或叫组对象),我们将对象称为节点,即一个根节点包含许多子节点,这些子节点有的不再包含子节点,而有的仍然包含子节点,以此类推.很明显,这是树形结构,终结点叫叶子节点,非终节点(组节点)叫树枝节点,第一个节点叫根节点.同时也类似于

Java设计模式系列之策略模式

策略模式的定义: 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换,策略模式让算法独立于使用它的客户而独立变化. 策略模式使这些算法在客户端调用它们的时候能够互不影响地变化. 策略模式的意义:   策略模式使开发人员能够开发出由许多可替换的部分组成的软件,并且各个部分之间是低耦合的关系. 低耦合的特性使软件具有更强的可扩展性,易于维护:更重要的是,它大大提高了软件的可重用性.    策略模式中有三个对象:      环境对象(Context):该类中实现了对抽象策略中

Java设计模式系列之状态模式

状态模式(State)的定义 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类 状态模式(State)适用性 1.一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为. 2.一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态. 这个状态通常用一个或多个枚举常量表示. 通常,有多个操作包含这一相同的条件结构. State模式将每一个条件分支

Java设计模式系列之桥接模式

桥接模式(Bridge)的定义 在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?这就要使用桥接模式 将抽象部分与它的实现部分分离,使它们都可以独立地变化. 桥接模式(Bridge)的动机 当一种抽象类型可能有多种实现方式时,一般情况我们可以考虑使用继承来解决抽象类型的多种实现,在抽象类型中定义接口,而子类负责接口的具体实现.但这种做法缺乏灵活性,由于抽象类型和子类之间紧紧地绑定在一起,使得这种关系在运行时不能再修改,这使得它难以修改.扩展和重用

Java设计模式系列之迭代器模式

迭代器模式定义 迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示. 迭代器模式的角色构成 (1)迭代器角色(Iterator):定义遍历元素所需要的方法,一般来说会有这么三个方法:取得下一个元素的方法next(),判断是否遍历结束的方法hasNext()),移出当前对象的方法remove(), (2)具体迭代器角色(Concrete Iterator):实现迭代器接口中定义的方法,完成集合的迭代. (3)容器角色(Aggregate): 

java设计模式之九外观模式(Facade)

外观模式是为了解决类与类之家的依赖关系的,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,该模式中没有涉及到接口,看下类图:(我们以一个计算机的启动过程为例) 我们先看下实现类: [java] view plaincopy public class CPU { public void startup(){ System.out.println("cpu startup!"); } public vo

Java设计模式系列之命令模式

命令模式(Command)的定义 将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化:对请求排队或记录日志,以及支持可撤销的操作,将”发出请求的对象”和”接收与执行这些请求的对象”分隔开来. 命令模式(Command)的适用性 1.抽象出待执行的动作以参数化某对象. 2.在不同的时刻指定.排列和执行请求. 3.支持取消操作. 4.支持修改日志,这样当系统崩溃时,这样修改可以被重做一遍. 5.用构建在原语操作上的高层操作构造一个系统. 命令模式(Command)的应用效果:1)comma