【java设计模式】组合模式(结构型模式)

在这里插入图片描述

简单理解组合模式


大部分国家的军队都采用层次结构管理。每支部队包括几个师,师由旅构成,旅由团构成,团可以继续划分为排。最后,每个排由一小队实实在在的士兵组成。军事命令由最高层下达,通过每个层级传递,直到每位士兵都知道自己应该服从的命令。

组合模式是一种结构型设计模式,你可以使用它将对象组合成树状结构,并且能像使用独立对象一样使用它们。

问题
如果应用的核心模型能用树状结构表示,在应用中使用组合模式才有价值。
例如,你有两类对象:​产品和盒子。一个盒子中可以包含多个产品或者几个较小的盒子。这些小盒子中同样可以包含一些产品或更小的盒子,以此类推。
假设你希望在这些类的基础上开发一个定购系统。订单中可以包含无包装的简单产品,也可以包含装满产品的盒子……以及其他盒子。此时你会如何计算每张订单的总价格呢?

在这里插入图片描述

订单中可能包括各种产品, 这些产品放置在盒子中, 然后又被放入一层又一层更大的盒子中。 整个结构看上去像是一棵倒过来的树。

你可以尝试直接计算:打开所有盒子,找到每件产品,然后计算总价。这在真实世界中或许可行,但在程序中,你并不能简单地使用循环语句来完成该工作。你必须事先知道所有产品和盒子的类别,所有盒子的嵌套层数以及其他繁杂的细节信息。因此,直接计算极不方便,甚至完全不可行。

解决方案
组合模式建议使用一个通用接口来与产品和盒子进行交互,并且在该接口中声明一个计算总价的方法。

那么方法该如何设计呢?对于一个产品,该方法直接返回其价格;对于一个盒子,该方法遍历盒子中的所有项目,询问每个项目的价格,然后返回该盒子的总价格。如果其中某个项目是小一号的盒子,那么当前盒子也会遍历其中的所有项目,以此类推,直到计算出所有内部组成部分的价格。你甚至可以在盒子的最终价格中增加额外费用,作为该盒子的包装费用。

在这里插入图片描述

组合模式以递归方式处理对象树中的所有项目

该方式的最大优点在于你无需了解构成树状结构的对象的具体类。你也无需了解对象是简单的产品还是复杂的盒子。你只需调用通用接口以相同的方式对其进行处理即可。当你调用该方法后,对象会将请求沿着树结构传递下去。
例子原文

结构


成员

  • Component(组合对象):为组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为,声明用于访问和管理其子组件的接口
  • Leaf(叶子对象):定义组合中原始对象的行为,叶子节点下再无节点
  • Composite(容器对象):定义所有节点行为,存储子组件,在Component接口中实现与子组件有关操作,如增加(add)和删除(remove)等

安全模式

UML

在这里插入图片描述

具体实现

抽象构件Component
public abstract class Component {
    /**
     * 个体和整体都具有
     */
    public void operation(){
        //编写业务逻辑
    }
}
树枝构件Composite
@SuppressWarnings("all")
public class Composite extends Component{
    /**
     * 构件容器
     */
    private List<Component> componentArrayList = new ArrayList<Component>();
    /**
     * 增加一个叶子构件或树枝构件
     */
    public void add(Component component){
        this.componentArrayList.add(component);
    }
    /**
     * 删除一个叶子构件或树枝构件
     */
    public void remove(Component component){
        this.componentArrayList.remove(component);
    }
    /**
     * 获得分支下的所有叶子构件和树枝构件
     */
    public List<Component> getChildren(){
        return this.componentArrayList;
    }
}
树叶构件Leaf
public class Leaf extends Component{
    /**
     * 可以覆写父类方法
     */
    public void operation(){
    }
}
Client
@SuppressWarnings("all")
public class Client {
    public static void main(String[] args) {
        //创建一个根节点
        Composite root = new Composite();
        root.operation();
        //创建一个树枝构件
        Composite branch = new Composite();
        //创建一个叶子节点
        Leaf leaf = new Leaf();
        //建立整体
        root.add(branch);
        branch.add(leaf);
    }

    /**
     * 通过递归遍历树
     */
    public static void showTree(Composite root){
        for(Component c:root.getChildren()){
            if(c instanceof Leaf){ //叶子节点
                c.operation();
            }else{ //树枝节点
                showTree((Composite)c);
            }
        }
    }
}

透明模式

UML

在这里插入图片描述

具体实现

抽象构件Component
public abstract class Component {
    /**
     * 个体和整体都具有
     */
    public void operation(){
        //编写业务逻辑
    }
    /**
     * 增加一个叶子构件或树枝构件
     */
    public abstract void add(Component component);
    /**
     * 删除一个叶子构件或树枝构件
     */
    public abstract void remove(Component component);
    /**
     * 获得分支下的所有叶子构件和树枝构件
     */
    public abstract List<Component> getChildren();
}
树枝构件Composite
public class Composite extends Component{
    /**
     * 构件容器
     */
    private ArrayList<Component> componentArrayList = new ArrayList<Component>();
    /**
     * 增加一个叶子构件或树枝构件
     */
    public void add(Component component){
        this.componentArrayList.add(component);
    }
    /**
     * 删除一个叶子构件或树枝构件
     */
    public void remove(Component component){
        this.componentArrayList.remove(component);
    }
    /**
     * 获得分支下的所有叶子构件和树枝构件
     */
    public List<Component> getChildren(){
        return this.componentArrayList;
    }
}
树叶构件Leaf
public class Leaf extends Component {
    public void add(Component component) {
        //空实现
    }

