2,组合模式

来源:互联网 发布:苹果手机数据恢复公司 编辑:程序博客网 时间:2024/05/17 14:25

一,前言

上一节我们介绍了 [迭代器模式](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从输出我们看到    新增加的甜点菜单从属于晚餐菜单下,层级关系明确    并且应用组合模式,今后的拓展和维护也变得简单了
原创粉丝点击