【设计模式】组合迭代器
发布于: 刚刚
一,前言
按照灵活性和复杂度递增的原则,这一节说一下组合模式
相对于较复杂的设计模式来讲,组合模式适用的场景相对比较固定
就是大家常说的"树形结构","整体/部分"等,具有层次关系的应用场景
复制代码
二,背景
由于之前早点铺和炒菜馆合并了,所以饭店的菜单包含早餐菜单和晚餐菜单
(迭代器模式中,早餐菜单用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
从输出我们看到
新增加的甜点菜单从属于晚餐菜单下,层级关系明确
并且应用组合模式,今后的拓展和维护也变得简单了
复制代码
划线
评论
复制
发布于: 刚刚阅读数: 2
Brave
关注
还未添加个人签名 2018.12.13 加入
还未添加个人简介
评论