2,组合模式

一,前言

上一节我们介绍了 [迭代器模式](http://blog.csdn.net/ABAP_Brave/article/details/54949056)
按照灵活性和复杂度递增的原则,这一节说一下组合模式
相对于较复杂的设计模式来讲,组合模式适用的场景相对比较固定
就是大家常说的"树形结构","整体/部分"等,具有层次关系的应用场景

二,背景

由于之前早点铺和炒菜馆合并了,所以饭店的菜单包含早餐菜单和晚餐菜单
(迭代器模式中,早餐菜单用Array数组保存,晚餐菜单由List集合保存)
现在需要在晚餐菜单中加入一套甜点菜单,这样菜单就具有了一种层级和所属的概念

我们先认识一下组合模式,再使用组合模式解决这个问题

三,初识组合模式

我们使用一个简单的树形结构(分支,叶子),先认识一下这组合模式

1,首先定义组件: 分支节点 和 叶子节点 共同的的抽象父类

package com.brave.component_tree;

/**
 * 定义组件 : 
 *  分支节点 和 叶子节点 共同的的抽象父类
 *  使得分支和叶子节点可以使用相同的处理方式
 * 
 * @author Brave
 *
 */
public abstract class Component {

    // 组件名称
    protected String componentName;

    // 构造方法
    public Component(String componentName) {
        this.componentName = componentName;
    }

    // 增加分支/叶子节点
    public abstract void add(Component component);

    // 移除分支/叶子节点
    public abstract void remove(Component component);

    // 层级
    public abstract void show(int level);

}

2,创建分支和叶子对象,继承该组件

分支节点对象
 *  继承Component组件,实现对分支的添加,删除,输出
 *  分支节点拥有下级节点,该对象具有一个组件列表childrenList,用于保存其下级的分支或叶子节点对象
 *  由于分支和叶子节点都继承自相同组件Component,故可以使用相同的处理方式,如:递归输出
package com.brave.component_tree;

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

/**
 * 分支节点对象:
 *  继承Component组件,实现对分支的添加,删除,输出
 *  分支节点拥有下级节点,该对象具有一个组件列表childrenList,用于保存其下级的分支或叶子节点对象
 *  由于分支和叶子节点都继承自相同组件Component,故可以使用相同的处理方式,如:递归输出
 *  
 * @author Brave
 *
 */
public class Composite extends Component{

    private List<Component> childrenList = new ArrayList<Component>();

    public Composite(String componentName) {
        super(componentName);
    }

    // 新增
    @Override
    public void add(Component component) {
        childrenList.add(component);
    }

    // 删除
    @Override
    public void remove(Component component) {
        childrenList.remove(component);
    }

    // 递归输出
    @Override
    public void show(int level) {

        // 按照level级别添加前置"+"区分层级关系
        for(int i=0; i < level; i++){
            System.out.print("+");
        }

        System.out.print(componentName + '\n');

        // 递归打印下级节点
        for(int i=0; i < childrenList.size(); i++){
            System.out.print("+");
            Component component = childrenList.get(i);
            component.show(level + 2);
        }

    }

}
 叶子节点对象:
 *  继承Component组件,实现对分支的添加,删除,输出
 *  叶子节点不再具有下级节点
package com.brave.component_tree;

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

/**
 * 叶子节点对象:
 *  继承Component组件,实现对分支的添加,删除,输出
 *  叶子节点不再具有下级节点
 *  
 * @author Brave
 *
 */
public class Leaf extends Component{

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

    @Override
    public void add(Component component) {
        System.out.println("叶子节点不能增加子节点");
    }

    @Override
    public void remove(Component component) {
        System.out.println("叶子节点不能删除子节点");
    }

    // 打印叶子节点
    @Override
    public void show(int level) {

        // 按照level级别添加前置"+"区分层级关系
        for(int i=0; i < level; i++){
            System.out.print("+");
        }

        System.out.print(componentName + '\n');
    }

}

3,组装一棵树并测试

创建一个树根(属于分支类型),若干分支和叶子对象,并自由组合成为一颗大树

package com.brave.component_tree;

public class TreeTest {

    /**
     * 由于大树的节点和叶子继承自同一组件Component
     * 所以对于叶子对象Leaf或分支对象Composite可以自由添加到任意分支对象Composite
     * 
     * 优点:可拓展,易于维护,灵活度高
     */
    public static void main(String[] args) {

        // 创建一棵树
        Composite root = new Composite("树根");

        // 向树根添加2片叶子
        root.add(new Leaf("叶子_树根_1"));
        root.add(new Leaf("叶子_树根_2"));

        //创建节点A及其叶子
        Composite compositeA = new Composite("节点A");
        compositeA.add(new Leaf("叶子_节点A_1"));
        compositeA.add(new Leaf("叶子_节点A_2"));

        //创建节点B及其叶子
        Composite compositeB = new Composite("节点B");
        compositeB.add(new Leaf("叶子_节点B_1"));
        compositeB.add(new Leaf("叶子_节点B_2"));

        // 自由组装大树:将B节点挂于A节点下,A节点在挂于树根下
        compositeA.add(compositeB);
        root.add(compositeA);

        // 输出大树结构
        root.show(1);
    }

}

4,运行代码测试:

+树根
++++叶子_树根_1
++++叶子_树根_2
++++节点A
++++++叶子_节点A_1
++++++叶子_节点A_2
++++++节点B
++++++++叶子_节点B_1
++++++++叶子_节点B_2

5,总结

通过 组合模式-树形结构 这个简单的例子我们可以看到

由于大树的节点和叶子继承自同一组件Component
所以对于叶子对象Leaf或分支对象Composite可以自由添加到任意分支对象Composite

如果有一天要对这颗树做一些结构上的调整,维护起来还是相当容易的
具有可拓展,易于维护,灵活度高的优点


所以,当涉及到整体/部分这种层级所属关系的树形结构时,组合模式将是首选

四,使用组合模式添加新的菜单

1,定义菜单项(分支节点)和菜单(叶子节点)公用组件

package com.brave.component;

/**
 * 菜单组件(叶子节点,分支节点)的抽象类
 * 作用:为 叶子节点(菜单项) 和 分支节点(菜单) 提供统一接口
 * 有些方式只对菜单有用,有些只对菜单项有用,这里写上父类默认实现,不重写默认无效抛出异常
 * @author Brave
 *
 */
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 String getDesc(){
        throw new UnsupportedOperationException();
    }

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

    /** 打印 **/
    public void print(int level){
        throw new UnsupportedOperationException();
    }

}

