【设计模式】组合模式

  |   0 评论   |   107 浏览

简介

将对象组合成树形结构以表示部分-整体的层次结构,使客户端对单个对象和组合对象保持一致的方式处理,如果你要处理树形结构的数据时,程序总是会写的很复杂,你必须要区分哪个是叶子节点,哪个是分支,使用组合模式,可以简化复杂,让程序更容易扩展。定义一个统一的Component接口,所有组合模式的类都实现该接口 ,Leaf对象直接实现Component接口,Composite对象将请求转发给其子组件,该模式 属于结构型。
树形如图:
image.png

适用场景

1、希望客户端忽略组合对象与单个对象的差异时
2、处理一个树形结构时

优点

1、清楚的定义分层次的复杂对象,表示对象的全部或部分层次
2、让客户端忽略了层次的差异,方便对整个层次结构进行控制
3、简化客户端代码
4、符合开闭原则

缺点

1、限制类型时会比较复杂
2、使设计变得更加抽象

代码示例

背景说明:菜谱有目录和具体菜名,目录下面包含一些菜名,菜的话有名称和价格,目录有名称,可以添加、删除某些菜。
image.png
目录和菜名的公共抽象类

public abstract class CatalogComponent {
    public String getName(CatalogComponent catalogComponent){
        throw new UnsupportedOperationException("不支持获取名称");
    }
    public void add(CatalogComponent catalogComponent){
        throw new UnsupportedOperationException("不支持添加");
    }
    public void remove(CatalogComponent catalogComponent){
        throw new UnsupportedOperationException("不支持移除");
    }
    public double getPrice(CatalogComponent catalogComponent){
        throw new UnsupportedOperationException("不支持获取价格");
    }
    public void show(){
        throw new UnsupportedOperationException("不支持展示");
    }
}

食物类,继承了CatalogComponent对象

public class Food extends CatalogComponent {
    private String name;
    private double price;
    public Food(String name, double price) {
        this.name = name;
        this.price = price;
    }
    @Override
    public String getName(CatalogComponent catalogComponent) {
        return this.name;
    }
    @Override
    public double getPrice(CatalogComponent catalogComponent) {
        return this.price;
    }
    @Override
    public void show() {
        System.out.println("食物名称:"+this.name+";价格"+this.price);
    }
}

菜谱目录类,也继承了CatalogComponent类

public class MenuCatalog extends CatalogComponent{
    private String name;
    private List<CatalogComponent> items = new ArrayList<CatalogComponent>();
    public MenuCatalog(String name) {
        this.name = name;
    }
    @Override
    public String getName(CatalogComponent catalogComponent) {
        return this.name;
    }
    @Override
    public void add(CatalogComponent catalogComponent) {
        items.add(catalogComponent);
    }
    @Override
    public void remove(CatalogComponent catalogComponent) {
        items.remove(catalogComponent);
    }
    @Override
    public void show() {
        System.out.println("菜单目录:"+this.name);
        for(CatalogComponent catalogComponent : items){
            catalogComponent.show();
        }
    }
}

测试类

public class CompositeTest {
    public static void main(String[] args) {
        CatalogComponent food = new Food("红烧肉",20);
        CatalogComponent food2 = new Food("红烧鸡",15);
        CatalogComponent meatCatalog = new MenuCatalog("荤菜系列");
        meatCatalog.add(food);
        meatCatalog.add(food2);

        CatalogComponent food3 = new Food("炒青菜",2);
        CatalogComponent food4 = new Food("干锅花菜",3);
        CatalogComponent soupCatalog = new MenuCatalog("蔬菜系列");
        soupCatalog.add(food3);
        soupCatalog.add(food4);

        CatalogComponent mainCatalog = new MenuCatalog("主菜单目录");
        mainCatalog.add(meatCatalog);
        mainCatalog.add(soupCatalog);
        mainCatalog.show();
    }
}

输出结果

菜单目录:主菜单目录
菜单目录:荤菜系列
食物名称:红烧肉;价格20.0
食物名称:红烧鸡;价格15.0
菜单目录:蔬菜系列
食物名称:炒青菜;价格2.0
食物名称:干锅花菜;价格3.0

上面示例源码

源码分析

jdk中应用

HashMap继承了Map接口

public void putAll(Map<? extends K, ? extends V> m) {
        putMapEntries(m, true);
    }

该方法的入参是map,跟示例中的add是一样的。
ArrayList中的addAll方法,也是一样的。

mybatis中应用

SqlNode是组合模式比较好的体现,如子类MixedSqlNode:

public class MixedSqlNode implements SqlNode {
  private List<SqlNode> contents;
  public MixedSqlNode(List<SqlNode> contents) {
    this.contents = contents;
  }
  @Override
  public boolean apply(DynamicContext context) {
    for (SqlNode sqlNode : contents) {
      sqlNode.apply(context);
    }
    return true;
  }
}

部分子类的UML类图
image.png

也可以关注我的公众号:程序之声
图片
关注公众号,领取更多资源

本文为博主原创文章,未经博主允许不得转载。

评论

发表评论