通俗易懂的java设计模式之组合模式

首先它强调的是结构,结构图如下。

通过上图,可以看到它的结构有3部分组成,和现实当中的大树结构很类似。它包括有Root根节点(相当于大树主干)、树枝节点和叶子节点。根节点连接着树枝节点和叶子节点,而叶子节点不能在连接其他节点

想象一颗大树是如何将营养供给给叶子的,它们是通过主干延伸的树枝最终作用到了叶子上,最终让叶子成长起来。树枝建立了树干和叶子之间的通道,并起到了单向传输的作用,而且能确保给到大树的每一个叶子上

那好,我问你一个树干上有多少树枝,很简单你会数一下从树干上延伸了多少树枝,告诉我答案。我再问你一个树枝上有多少叶子,也很简单通过递归的方式也能得到最终的结果。最后我问你,让你通过程序模拟大树结构,并实现营养从树干传到叶子,你有想法了吗?先不要往下看,请先认真的想一想。

......

接下来,我们先看看该设计模式需要用到哪些对象,这点很重要。

Component(Composite和Leaf共同的抽象类)

Composite (主干和树枝的具体实现类)

Leaf (叶子的具体实现类)

通过这3个对象我们已经可以很好的实现一把了,代码如下:

java 复制代码
import java.util.ArrayList;
import java.util.List;

//透明组合模式
public class ComponentLucency {
    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.provide("营养");
        System.out.println(result);
    }
}
// 抽象根节点
abstract class Component {
    protected String name;

    public Component(String name) {
        this.name = name;
    }

    public abstract String provide(String matter);

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

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

    public Component getChild(int index) {
        throw new UnsupportedOperationException("getChild not supported!");
    }
}
// 树节点
class Composite extends Component {
    private List<Component> mComponents;

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

    @Override
    public String provide(String matter) {
        StringBuilder builder = new StringBuilder(this.name);
        for (Component component : this.mComponents) {
            builder.append("\n");
            builder.append(component.provide(matter));
        }
        return builder.toString();
    }

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

    @Override
    public boolean removeChild(Component component) {
        return this.mComponents.remove(component);
    }

    @Override
    public Component getChild(int index) {
        return this.mComponents.get(index);
    }
}
//叶子节点
class Leaf extends Component {

    public Leaf(String name) {
        super(name);
    }

    @Override
    public String provide(String matter) {
        return this.name+"得到了"+matter;
    }
}

最终控制台输出效果如下:

css 复制代码
root
---branchA
------leafA得到了营养
------branchB
---------leafB得到了营养
---leafC得到了营养

可以看到所有叶子都得到了营养,最终实现了目标。

上边的代码真的没有问题了吗,细心的你发现到了一个细节。

Leaf节点继承了Component抽象类,却没有实现他的addChild...其他抽象方法,那么在leaf调用的时候,可能不小心就调用了这些没有实现的抽象方法,最终抛出异常,而这种实现方式可以称为透明组合模式

那该方法如何避免呢,请看下列代码:

java 复制代码
package design.component;

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

//安全组合模式
public class ComponentSafeMode {

    public static void main(String[] args) {
        // 来一个根节点
        CompositeSafe root = new CompositeSafe("root");
        // 来一个树枝节点
        CompositeSafe branchA = new CompositeSafe("---branchA");
        CompositeSafe branchB = new CompositeSafe("------branchB");
        // 来一个叶子节点
        ComponentSafe leafA = new LeafSafe("------leafA");
        ComponentSafe leafB = new LeafSafe("---------leafB");
        ComponentSafe leafC = new LeafSafe("---leafC");

        root.addChild(branchA);
        root.addChild(leafC);
        branchA.addChild(leafA);
        branchA.addChild(branchB);
        branchB.addChild(leafB);

        String result = root.provide("营养");
        System.out.println(result);
    }
}
// 抽象根节点
abstract class ComponentSafe {
    protected String name;

    public ComponentSafe(String name) {
        this.name = name;
    }

    public abstract String provide(String matter);
}

// 树节点
class CompositeSafe extends ComponentSafe {
    private List<ComponentSafe> mComponents;

    public CompositeSafe(String name) {
        super(name);
        this.mComponents = new ArrayList<ComponentSafe>();
    }

    @Override
    public String provide(String matter) {
        StringBuilder builder = new StringBuilder(this.name);
        for (ComponentSafe component : this.mComponents) {
            builder.append("\n");
            builder.append(component.provide(matter));
        }
        return builder.toString();
    }


    public boolean addChild(ComponentSafe component) {
        return this.mComponents.add(component);
    }


    public boolean removeChild(ComponentSafe component) {
        return this.mComponents.remove(component);
    }


    public ComponentSafe getChild(int index) {
        return this.mComponents.get(index);
    }

}

//叶子节点
class LeafSafe extends ComponentSafe {

    public LeafSafe(String name) {
        super(name);
    }

    @Override
    public String provide(String matter) {
        return this.name+"得到了"+matter;
    }
}

上述实现可以看到Component抽象类原本的addChild...方法移到了Composite具体实现类中,也就避免了刚才那个问题,而这种模式称为安全组合模式。

那你可能就要问了这种模式它有什么优缺点呢,实际应用场景是怎样的,源码的体现呢。

好处显而易见就是结构稳定,保证传输没有问题,而最大的缺点就是概念比较抽象不易理解和维护,而且调用链不易过深。

实际应用中,比如维护一个目录结构,我们是不是就可以采用这种方式。

至于源码的体现呢,想想Mybatis映射文件中是否存在很多的动态标签,它们是不是也可以使用该设计模式。而事实上Mybatis就是这样做的,如下图所示:

结束!

相关推荐
小小李程序员2 小时前
LRU缓存
java·spring·缓存
cnsxjean2 小时前
SpringBoot集成Minio实现上传凭证、分片上传、秒传和断点续传
java·前端·spring boot·分布式·后端·中间件·架构
hadage2332 小时前
--- stream 数据流 java ---
java·开发语言
《源码好优多》3 小时前
基于Java Springboot汽配销售管理系统
java·开发语言·spring boot
小林想被监督学习3 小时前
Java后端如何进行文件上传和下载 —— 本地版
java·开发语言
Erosion20203 小时前
SPI机制
java·java sec
逸风尊者4 小时前
开发也能看懂的大模型:RNN
java·后端·算法
尘浮生4 小时前
Java项目实战II基于Java+Spring Boot+MySQL的智能停车计费系统(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·mysql·微信小程序·maven
frost-cold4 小时前
【JavaEE】Servlet:表白墙
java·servlet·java-ee
那年星空4 小时前
Flutter 设计模式全面解析:抽象工厂
flutter·设计模式·架构