2,创建菜单和菜单项对象,继承自该组件

菜单对象
 * 菜单(Menu)和菜单项(MenuItem)都拓展自MenuComponent:可以使用相同方式来菜单和菜单项对象
 * 对于菜单对象:能够添加,删除,查询菜单项并遍历下级菜单项
package com.brave.component;

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


/**
 * 菜单对象
 * 菜单(Menu)和菜单项(MenuItem)都拓展自MenuComponent:可以使用相同方式来菜单和菜单项对象
 * 对于菜单对象:能够添加,删除,查询菜单项并遍历下级菜单项
 * @author Brave
 *
 */
public class Menu extends MenuComponent{

    ArrayList<MenuComponent> menuComponents = new ArrayList<MenuComponent>();

    String name;    //名称
    String desc;    //描述

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

    /** 重写增 删 查 **/
    @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);
    }

    /** 重写getter **/
    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getDesc() {
        return desc;
    }

    /** 打印 **/
    @Override
    public void print(int level){

        String pre = "";
        for(int i=0; i<level; i++){
            pre = pre+"--";
        }

        System.out.println(pre + "*******************************************");
        System.out.println(pre + "name = " + getName() + ", Desc = " + getDesc());
        System.out.println(pre + "*******************************************");

        Iterator<MenuComponent> iterator = menuComponents.iterator();

        while(iterator.hasNext()){
            MenuComponent menuComponent = iterator.next();
            menuComponent.print(level + 2);
        }
    }

}
菜单项对象
 * 菜单(Menu)和菜单项(MenuItem)都拓展自MenuComponent:可以使用相同的方式吃力菜单和菜单项对象
 * 对于菜单项对象:不能够添加,删除,查询菜单项和菜单项,只能获取菜单项对象的属性
package com.brave.component;

/**
 * 菜单项对象
 * 菜单(Menu)和菜单项(MenuItem)都拓展自MenuComponent:可以使用相同的方式吃力菜单和菜单项对象
 * 对于菜单项对象:不能够添加,删除,查询菜单项和菜单项,只能获取菜单项对象的属性
 * @author Brave
 *
 */
public class MenuItem extends MenuComponent{

    String name;    //名称
    String desc;    //描述
    double price;   //价格

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

    /** 重写getter **/
    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getDesc() {
        return desc;
    }

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

