【设计模式】组合迭代器
一,前言
前面我们已经了解了两种设计模式:迭代器模式、组合模式
1,迭代器模式 :
封装集合的遍历方式,使不同集合可以使用相同的方式进行遍历
2,组合模式 :
将对象组合成树形结构以表示"部分-整体"的层次结构,可以使用相同的方法处理"分支"和"叶子"节点对象
那么,如果遍历一颗“树”,要如何实现呢?
可以使用以上两种模式构造“组合迭代器”模式;
二,组合迭代器思想
组合迭代器模式:用于遍历组合模式树形结构中的所有组件,在遍历过程中可以对返回的组件对象进行筛选和操作
组合迭代器核心思想:递归迭代,即让"分支"节点拥有将子对象集创建为迭代器对象的能力
在组合模式中我们将"分支"和"叶子"节点对象继承自相同的组件,从而视分支和叶子节点为相同对象进行处理
那显然组合迭代器需要"分支"节点对象继续递归迭代其子对象集,直至"叶子"节点位置,所以这里我们需要拓展组件对象,使"分支"节点可以创建迭代器用于迭代子对象集
当然由于分支和叶子节点都继承自该组件,所以在"分支"节点具备创建子对象迭代器功能的同时,"叶子"节点对象也会拥有迭代子对象的功能,但是"叶子"节点下已无子对象,所以在"叶子"节点中我们做一个空的实现即可(我们这里采用一个空的迭代器)
三,组合迭代器
1,在组合模式的基础上,拓展组件对象 MenuComponent,使之可以将分支节点对象中包含的对象集创建为迭代器对象:
package com.brave.compositeIterator;
import java.util.Iterator;
/**
* 菜单组件(叶子节点,分支节点)的抽象类
* 作用:为 叶子节点(菜单项) 和 分支节点(菜单) 提供统一接口
* 有些方式只对菜单有用,有些只对菜单项有用,这里写上父类默认实现,不重写默认无效抛出异常
* @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 boolean isVegetarian() {
throw new UnsupportedOperationException();
}
/** 打印 **/
public void print(int level){
throw new UnsupportedOperationException();
}
/**
* 创建迭代器方法,使分支节点对象可以将子对象集创建为迭代器对象,用于迭代
*/
public abstract Iterator createIterator();
}
2,在组合模式的基础上,分支节点继承自拓展后的组件对象 MenuComponent,菜单(分支)节点需添加对新方法 createIterator 的实现,这里我们需要返回一个组合迭代器(组合迭代器的核心):
package com.brave.compositeIterator;
import java.util.Iterator;
import java.util.Stack;
/**
* 组合迭代器:
* 实现了迭代器Iterator
* @author Brave
*
*/
public class CompositeIterator implements Iterator{
// 堆栈数据结构:后进先出 pop取后删掉,peek只取不删
Stack<Iterator> stack = new Stack<Iterator>();
/**
* 将顶层迭代器对象抛入一个堆栈数据结构中
* @param iterator 顶层迭代器对象
*/
public CompositeIterator(Iterator iterator) {
stack.push(iterator);
}
@Override
public boolean hasNext() {
if(stack.empty()){
return false;
}else{
// 如果不为空,从堆顶取出迭代器
Iterator iterator = stack.peek();
//如果没有下一个元素,弹出堆栈
if(!iterator.hasNext()){
stack.pop();// 移除此迭代器对象
return hasNext();//递归检查下一个迭代器
}else{
return true;
}
}
}
@Override
public Object next() {
if(hasNext()){
Iterator iterator = stack.peek();
MenuComponent component = (MenuComponent)iterator.next();
//如果是菜单对象,将本层迭代器放入堆栈,准备读取
if(component instanceof Menu){
stack.push(component.createIterator());
}
return component;
}else{
return null;
}
}
}
package com.brave.compositeIterator;
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);
}
}
/**
* 将分支节点的子对象集创建为迭代器对象(用于迭代组件)
*/
@Override
public Iterator createIterator() {
return new CompositeIterator(menuComponents.iterator());
}
}
3,菜单项(叶子)节点也需添加对新方法 createIterator 的实现,由于菜单项已经是“叶子”节点,不会在有下级节点,也无需迭代,这里我们可以在 createIterator 的实现中使用一个空迭代器对象:
package com.brave.compositeIterator;
import java.util.Iterator;
/**
* 空迭代器对象
* 因为菜单项已经是叶子节点,下边没有可迭代的对象
* @author Brave
*
*/
public class NullIterator implements Iterator {
@Override
public boolean hasNext() {
return false;
}
@Override
public Object next() {
return null;
}
}
package com.brave.compositeIterator;
import java.util.Iterator;
/**
* 菜单项对象
* 菜单(Menu)和菜单项(MenuItem)都拓展自MenuComponent:可以使用相同的方式吃力菜单和菜单项对象
* 对于菜单项对象:不能够添加,删除,查询菜单项和菜单项,只能获取菜单项对象的属性
* @author Brave
*
*/
public class MenuItem extends MenuComponent{
String name; //名称
String desc; //描述
boolean vegetarian; //是否素食
double price; //价格
public MenuItem(String name, String desc, boolean vegetarian, double price) {
this.name = name;
this.desc = desc;
this.vegetarian = vegetarian;
this.price = price;
}
/** 重写getter **/
@Override
public String getName() {
return name;
}
@Override
public String getDesc() {
return desc;
}
@Override
public double getPrice() {
return price;
}
@Override
public boolean isVegetarian() {
return vegetarian;
}
/** 打印 **/
@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());
}
/** 返回空迭代器 :因为菜单已经是叶子节点,下边没有可迭代的对象,故返回一个空迭代器对象 **/
@Override
public Iterator createIterator() {
return new NullIterator();
}
}
在这个例子中,我们对菜单项对象添加了一个新的属性 vegetarian:表示菜品是否为素食;用于后续展示组合迭代器的作用;
4,和组合模式类似,创建一个树形结构的菜单,但这里我们将菜单的输出封装到一个招待员类,可以输出全部菜单和仅素食菜单:
package com.brave.compositeIterator;
import java.util.Iterator;
/**
* 招待员类
* 面向接口编程 MenuComponent, Iterator
* 封装了Menu的各种遍历
* @author Brave
*
*/
public class Waitress {
MenuComponent allMenus;
public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
}
public void printMenu(){
allMenus.print(1);
}
public void printVegetarianMenu() {
// 将菜单创建为一个组合迭代器
Iterator iterator = allMenus.createIterator();
while(iterator.hasNext()){
try{
MenuComponent menuComponent = (MenuComponent)iterator.next();
if(menuComponent.isVegetarian()){
menuComponent.print(1);
}
}catch(UnsupportedOperationException e){
// 暂不处理 : 菜单对象会进入Catch(因为没有重写isVegetarian)
}
}
}
}
通过招待员类对菜单的进一步封装,我们在客户端调用时,可以选中输出全部菜单或者仅素食菜单:
package com.brave.compositeIterator;
/**
* 组合模式的测试类
* 由于菜单(Menu)和菜单项(MenuItem)都集成自菜单组件(MenuComponent):菜单和菜单项间可以自由的进行组合,并可以使用相同的处理方式
* @author Brave
*
*/
public class MentTest {
public static void main(String[] args) {
// 创建早餐菜单
MenuComponent breakFastMenu = new Menu("菜单1","早餐菜单");
breakFastMenu.add(new MenuItem("煎饼果子", "鸡蛋,果子", false, 5.00));
breakFastMenu.add(new MenuItem("豆浆", "豆浆", true, 2.00));
breakFastMenu.add(new MenuItem("豆腐脑", "豆腐脑", true, 3.00));
breakFastMenu.add(new MenuItem("锅巴菜", "锅巴,卤子", true, 4.00));
breakFastMenu.add(new MenuItem("茶叶蛋", "鸡蛋", false, 1.00));
breakFastMenu.add(new MenuItem("云吞", "猪肉,面皮,高汤", false, 4.00));
// 创建晚餐菜单
MenuComponent dinerMenu = new Menu("菜单2","晚餐菜单");
dinerMenu.add(new MenuItem("京酱肉丝", "肉丝,葱,豆皮", false, 25.00));
dinerMenu.add(new MenuItem("五谷丰登", "各种菜", true, 10.00));
dinerMenu.add(new MenuItem("水果沙拉", "各种水果和沙拉", true, 15.00));
dinerMenu.add(new MenuItem("宫保鸡丁", "鸡丁,葱,姜,蒜,微辣", false, 15.00));
// 创建甜点菜单
MenuComponent cakeMenu = new Menu("菜单3","甜点菜单");
cakeMenu.add(new MenuItem("慕斯蛋糕", "黄油,淡奶油,蛋糕", true, 25.00));
cakeMenu.add(new MenuItem("黑森林蛋糕", "巧克力,奶油,樱桃酒,蛋糕", true, 25.00));
cakeMenu.add(new MenuItem("提拉米苏", "芝士,蛋黄,咖啡,可可粉", true, 25.00));
// 组装菜单:甜点菜单添加到晚餐,早餐和晚餐添加到总菜单
dinerMenu.add(cakeMenu);
MenuComponent Menu = new Menu("总菜单","早餐+晚餐");
Menu.add(breakFastMenu);
Menu.add(dinerMenu);
// 展示菜单
Waitress waitress = new Waitress(Menu);
// 全部
waitress.printMenu();
// 仅素食
waitress.printVegetarianMenu();
}
}
输出如下:
--*******************************************
--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
--name = 豆浆, Desc = 豆浆, Price = 2.0
--name = 豆腐脑, Desc = 豆腐脑, Price = 3.0
--name = 锅巴菜, Desc = 锅巴,卤子, Price = 4.0
--name = 五谷丰登, Desc = 各种菜, Price = 10.0
--name = 水果沙拉, Desc = 各种水果和沙拉, Price = 15.0
--name = 慕斯蛋糕, Desc = 黄油,淡奶油,蛋糕, Price = 25.0
--name = 黑森林蛋糕, Desc = 巧克力,奶油,樱桃酒,蛋糕, Price = 25.0
--name = 提拉米苏, Desc = 芝士,蛋黄,咖啡,可可粉, Price = 25.0
--name = 慕斯蛋糕, Desc = 黄油,淡奶油,蛋糕, Price = 25.0
--name = 黑森林蛋糕, Desc = 巧克力,奶油,樱桃酒,蛋糕, Price = 25.0
--name = 提拉米苏, Desc = 芝士,蛋黄,咖啡,可可粉, Price = 25.0
四,结论
从以上示例可以看出,使用组合迭代器模式,可以对树形结构的组合迭代器进行遍历迭代,在迭代过程中返回每一个组件对象进行处理;
其实,对于树形结构的迭代,还可以使用其他的方式;
这里,我们将组合模式和迭代器模式合二为一,也展示了设计模式的强大;
对于类似 java 这种面向对象语言,其特性在于继承,多态和封装;
而设计模式正是对其特性的淋漓尽致的体现;
设计模式仅仅是一招半式,融会贯通才能发挥真正威力;
编程,不仅是技术,更是艺术;
Brave
还未添加个人签名 2018.12.13 加入
还未添加个人简介
评论