前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >组合模式.

组合模式.

作者头像
JMCui
发布2019-01-03 10:47:48
7830
发布2019-01-03 10:47:48
举报
文章被收录于专栏:JMCui

一、概念

  • 组合模式:允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
  • 组合包含组件。组件有两种:组合和叶节点元素。组合持有一群孩子,这些孩子可以是别的组合或者叶节点元素。
  • 角色:  组合部件(Component):它是一个抽象角色,为要组合的对象提供统一的接口。  叶子节点(Leaf):定义无子节点的行为,在组合中表示子节点对象,叶子节点不能有子节点。  组合节点(Composite):定义有子节点的行为,用来存储部件,实现在Component接口中的有关操作,如增加(Add)和删除(Remove)。
avatar
avatar

二、Demo 实现

TOPIC:我们的任务是想实现一个树形结构的菜单,如下~

avatar
avatar

1、组合部件

代码语言:javascript
复制
/**
 * @Description: 组件的抽象类,菜单组件的角色是为叶节点和组合节点提供一个公共的接口。
 * @author: cuixiuyin
 * @date: 2018/12/29 08:59
 */
public abstract class MenuComponent {

    /**
     * @Description 组合节点(菜单)的方法组织在一起
     * @author cuixiuyin
     * @date 2018/12/29 09:03
     */
    public void add(MenuComponent menuComponent) {
        throw new UnsupportedOperationException();
    }

    public void remove(MenuComponent menuComponent) {
        throw new UnsupportedOperationException();
    }

    public MenuComponent getChild(int i) {
        throw new UnsupportedOperationException();
    }

    /**
     * @Description 叶子节点(菜单项)的方法组织在一起、其中有一些也可以用于组合节点(菜单)上
     * @author cuixiuyin
     * @date 2018/12/29 09:03
     */
    public String getName() {
        throw new UnsupportedOperationException();
    }

    public String getDescription() {
        throw new UnsupportedOperationException();
    }

    public Double getPrice() {
        throw new UnsupportedOperationException();
    }

    public Boolean isVegetarian() {
        throw new UnsupportedOperationException();
    }

    /**
     * @Description 相关操作
     * @author cuixiuyin
     * @date 2019/01/02 09:04
     */
    public void print() {
        throw new UnsupportedOperationException();
    }
}

组合部件为叶子节点和组合节点定义了统一的接口。所有的操作,如果子类没有实现,我们默认抛出一个 UnsupportedOperationException 异常。

为了要保持透明性,组合内所有的对象都必须实现这个接口,否则客户就必须操心哪个对象是用哪个接口,这就失去了组合模式的意义。

2、叶子节点

代码语言:javascript
复制
/**
 * @Description: 菜单项 (叶子节点)
 * @author: cuixiuyin
 * @date: 2018/12/29 09:09
 */
public class MenuItem extends MenuComponent {
    /**
     * 名称
     */
    private String name;
    /**
     * 描述
     */
    private String description;
    /**
     * 是否为素食
     */
    private Boolean vegetarian;
    /**
     * 价格
     */
    private Double price;

    public MenuItem(String name, String description, Boolean vegetarian, Double price) {
        this.name = name;
        this.description = description;
        this.vegetarian = vegetarian;
        this.price = price;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public Boolean isVegetarian() {
        return vegetarian;
    }

    @Override
    public Double getPrice() {
        return price;
    }
    @Override
    public void print() {
        System.out.print(getName() + "--" + getDescription() + "," + getPrice() + ",");
        if (isVegetarian()) {
            System.out.print("(v)");
        }
        System.out.println();
    }
}

叶子节点没有子节点,只有一些菜单项上的类目,不存在下一级结构的维护。

3、组合节点

代码语言:javascript
复制
/**
 * @Description: 菜单(组合节点)
 * @author: cuixiuyin
 * @date: 2018/12/29 09:19
 */
public class Menu extends MenuComponent {
    /**
     * 可能持有菜单项或其他菜单
     */
    List<MenuComponent> list = new ArrayList<>();

    /**
     * 名称
     */
    private String name;
    /**
     * 描述
     */
    private String description;

    public Menu(String name, String description) {
        this.name = name;
        this.description = description;
    }

    @Override
    public void add(MenuComponent menuComponent) {
        list.add(menuComponent);
    }

    @Override
    public void remove(MenuComponent menuComponent) {
        list.remove(menuComponent);
    }

