设计模式-组合模式

时间:2022-07-25
本文章向大家介绍设计模式-组合模式,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

背景

世界分为7大洲,每个国家,一般只属一个洲(像俄罗斯这种除外哈),这样的话就构建成级联关系,一般世界上任何某处地区都所属某个国家,而这个国家又所属某个洲,组合模式是就是将地区(基本对象)和组合对象(国家、洲)不断的去组合成更复杂的的对象。将所有的地区组成树型结构;

组合模式是什么?

组合模式(Composite Pattern),又叫部分整体模式,也有叫合成模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。组合模式分为安全模式和透明模式。

组合模式的安全性:从客户使用组合模式上看是否更安全。如果是安全的,那么就不会有发生误操作的可能,能访问的方法都是被支持的功能。

组合模式的透明性:从客户使用组合模式上看是否需要区分到底是组合对象还是叶子对象。如果是透明的,那就不用再区分,对于客户而言,都是组件对象,具体的类型对于客户而言是透明的,是客户无须关心的。

包含的角色有:

抽象组件:(Component)角色:这是一个抽象角色,它给参加组合的对象规定一个接口。这个角色给出共有的接口及其默认行为。

叶子对象:(Leaf)角色:代表参加组合的树叶对象。一个树叶没有下级的子对象。定义出参加组合的原始对象的行为。

容器对象:(Composite)角色:代表参加组合的有子对象的对象,并给出树枝构件对象行为。

组合模式可以干嘛?

该模式主要是解决树型结构遍历和递归处理的问题;

优点:

易拓展:节点可以自由增加,遵循了开闭原则;

统一了组合对象和叶子对象,子节点和父节点统一类型;

缺点:

破坏了单一职责,又获取父节点又打印子结点破坏了单一职责;

组合模式类图

源码下载:https://gitee.com/hong99/design-model/issues/I1IMES

实现代码

  • 安全模式
/**
 * @Auther: csh
 * @Date: 2020/5/25 18:08
 * @Description:抽象的位置(抽象构件)
 */
public interface IPosition {
    /**
     *
     * 功能描述:输出位置名称
     *
     * @param: 
     * @return: 
     * @auther: csh
     * @date: 2020/5/25 18:09
     */
    void printPositionName(String preStr);
}
/**
 * @Auther: csh
 * @Date: 2020/5/25 18:13
 * @Description:洲
 */
public class Continent implements IPosition {
    /**
     *
     * 功能描述:各地方
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/5/25 18:14
     */
    private List<IPosition>  childPosition = new ArrayList <IPosition>();
    /**
     *
     * 功能描述:地方名称
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/5/25 18:14
     */
    private String name;

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

    public String getName() {
        return name;
    }
    /**
     *
     * 功能描述:添加
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/5/25 18:16
     */
    public void addChild(IPosition child){
        childPosition.add(child);
    }
    /**
     *
     * 功能描述:删除
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/5/25 18:16
     */
    public void removeChild(IPosition position){
        childPosition.remove(position);
    }
    /**
     *
     * 功能描述:返回列表
     *
     * @param: 
     * @return: 
     * @auther: csh
     * @date: 2020/5/25 18:16
     */
    public List<IPosition> getChild(){
        return childPosition;
    }



    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void printPositionName(String preStr) {
        //当前的位置
        System.out.println(preStr+"+"+this.name);
        //获取包含的地区
        if(null!=childPosition && childPosition.size()>0){
            preStr+=" ";
            for (IPosition iPosition : childPosition) {
                iPosition.printPositionName(preStr);
            }
        }
    }
}
/**
 * @Auther: csh
 * @Date: 2020/5/25 18:33
 * @Description:国家
 */
public class Country implements IPosition {

    /**
     *
     * 功能描述:地方名称
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/5/25 18:14
     */
    private String name;

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

    @Override
    public void printPositionName(String preStr) {
        System.out.println(preStr + "-" + name);
    }
}
/**
 * @Auther: csh
 * @Date: 2020/5/25 18:36
 * @Description:演示  (安全模式)
 */
