写点什么

【设计模式】组合迭代器

用户头像
Brave
关注
发布于: 刚刚

一,前言

按照灵活性和复杂度递增的原则,这一节说一下组合模式相对于较复杂的设计模式来讲,组合模式适用的场景相对比较固定就是大家常说的"树形结构","整体/部分"等,具有层次关系的应用场景
复制代码



二,背景

由于之前早点铺和炒菜馆合并了,所以饭店的菜单包含早餐菜单和晚餐菜单(迭代器模式中,早餐菜单用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

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


用户头像

Brave

关注

还未添加个人签名 2018.12.13 加入

还未添加个人简介

评论

发布
暂无评论
【设计模式】组合迭代器