迭代器模式
定义: 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示
分析: 有许多种方法可以将对象堆起来成为一个集合,例如数组,堆栈或是列表,当客户想要对其进行遍历时,我们不应该让其看到内部实现。使用一个迭代器来进行遍历
UML类图
Aggregate: 共同的接口供所有的聚合使用
ConcreteAggregate: 具体聚合,持有一个对象的集合,实现一个返回集合的迭代器的方法,负责实例化一个具体迭代器,用于遍历对象集合
Iterator: 迭代器的父接口,包含一些用于遍历的方法
ConcreteIterator: 具体迭代器,负责管理目前遍历的位置
示例
有两家餐馆进行合并,计划使用餐馆A菜单作为早餐,餐馆B菜单作为午餐。问题在于A菜单使用ArrayList用来存储菜单项,B菜单使用数组,而两边都不愿进行改变。对于客户代码,总是需要使用处理两个菜单,并使用两个循环遍历,如果有第三个菜单,甚至是第四个呢?
菜单项
public class MenuItem {
String name;
String description;
boolean vegetarian;
double price;
public MenuItem(String name, String description, boolean vegetarian, double price) {
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public boolean isVegetarian() {
return vegetarian;
}
public double getPrice() {
return price;
}
}
具体菜单
//早餐菜单
public class BreakfastMenu {
static final int MAX_ITEMS = 6;
int numberOfItems = 0;
ArrayList<MenuItem> arrayList;
public BreakfastMenu(ArrayList<MenuItem> arrayList) {
this.arrayList = arrayList;
}
public BreakfastMenu() {
}
public void addItem(String name, String description, boolean vegetarian, double price){
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
if(numberOfItems >= MAX_ITEMS){
System.out.println("菜单没有位置");
} else {
arrayList.add(menuItem);
numberOfItems += 1;
}
}
public Iterator createIterator(){
return new IteratorA(arrayList);
}
public ArrayList<MenuItem> getMenuItems(){
return arrayList;
}
}
//晚餐菜单
public class DinnerMenu {
static final int MAX_ITEMS = 6;
int numberOfItems = 0;
MenuItem[] menuItems;
public DinnerMenu(MenuItem[] menuItems) {
this.menuItems = menuItems;
}
public DinnerMenu() {
}
public void addItem(String name, String description, boolean vegetarian, double price){
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
if(numberOfItems >= MAX_ITEMS){
System.out.println("菜单没有位置");
} else {
menuItems[numberOfItems] = menuItem;
numberOfItems += 1;
}
}
public Iterator createIterator(){
return new IteratorB(menuItems);
}
public MenuItem[] getMenuItems(){
return menuItems;
}
}
迭代器接口
public interface Iterator {
boolean hasNext();
Object next();
}
具体迭代器
public class IteratorA implements Iterator {
ArrayList<MenuItem> arrayList;
int position = 0;
public IteratorA(ArrayList arrayList) {
this.arrayList = arrayList;
}
@Override
public boolean hasNext() {
if (position >= arrayList.size() || arrayList.get(position) == null){
return false;
} else {
return true;
}
}
@Override
public Object next() {
MenuItem menuItem = arrayList.get(position);
position += 1;
return menuItem;
}
}
public class IteratorB implements Iterator {
MenuItem[] menuItems;
int position = 0;
public IteratorB(MenuItem[] menuItems) {
this.menuItems = menuItems;
}
@Override
public boolean hasNext() {
if (position >= menuItems.length || menuItems[position] == null){
return false;
} else {
return true;
}
}
@Override
public Object next() {
MenuItem menuItem = menuItems[position];
position += 1;
return menuItem;
}
}
客户代码
public class Waitress {
BreakfastMenu breakfastMenu;
DinnerMenu dinnerMenu;
public Waitress(BreakfastMenu breakfastMenu, DinnerMenu dinnerMenu) {
this.breakfastMenu = breakfastMenu;
this.dinnerMenu = dinnerMenu;
}
public Waitress() {
}
public void printMenu(){
Iterator iteratorA = breakfastMenu.createIterator();
Iterator iteratorB = dinnerMenu.createIterator();
System.out.println("\n-------早餐菜单-------");
printMenu(iteratorA);
System.out.println("\n-------晚餐菜单--------");
printMenu(iteratorB);
}
public void printMenu(Iterator iterator){
while(iterator.hasNext()){
MenuItem item = (MenuItem) iterator.next();
System.out.print(item.getName()+",");
System.out.print(item.getDescription()+",");
System.out.println(item.getPrice());
}
}
}
测试
public class test {
public static void main(String[] args) {
BreakfastMenu breakfastMenu = new BreakfastMenu(new ArrayList<MenuItem>());
DinnerMenu dinnerMenu = new DinnerMenu(new MenuItem[6]);
breakfastMenu.addItem("煎饼","煎",true,6);
breakfastMenu.addItem("包子","蒸",false,1);
breakfastMenu.addItem("油条","炸",true,2);
dinnerMenu.addItem("手撕包菜","酸辣",true,10);
dinnerMenu.addItem("鱼香肉丝","酸甜",false,12);
dinnerMenu.addItem("辣子鸡","辣",false,14);
Waitress waitress = new Waitress(breakfastMenu, dinnerMenu);
waitress.printMenu();
}
}
做了什么事情?
我们不改变两个餐馆菜单的内部实现,各自提供一个迭代器,只需要实现一个create方法,客户不知道菜单内部实现,使得客户代码更容易维护和扩展。
优化
现在的客户代码还是依赖于菜单的具体实现,我们将两个菜单类实现一个共同接口,则客户代码可以使用接口来调用需要的菜单对象,减少客户和具体类的依赖
public interface Menu {
public Iterator createIterator();
}
同时,JAVA提供了java.util.Iterator接口,我们可以直接实现该接口,并且ArrayList也内置了Iterator。
设计原则: 单一原则,一个类应该只有一个引起变化的原因。
我们知道要避免类内的改变,因为修改代码很容易造成很多潜在的错误。如果一个类内具有两个改变的原因,这会使得将来该类的变化几率上升,例如当我们允许一个类完成自己的事情(管理某种聚合),同时还有担负更多责任(例如遍历),我们就给了这个类两个变化的原因。
新的问题: 现在,他们希望在午餐菜单加上一份饭后甜点的“子菜单”,我们不仅仅要支持多个菜单,甚至还有支持菜单中的菜单
现在,菜单内部实现的修改已经不可避免了。我们有新的需求
- 我们需要某种树形结构,以容纳菜单,子菜单,和菜单项
- 我们要确定能够在每个菜单的每个项之间游走,至少要向现在的迭代器一样方便
- 我们也需要更有弹性的进行遍历,比如只需要遍历甜点
组合模式
定义: 允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合
分析: 以我们的菜单为例子来思考这一切,这个模式创建一个树形结构,在同一结构中处理嵌套菜单和菜单项组,我们得到一个“整体/部分"层次结构,即一个对象树,也可以看做一个大的菜单。所以我们可以”统一处理个别对象和组合对象“,这意味着,如果我们有一个树形结构的菜单,子菜单和可能带有菜单项的子菜单,那么任何一个菜单都是一种”组合“,因为它既可以包含其他菜单,也可以包含菜单项。个别对象只能是菜单项,并未持有其他对象。
UML类图
Component: 所以对象的父接口,不管是组合还是叶节点
Leaf: 没有子节点,实现了operation方法
Composite: 实现了叶节点的相关操作,它的角色是要定义组件的行为,而这样的组件具有字节点
组件接口
public abstract class MenuComponent {
public void add(MenuComponent menuComponent){
throw new UnsupportedOperationException();
}
public void remove(MenuComponent menuComponent){
throw new UnsupportedOperationException();
}
public MenuComponent getChild(){
throw new UnsupportedOperationException();
}
public String getName(){
throw new UnsupportedOperationException();
}
public String getDescription(){
throw new UnsupportedOperationException();
}
public double getPrice(){
throw new UnsupportedOperationException();
}
public boolean isVegetarian(){
throw new UnsupportedOperationException();
}
public void print(){
throw new UnsupportedOperationException();
}
}
组合
public class Menu extends MenuComponent {
ArrayList<MenuComponent> menuComponents = new ArrayList<>();
String name;
String description;
public Menu(String name, String description) {
this.name = name;
this.description = description;
}
public Menu() {
}
public void add(MenuComponent menuComponent){
menuComponents.add(menuComponent);
}
public void remove(MenuComponent menuComponent){
menuComponents.remove(menuComponent);
}
public MenuComponent getChild(int i){
return menuComponents.get(i);
}
@Override
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public void print(){
System.out.print("\n"+getName());
System.out.println(","+getDescription());
System.out.println("--------------------");
Iterator<MenuComponent> iterator = menuComponents.iterator();
while (iterator.hasNext()){
MenuComponent next = iterator.next();
next.print();
}
}
}
叶
public class MenuItem extends MenuComponent {
private String name;
private String description;
private boolean isVegetarian;
private double price;
public MenuItem(String name, String description, boolean isVegetarian, double price) {
this.name = name;
this.description = description;
this.isVegetarian = isVegetarian;
this.price = price;
}
public MenuItem() {
}
@Override
public String getName() {
return name;
}
public String getDescription() {
return description;
}
@Override
public boolean isVegetarian() {
return isVegetarian;
}
@Override
public double getPrice() {
return price;
}
public void print(){
System.out.print(" "+getName());
if(isVegetarian()){
System.out.print(","+"素食");
}
System.out.print(","+getDescription());
System.out.println("-----"+getPrice());
}
}
客户代码
public class Waitress {
MenuComponent allMenus;
public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
}
public Waitress() {
}
public void printMenu(){
allMenus.print();
}
}
测试
public class test {
public static void main(String[] args) {
MenuComponent menuA = new Menu("pancake", "早餐");
MenuComponent menuB = new Menu("dinner menu", "午餐");
MenuComponent menuC = new Menu("cafe menu", "甜点");
MenuComponent allMenus = new Menu("allmenus","顶层菜单");
allMenus.add(menuA);
allMenus.add(menuB);
allMenus.add(menuC);
menuA.add(new MenuItem("豆浆","饮品",true,2));
menuB.add(new MenuItem("牛排","正餐",false,20));
menuB.add(menuC);
menuC.add(new MenuItem("黑咖啡","甜点",true,10));
Waitress waitress = new Waitress(allMenus);
waitress.printMenu();
}
}