设计模式-组合模式

1. 组合模式概念

组合模式(Composite Pattern)也称为整体-部分(Part-Whole)模式,它的宗旨是通过将单个 对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得客户对单个对象和组合对象的 使用具有一致性,属于结构型模式

  • 组合关系与聚合关系的区别:

组合关系:胳膊和身体的关系,胳膊不能脱离身体而独立存在,胳膊只能算是身体的一部分(具有相同的生命周期)。
聚合关系:一个老师有很多学生,但是每一个学生又属于多个老师(具有不同的生命周期)。

&emps;&emps;组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,最顶层的节点称为根节点, 根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点。如下图所示:
在这里插入图片描述

由上图可以看出,其实根节点和树枝节点本质上是同一种数据类型,可以作为容器使用;而叶子节 点与树枝节点在语义上不属于同一种类型,但是在组合模式中,会把树枝节点和叶子节点认为是同一种 数据类型(用同一接口定义),让它们具备一致行为。这样,在组合模式中,整个树形结构中的对象都 是同一种类型,带来的一个好处就是客户无需辨别树枝节点还是叶子节点,而是可以直接进行操作,给客户使用带来极大的便利。

-组合模式包含 的角色

  • 抽象根节点(Component)

定义系统各层次对象的共有方法和属性,可以预先定义一些默认 行为和属性;

  • 树枝节点(Composite)

定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成 一个树形结构;

  • 叶子节点(Leaf)

叶子节点对象,其下再无分支,是系统层次遍历的最小单位。 组合模式 在代码具体实现上,有两种不同的方式,分别是透明组合模式和安全组合模式

2. 组合模式应用场景

组合模式主要总结为以下应用场景:
1、希望客户端可以忽略组合对象与单个对象的差异时;
2、对象层次具备整体和部分,呈树形结构。 在我们生活中的组合模式也非常常见,比如树形菜单,操作系统目录结构,公司组织架构等

  当子系统与其内各个对象层次呈现树形结构时,可以使用组合模式让子系统内各个对象层次的行为 操作具备一致性。客户端使用该子系统内任意一个层次对象时,无须进行区分,直接使用通用操作即可,为客户端的使用带来了便捷。

  如果树形结构系统不使用组合模式进行架构,那么按照正常的思维逻辑,对该系统进行职责分析,按上文树形结 构图所示,该系统具备两种对象层次类型:树枝节点和叶子节点。那么我们就需要构造两种对应的类型,然后由于树 枝节点具备容器功能,因此树枝节点类内部需维护多个集合存储其他对象层次(如:List,List), 如果当前系统对象层次更复杂时,那么树枝节点内就又要增加对应的层次集合,这对树枝节点的构建带来了巨大的复 杂性,臃肿性以及不可扩展性。同时客户端访问该系统层次时,还需进行层次区分,这样才能使用对应的行为,给客 户端的使用也带来了巨大的复杂性。而如果使用组合模式构建该系统,由于组合模式抽取了系统各个层次的共性行为, 具体层次只需按需实现所需行为即可,这样子系统各个层次就都属于同一种类型,所以树枝节点只需维护一个集合 (List)即可存储系统所有层次内容,并且客户端也无需区分该系统各个层次对象,对内系统架构简洁优 雅,对外接口精简易用。

3.组合模式透明写法

透明组合模式是把所有公共方法都定义在 Component 中,这样做的好处是客户端无需分辨是叶子 节点(Leaf)和树枝节点(Composite),它们具备完全一致的接口。

  • 抽象根节点:
package com.gupaoedu.vip.pattern.composite.general.transparent;
/**
 * 抽象根节点;抽取了系统各个层次的共性行为,把根节点和子节点的相同的行为取出来
 */
public abstract class Component {

    protected String name;
    public Component(String name){
        this.name = name;
    }
    // 各个叶子节点自己的特性,定义为抽象方法,由叶子节点自行实现
    public abstract String operation();

    // 重要:addChild的入参一定是抽象根节点Component,这样就可以实现根节点添加树节点,树节点添加叶子节点,层层嵌套添加!
    /*叶子节点显然不具备addChild的能力,但是这里也进行了空实现,子类可以去实现,
       但是调用的时候又不让调,是一种不安全的写法,违背了最少知道原则*/
    public boolean addChild(Component component) {
        throw new UnsupportedOperationException("addChild not supported!");
    }

    public boolean removeChild(Component component){
        throw new UnsupportedOperationException("removeChild not supported!");
    }

    public Component getCild(int index) {
        throw new UnsupportedOperationException("getChild not supported!");
    }
}

组合模式把所有可能用到的方法都定义到这个最顶层的抽象类中,但是不写任何逻辑处理的代码,而是直接 抛异常。这里为什么不用抽象方法?因为,用了抽象方法,其子类就必须实现, 这样便体现不出各子类的细微差异。因此,子类继承此抽象类后,只需要重写有差异的方法覆盖父类的方法即可。

  • 树枝节点
package com.gupaoedu.vip.pattern.composite.general.transparent;

import java.util.ArrayList;
import java.util.List;