    public void remove(Component component) {
        //空实现
    }

    public List<Component> getChildren() {
        //空实现
        return null;
    }
}
Client
@SuppressWarnings("all")
public class Client {
    public static void main(String[] args) {
        //创建一个根节点
        Composite root = new Composite();
        root.operation();
        //创建一个树枝构件
        Composite branch = new Composite();
        //创建一个叶子节点
        Leaf leaf = new Leaf();
        //建立整体
        root.add(branch);
        branch.add(leaf);
    }

    /**
     * 通过递归遍历树
     */
    public static void showTree(Component root){
        for(Component c:root.getChildren()){
            if(c instanceof Leaf){ //叶子节点
                c.operation();
            }else{ //树枝节点
                showTree(c);
            }
        }
    }
}

区别

  • 安全模式在抽象组件中只定义一些默认的行为或属性,它是把树枝节点和树叶节点彻底分开;透明模式是把用来组合使用的方法放到抽象类中,不管叶子对象还是树枝对象都有相同的结构,通过判断确认是叶子节点还是树枝节点,如果处理不当,这个会在运行期出现问题,不是很建议的方式。
  • 安全模式与依赖倒置原则冲突;透明模式的好处就是它基本遵循了依赖倒转原则,方便系统进行扩展。
  • 安全模式在遍历树形结构的的时候需要进行强制类型转换;在透明模式下,遍历整个树形结构是比较容易的,不用进行强制类型转换。

实现代码


某一天你的手机掉进马桶了,不能用了,这时候你的女朋友正好送你了一款最新的5G手机作为礼物。手机的包装盒一般是这样的
在这里插入图片描述

安全模式

抽象构件

public abstract class Component {
    private String desc;
    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public abstract void display(int index);
}

树枝构件

public class Box extends Component {
    private List<Component> components = new ArrayList<>();

    public Box(String desc) {
        this.setDesc(desc);
    }

    public void add(Component component) {
        this.components.add(component);
    }

    public void remove(Component component) {
        this.components.remove(component);
    }

    @Override
    public void display(int index) {
        for (int i = 0; i < index; i++) {
            System.out.print("———");
        }
        System.out.println(this.getDesc());
        this.components.forEach(c -> {
            c.display(index + 1);
        });
    }
}

树叶构件

public class Gift extends Component {
    public Gift(String desc) {
        this.setDesc(desc);
    }

    @Override
    public void display(int index) {
        for (int i = 0; i < index; i++) {
            System.out.print("———");
        }
        System.out.println(this.getDesc());
    }
}

Client

public class Client {
    public static void main(String[] args) {
        Box box1 = new Box("大盒子");
        Box box2 = new Box("中盒子");
        Box box3 = new Box("小盒子1");
        Box box4 = new Box("小盒子2");

        Gift gift1 = new Gift("手机");
        Gift gift2 = new Gift("充电器");
        Gift gift3 = new Gift("耳机");
        
        /**
         * 把手机和中盒子装进大盒子
         * 把两个小盒子装进中盒子
         * 把耳机和充电器分别装进两个小盒子
         */
        box1.add(gift1);
        box1.add(box2);
        box2.add(box3);
        box2.add(box4);
        box3.add(gift2);
        box4.add(gift3);

        box1.display(0);
    }
}

输出

大盒子
———手机
———中盒子
——————小盒子1
—————————充电器
——————小盒子2
—————————耳机

透明模式

抽象构件

public abstract class Component {
    private String desc;
    private List<Component> components = new ArrayList<>();

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public List<Component> getComponents() {
        return components;
    }

    public void setComponents(List<Component> components) {
        this.components = components;
    }

    public abstract void add(Component component);

    public abstract void remove(Component component);

    public abstract void display(int index);
}

树枝构件

public class Box extends Component {
    public Box(String desc) {
        this.setDesc(desc);
    }

    @Override
    public void add(Component component) {
        this.getComponents().add(component);
    }

    @Override
    public void remove(Component component) {
        this.getComponents().remove(component);
    }

    @Override
    public void display(int index) {
        for (int i = 0; i < index; i++) {
            System.out.print("———");
        }
        System.out.println(this.getDesc());
        this.getComponents().forEach(c -> {
            c.display(index + 1);
        });
    }
}

树叶构件

public class Gift extends Component {
    public Gift(String desc) {
        this.setDesc(desc);
    }

    @Override
    public void add(Component component) {
        // 空实现
    }

    @Override
    public void remove(Component component) {
        // 空实现
    }

    @Override
    public void display(int index) {
        for (int i = 0; i < index; i++) {
            System.out.print("———");
        }
        System.out.println(this.getDesc());
    }
}

Client

public class Client {
    public static void main(String[] args) {
        Box box1 = new Box("大盒子");
        Box box2 = new Box("中盒子");
        Box box3 = new Box("小盒子1");
        Box box4 = new Box("小盒子2");

        Gift gift1 = new Gift("手机");
        Gift gift2 = new Gift("充电器");
        Gift gift3 = new Gift("耳机");

        /**
         * 把手机和中盒子装进大盒子
         * 把两个小盒子装进中盒子
         * 把耳机和充电器分别装进两个小盒子
         */
        box1.add(gift1);
        box1.add(box2);
        box2.add(box3);
        box2.add(box4);
        box3.add(gift2);
        box4.add(gift3);

        box1.display(0);
    }
}

输出

大盒子
———手机
———中盒子
——————小盒子1
—————————充电器
——————小盒子2
—————————耳机
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值