public class Client {
    public static void main(String[] args) {
        Continent earth = new Continent("地球");
        Continent asian = new Continent("亚洲");
        Continent america = new Continent("美洲");
        Continent africa = new Continent("非洲");


        Country china = new Country("中国");
        asian.addChild(china);
        Country american = new Country("美国");
        america.addChild(american);
        Country cameroon = new Country("喀麦隆");
        africa.addChild(cameroon);

        earth.addChild(asian);
        earth.addChild(america);
        earth.addChild(africa);

        earth.printPositionName("");
        
    }
}

结果

+地球
 +亚洲
  -中国
 +美洲
  -美国
 +非洲
  -喀麦隆

透明模式

/**
 * @Auther: csh
 * @Date: 2020/5/25 18:13
 * @Description:抽象的地方(组件)
 */
public abstract class IPosition  {

    /**
     *
     * 功能描述:打印位置名称
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/5/25 18:58
     */
    public abstract void printPositionName(String preStr);
    /**
     *
     * 功能描述: 添加子类
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/5/26 9:38
     */
    public  void addChild(IPosition child){
        throw  new UnsupportedOperationException("对象不支持此功能");
    }
    /**
     *
     * 功能描述:删除子类
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/5/26 9:38
     */
    public  void removeChild(IPosition child){
        throw  new UnsupportedOperationException("对象不支持此功能");
    }
    /**
     *
     * 功能描述:获取所有子类
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/5/26 9:40
     */
    public List<IPosition> getChild(){
        throw  new UnsupportedOperationException("对象不支持此功能");
    }


}
/**
 * @Auther: csh
 * @Date: 2020/5/26 09:41
 * @Description:洲
 */
public class Continent extends IPosition {

    /**
     *
     * 功能描述:子类集合
     *
     * @param: 
     * @return: 
     * @auther: csh
     * @date: 2020/5/26 9:42
     */
    private List<IPosition> childList = new ArrayList <IPosition>();

    /**
     *
     * 功能描述:对象名称
     *
     * @param: 
     * @return: 
     * @auther: csh
     * @date: 2020/5/26 9:42
     */
    private String name;

    @Override
    public void printPositionName(String preStr) {
        //当前的位置
        System.out.println(preStr+"+"+this.name);
        //获取包含的地区
        if(null!=childList && childList.size()>0){
            preStr+=" ";
            for (IPosition iPosition : childList) {
                iPosition.printPositionName(preStr);
            }
        }
    }


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

    @Override
    public void removeChild(IPosition child) {
        childList.remove(child);
    }

    @Override
    public void addChild(IPosition child){
        childList.add(child);
    }

    @Override
    public List <IPosition> getChild() {
        return childList;
    }
}
/**
 * @Auther: csh
 * @Date: 2020/5/26 10:23
 * @Description:国家
 */
public class Country extends IPosition{

    private String name;

    public Country(String name) {
        this.name = name;
    }
    
    @Override
    public void printPositionName(String preStr) {
        System.out.println(preStr+"-"+name);
    }
}
/**
 * @Auther: csh
 * @Date: 2020/5/26 10:25
 * @Description:透明模式
 */
public class Client {

    public static void main(String[] args) {
        Continent earth = new Continent("地球");
        Continent america = new Continent("美洲");
        Continent asian = new Continent("亚洲");
        Continent africa = new Continent("非洲");

        Country china = new Country("中国");
        asian.addChild(china);
        Country american = new Country("美国");
        america.addChild(american);
        Country cameroon = new Country("喀麦隆");
        africa.addChild(cameroon);

        earth.addChild(asian);
        earth.addChild(america);
        earth.addChild(africa);

        earth.printPositionName("");

    }
}
+地球
 +亚洲
  -中国
 +美洲
  -美国
 +非洲
  -喀麦隆

源码下载:https://gitee.com/hong99/design-model/issues/I1IMES

最后

实际使用过程中,透明模式使用得比较多,但是建议在使用该模式的时候加上缓存 redis或者本地缓存,这样的效率更高,没必要每次都去遍历,这样太浪费性能了,而且如需要遍历,建议使用jdk8的新特性来处理。至于本文主要都是从上到下的模式,其他还有从下到上的查询以及同级别的查找,这里就不一一列举了,熟悉本文后可以灵活变通。