    @Override
    public MenuComponent getChild(int i) {
        return list.get(i);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getDescription() {
        return description;
    }

    /**
     * @Description 递归 —— 不仅打印出菜单本身的信息,也打印出菜单内所有组件的内容:其他菜单和菜单项目。
     * @author cuixiuyin
     * @date 2018/12/29 09:26
     */
    @Override
    public void print() {
        System.out.println("\n" + getName() + "," + getDescription());
        System.out.println("--------------------");
        Iterator<MenuComponent> iterator = list.iterator();
        while (iterator.hasNext()) {
            MenuComponent component = iterator.next();
            component.print();
        }
    }
}

组合节点维护了一个集合,这个集合可以持有其他菜单项(叶子节点)或者菜单(组合节点)。

4、测试

代码语言:javascript
复制
public class Waitress {

    private MenuComponent menuComponent;

    public Waitress(MenuComponent menuComponent) {
        this.menuComponent = menuComponent;
    }

    public void printMenu() {
        menuComponent.print();
    }
}

public class Test {

    public static void main(String[] args) {
        // 1、创建所有的菜单对象
        MenuComponent breakfast = new Menu("早餐菜单", "Breakfast");
        MenuComponent lunch = new Menu("午餐菜单", "Lunch");
        MenuComponent dinner = new Menu("晚餐菜单", "Dinner");
        MenuComponent dessert = new Menu("晚餐甜点", "Dessert");

        // 2、创建顶层菜单
        MenuComponent top = new Menu("顶层菜单", "Top");
        top.add(breakfast);
        top.add(lunch);
        top.add(dinner);

        // 3、晚餐菜单中加入甜点
        dinner.add(dessert);

        // 4、构建每份菜单中的菜单项(叶子节点)
        breakfast.add(new MenuItem("dumplings", "饺子", false, 10.0));
        breakfast.add(new MenuItem("bread", "面包", true, 7.0));
        breakfast.add(new MenuItem("mile", "牛奶", false, 5.0));

        lunch.add(new MenuItem("rice", "米饭", true, 2.0));
        lunch.add(new MenuItem("burger", "汉堡", false, 6.0));
        lunch.add(new MenuItem("vegetables", "蔬菜", true, 1.0));

        dinner.add(new MenuItem("noodle", "面条", true, 4.0));

        dessert.add(new MenuItem("cake", "蛋糕", true, 3.0));

        // 5、把最顶层的组合给服务员,以便可以获取到所有订单
        Waitress waitress = new Waitress(top);
        waitress.printMenu();
    }
}
avatar
avatar

三、总结

  • 迭代器模式:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
  • 迭代器模式的另一个意义在于:把游走的任务放在迭代器上,而不是聚合上。这样简化了聚合的接口和实现,也让责任各得其所。
  • 单一职责原则:一个类应该只有一个引起变化的原因。类的每个责任都有改变的潜在区域,超过一个责任,意味着超过一个改变的区域。因此,尽量让每个类保持单一责任。
  • 单一职责原则听起来很容易,但其实做起来并不简单:区分设计中的责任,是最困难的事情之一。我们的大脑很习惯看着一大群的行为,然后将它们集中在一起,尽管他们可能属于两个或多个不同的责任。想要成功的唯一方法,就是努力不懈地检查你的设计,随着系统的成长。随时观察有没有迹象显示某个类改变的原因超过一个。
  • 组合模式特别适用于树形结构,假设我们有了一个树形结构的菜单、子菜单和可能还带有菜单项的子菜单,那么任何一个菜单都是一种“组合”。因为它既可以包含其他菜单,也可以包含菜单项,甚至个别对象只是菜单项 —— 并未持有其他对象。
  • 组合模式是违反单一职责原则的,这是一个典型的折衷案例。组合模式以单一责任设计原则换取换取透明性。什么是透明性?通过让组件的接口同时包含一些管理子节点和叶节点的操作,客户就可以将组合和叶节点一视同仁。也就是说,一个元素究竟是组合还是叶节点,对客户是透明的。为了维护透明性,每一个对象都使有相同的接口,难免组合中有些对象的行为不太一样,我们可以选择返回 null 或者 false,甚至是直接抛出异常。
  • 组合模式的优点:  1、组合模式让我们能用树形方式创建对象的结构,树里面包含了组合以及个别的对象(叶节点元素)。  2、使用组合结构,我们能把相同的操作应用在组合和个别对象(叶节点元素)上。换句话说,在大多数情况下,我们可以忽略对象组合和个体对象(叶节点元素)之间的差别。
  • 组合模式的使用场景:  1、当想表达对象的“整体/部分”的层次结构时。  2、希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象时。
  • 在实现组合模式时,有许多设计上的折衷。你要根据需要平衡透明性和安全性。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-01-02 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、概念
  • 二、Demo 实现
    • 1、组合部件
      • 2、叶子节点
        • 3、组合节点
          • 4、测试
          • 三、总结
          相关产品与服务
          数据库
          云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档