    /** 打印 **/
    @Override
    public void print(int level){

        String pre = "";
        for(int i=0; i<level; i++){
            pre = pre+"--";
        }

        System.out.println(pre + "name = " + getName() + ", Desc = "+  getDesc() + ", Price = " + getPrice());

    }

}

3.创建并组合菜单

package com.brave.component;

/**
 * 组合模式的测试类
 * 由于菜单(Menu)和菜单项(MenuItem)都集成自菜单组件(MenuComponent):菜单和菜单项间可以自由的进行组合,并可以使用相同的处理方式
 * @author Brave
 *
 */
public class MentTest {

    public static void main(String[] args) {

        // 创建早餐菜单
        MenuComponent breakFastMenu = new Menu("菜单1","早餐菜单");
        breakFastMenu.add(new MenuItem("煎饼果子", "鸡蛋,果子", 5.00));
        breakFastMenu.add(new MenuItem("豆浆", "豆浆", 2.00));
        breakFastMenu.add(new MenuItem("豆腐脑", "豆腐脑", 3.00));
        breakFastMenu.add(new MenuItem("锅巴菜", "锅巴,卤子", 4.00));
        breakFastMenu.add(new MenuItem("茶叶蛋", "鸡蛋", 1.00));
        breakFastMenu.add(new MenuItem("云吞", "猪肉,面皮,高汤", 4.00));

        // 创建晚餐菜单
        MenuComponent dinerMenu = new Menu("菜单2","晚餐菜单");
        dinerMenu.add(new MenuItem("京酱肉丝", "肉丝,葱,豆皮", 25.00));
        dinerMenu.add(new MenuItem("五谷丰登", "各种菜", 10.00));
        dinerMenu.add(new MenuItem("水果沙拉", "各种水果和沙拉", 15.00));
        dinerMenu.add(new MenuItem("宫保鸡丁", "鸡丁,葱,姜,蒜,微辣", 15.00));

        // 创建甜点菜单
        MenuComponent cakeMenu = new Menu("菜单3","甜点菜单");
        cakeMenu.add(new MenuItem("慕斯蛋糕", "黄油,淡奶油,蛋糕", 25.00));
        cakeMenu.add(new MenuItem("黑森林蛋糕", "巧克力,奶油,樱桃酒,蛋糕", 25.00));
        cakeMenu.add(new MenuItem("提拉米苏", "芝士,蛋黄,咖啡,可可粉", 25.00));

        // 组装菜单:甜点菜单添加到晚餐,早餐和晚餐添加到总菜单
        dinerMenu.add(cakeMenu);
        MenuComponent Menu = new Menu("总菜单","早餐+晚餐");
        Menu.add(breakFastMenu);
        Menu.add(dinerMenu);

        // 展示菜单
        Menu.print(1);
    }
}

运行测试:

--*******************************************
--name = 总菜单, Desc = 早餐+晚餐
--*******************************************
------*******************************************
------name = 菜单1, Desc = 早餐菜单
------*******************************************
----------name = 煎饼果子, Desc = 鸡蛋,果子, Price = 5.0
----------name = 豆浆, Desc = 豆浆, Price = 2.0
----------name = 豆腐脑, Desc = 豆腐脑, Price = 3.0
----------name = 锅巴菜, Desc = 锅巴,卤子, Price = 4.0
----------name = 茶叶蛋, Desc = 鸡蛋, Price = 1.0
----------name = 云吞, Desc = 猪肉,面皮,高汤, Price = 4.0
------*******************************************
------name = 菜单2, Desc = 晚餐菜单
------*******************************************
----------name = 京酱肉丝, Desc = 肉丝,葱,豆皮, Price = 25.0
----------name = 五谷丰登, Desc = 各种菜, Price = 10.0
----------name = 水果沙拉, Desc = 各种水果和沙拉, Price = 15.0
----------name = 宫保鸡丁, Desc = 鸡丁,葱,姜,蒜,微辣, Price = 15.0
----------*******************************************
----------name = 菜单3, Desc = 甜点菜单
----------*******************************************
--------------name = 慕斯蛋糕, Desc = 黄油,淡奶油,蛋糕, Price = 25.0
--------------name = 黑森林蛋糕, Desc = 巧克力,奶油,樱桃酒,蛋糕, Price = 25.0
--------------name = 提拉米苏, Desc = 芝士,蛋黄,咖啡,可可粉, Price = 25.0


从输出我们看到
    新增加的甜点菜单从属于晚餐菜单下,层级关系明确
    并且应用组合模式,今后的拓展和维护也变得简单了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BraveWangDev

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值