// 树节点
public class Composite extends Component {

    //树枝节点只需维护一个抽象根节点的集合 (List<Component>)即可存储系统所有层次内容
    private List<Component> components;

    public Composite(String name) {
        super(name);
        this.components = new ArrayList<Component>();
    }

    // 节点的具体行为
    @Override
    public String operation() {
        StringBuilder builder = new StringBuilder(this.name);
        for (Component component : components) {
            builder.append("\n").append(component.operation());
        }
        return builder.toString();
    }

    @Override
    public boolean addChild(Component component) {
        return this.components.add(component);
    }

    // 移除操作是将components中的元素移除
    @Override
    public boolean removeChild(Component component) {
        return this.components.remove(component);
    }

    @Override
    public Component getCild(int index) {
        return this.components.get(index);
    }
}
  • 叶子节点:
package com.gupaoedu.vip.pattern.composite.general.transparent;

/**
 * 叶子节点
 * 不管是叶子节点,还是根节点,都继承抽象根节点Component
 */
public class Leaf extends Component {
    public Leaf(String name) {
        super(name);
    }

    @Override
    public String operation() {
        return this.name;
    }

    @Override
    public boolean addChild(Component component) {
        return super.addChild(component);
    }

    @Override
    public boolean removeChild(Component component) {
        return super.removeChild(component);
    }

    @Override
    public Component getCild(int index) {
        return super.getCild(index);
    }
}
  • 客户端调用
package com.gupaoedu.vip.pattern.composite.general.transparent;
public class Test {
    public static void main(String[] args) {
        // 创建一个根节点
        Component root = new Composite("root");
        //  创建两个树枝节点
        Component branchA = new Composite("--BranchA");
        Component branchB = new Composite("----BranchB");
        // 创建三个叶子节点
        Component leafA = new Leaf("----leafA");
        Component leafB = new Leaf("------leafB");
        Component leafC = new Leaf("--leafC");

        root.addChild(branchA);
        root.addChild(leafC);
        branchA.addChild(leafA);
        branchA.addChild(branchB);
        branchB.addChild(leafB);
        String result = root.operation();
        System.out.println(result);
    }
}

UML类图如下:
在这里插入图片描述

透明组合模式把所有公共方法都定义在 Component 中,这样做的好处是客户端无需分辨是叶子节点(Leaf)和树枝节点(Composite),它们具备完全一致的接口;缺点是叶子节点(Leaf)会继承 得到一些它所不需要(管理子类操作的方法)的方法,这与设计模式接口隔离原则相违背。

4.组合模式安全写法

安全组合模式是只规定系统各个层次的最基础的一致行为,而把组合(树节点)本身的方法(管理 子类对象的添加,删除等)放到自身当中。
-抽象根节点:

package com.gupaoedu.vip.pattern.composite.general.safe;
/**
 * 抽象根节点;抽取了系统各个层次的共性行为,把根节点和子节点的相同的行为取出来
 */
public abstract class Component {

    protected String name;
    public Component(String name){
        this.name = name;
    }
    // 各个叶子节点自己的特性,定义为抽象方法,由叶子节点自行实现
    public abstract String operation();

//    public boolean addChild(Component component) {
//        throw new UnsupportedOperationException("addChild not supported!");
//    }
//
//    public boolean removeChild(Component component){
//        throw new UnsupportedOperationException("removeChild not supported!");
//    }
//
//    public Component getCild(int index) {
//        throw new UnsupportedOperationException("getChild not supported!");
//    }
}
  • 树枝节点

1.树枝节点要继承抽象根节点Component
2.树枝节点只需维护一个抽象根节点的集合 (List)即可存储系统所有层次内容
3.只有树枝节点才有addChild removeChild getCild等操作,叶子节点不具备,因此抽象根节点中去除这三个方法

package com.gupaoedu.vip.pattern.composite.general.safe;

import java.util.ArrayList;
import java.util.List;

// 树节点
public class Composite extends Component {

    //树枝节点只需维护一个抽象根节点的集合 (List<Component>)即可存储系统所有层次内容
    private List<Component> components;

    public Composite(String name) {
        super(name);
        this.components = new ArrayList<Component>();
    }

    // 节点的具体行为
    @Override
    public String operation() {
        StringBuilder builder = new StringBuilder(this.name);
        for (Component component : components) {
            builder.append("\n").append(component.operation());
        }
        return builder.toString();
    }

    public boolean addChild(Component component) {
        return this.components.add(component);
    }
    // 移除操作是将components中的元素移除
    public boolean removeChild(Component component) {
        return this.components.remove(component);
    }
    public Component getCild(int index) {
        return this.components.get(index);
    }
}
  • 叶子节点
package com.gupaoedu.vip.pattern.composite.general.safe;

/**
 * 叶子节点
 * 不管是叶子节点,还是根节点,都继承抽象根节点Component
 */
public class Leaf extends Component {
    public Leaf(String name) {
        super(name);
    }

    //  这里继承的时候只会重写operation
    @Override
    public String operation() {
        return this.name;
    }
}

UML类图:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值