Java 设计模式之组合模式

一、了解组合模式

1.1 什么是组合模式

组合模式允许你将对象组合成树形结构来表现“整体/部分”层次机构。组合能够使客户以一致的方式处理个别对象以及组合对象。

组合模式让我们能够用树形方式创建对象的结构,树里面包含了组合以及个别的对象。

使用组合结构,我们能够把相同的操作应用在组合和个别对象上。换句话说,在大多数情况下,我们可以忽略对象组合和个别对象之间的差别。

1.2 组合模式组成结构

  • Component :是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理 Component 子部件。
  • Leaf :在组合中表示叶子结点对象,叶子结点没有子结点。
  • Composite :定义有枝节点行为,用来存储子部件,在 Component 接口中实现与子部件有关操作,如增加 (add) 和删除 (remove) 等。

1.3 组合模式 UML 图解
这里写图片描述

组合包含组件。组件有两种:组合和叶节点元素。组合持有一群孩子,这些孩子可以分别是别的组合或者叶节点元素。当你使用这种方式组织数据的时候,最终会得到树形结构 (由上而下的树形结构),根部是一个组合,而组合的分支逐渐向下延伸,直到叶节点为止。

1.4 组合模式应用场景

  • 想表示对象的部分-整体层次结构(树形结构)。
  • 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
二、组合模式具体应用

2.1 问题描述

打印菜单:通过代码实现打印早、中、晚餐的菜单,列出所有的菜品,并且在晚餐菜单中包含甜品菜单。

2.2 问题树形结构分析图
这里写图片描述

2.3 使用组合模式设计相关类图
这里写图片描述

2.4 代码实现

MenuComponent 抽象类

package com.jas.component;

/**
 * @author Jas
 * @create 2018-01-30 10:23
 **/
public abstract class MenuComponent {
    public void add(MenuComponent menuComponent){
        throw new UnsupportedOperationException();
    }

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

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

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

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

    public void print(){
        throw new UnsupportedOperationException();
    }

}

组件 MenuItem 类

package com.jas.component;

/**
 * 菜单项类实现,用来封装菜品信息
 * 
 * getName() 方法返回菜品名字
 * getPrice() 方法返回菜品价格
 * print() 输出该菜品的详细信息
 * 
 * @author Jas
 * @create 2018-01-30 10:26
 **/
public class MenuItem extends MenuComponent{
    String name;
    double price;

    public MenuItem(String name, double price){
        this.name = name;
        this.price = price;
    }

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

    @Override
    public double getPrice() {
        return price;
    }

    @Override
    public void print() {
        System.out.print(" " + getName());
        System.out.println(" " + getPrice());
    }
}

组件 Menu 类

package com.jas.component;

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

/**
 * 菜单类,
 * 
 * getName() 方法返回具体菜单名
 * add(MenuComponent menuComponent) 方法表示添加菜单
 * remove(MenuComponent menuComponent) 删除一个菜单
 * getChild(int i) 返回一个指定的菜单
 * print() 用于输出菜单名和具体菜单菜品信息,也是所有代码中最难理解的一部分
 * 
 * @author Jas
 * @create 2018-01-30 10:30
 **/
public class Menu extends MenuComponent {
    /** menuComponents 用来保存早中晚的菜单项。 */
    List<MenuComponent> menuComponents = new ArrayList<>();
    /** name 表示菜单名 (早中晚甜品)*/
    String name;

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

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

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

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

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

    /**
     * 既可以输出菜单名,也可以输出菜单中的菜品名
     */
    @Override
    public void print() {
        System.out.println("\n" + getName());
        System.out.println("====================");

        Iterator<MenuComponent> iterator = menuComponents.iterator();
        while (iterator.hasNext()){
            MenuComponent menuComponent = iterator.next();
            menuComponent.print();
        }
    }
}

Waitress 类

package com.jas.component;

/**
 * 招待员类
 * 
 * @author Jas
 * @create 2018-01-30 10:39
 **/
public class Waitress {
    MenuComponent allMenus;

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

    public void print(){
        allMenus.print();
    }
}

测试类

package com.jas.component;

/**
 *测试类
 * 
 * @author Jas
 * @create 2018-01-30 10:40
 **/
public class MenuTestDrive {
    public static void main(String[] args) {

        MenuComponent breakfastMenu = new Menu("早餐菜单");
        MenuComponent lunchMenu = new Menu("午餐菜单");
        MenuComponent dinnerMenu = new Menu("晚餐菜单");
        MenuComponent dessertMenu = new Menu("甜品菜单");
        /** 顶层菜单*/
        MenuComponent allMenus = new Menu("ALL MENUS LIST");

        breakfastMenu.add(new MenuItem("包子", 1.0));
        breakfastMenu.add(new MenuItem("豆浆",2.0));
        lunchMenu.add(new MenuItem("西红柿盖浇饭", 10.0));
        lunchMenu.add(new MenuItem("西红柿鸡蛋面",10.0));
        dinnerMenu.add(new MenuItem("热狗", 15.0));
        dessertMenu.add(new MenuItem("苹果派", 5.0));

        allMenus.add(breakfastMenu);
        allMenus.add(lunchMenu);
        allMenus.add(dinnerMenu);
        /** 将甜品菜单添加在晚餐菜单中*/
        dinnerMenu.add(dessertMenu);

        Waitress waitress = new Waitress(allMenus);
        waitress.print();
    }
}

    /**
     * 输出
     * ALL MENUS LIST
     * ====================
     *
     * 早餐菜单
     * ====================
     * 包子 1.0
     * 豆浆 2.0
     *
     * 午餐菜单
     * ====================
     * 西红柿盖浇饭 10.0
     * 西红柿鸡蛋面 10.0
     *
     * 晚餐菜单
     * ====================
     *  热狗 15.0
     *
     *  甜品菜单
     * ====================
     *  苹果派 5.0
     * 
     */
三、组合模式总结

3.1 组合模式优缺点

优点

  • 高层模块调用简单。
  • 节点可自由增加。

缺点

在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了面对抽象编程和依赖倒置原则。

3.2 组合模式知识点总结

  • 组合模式提供一个结构,可同时包容个别对象和组合对象。
  • 组合模式允许客户对个别对象以及组合对象一视同仁。
  • 组合结构内的任意对象称为组件,组件可以是组合,也可以是叶节点。
  • 在使用组合模式时,要多加考虑方式,有的时候可以与迭代器模式配合使用。

PS:点击了解更多设计模式 http://blog.csdn.net/codejas/article/details/79236013

参考文献

《Head First 设计模式》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值