- 迭代器与组合模式
定义
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
组合模式允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合
例子
废话不多说,先看具体案例
煎饼屋和餐厅合并了,现在需要使用新的菜单,但是两者菜单实现的结构不太一样,煎饼屋使用ArrayList记录菜单,餐厅使用的是数组。但是二者使用相同的菜单项MenuItem,看下面代码它们的实现:
菜单项代码
/**
* 菜单项
* @author Kwin
*
*/
public class MenuItem {
private String name;
private String desc;
private boolean vegetarian;
private double price;
public MenuItem(String name, String desc, boolean vegetarian, double price) {
this.name = name;
this.desc = desc;
this.vegetarian = vegetarian;
this.price = price;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
public boolean isVegetarian() {
return vegetarian;
}
public double getPrice() {
return price;
}
}
煎饼屋菜单实现
/**
* 煎饼屋菜单
* @author Kwin
*
*/
public class PancakeHouseMenu {
ArrayList<MenuItem> menuItems;
public PancakeHouseMenu() {
menuItems = new ArrayList<MenuItem>();
addItem("K&B's Pancake Breakfast",
"Pancakes with scrambled eggs, and toast",
true, 2.99);
addItem("Regular Pancake Breakfast",
"Pancakes with scrambled eggs, and sausage",
false, 2.99);
addItem("Blueberry Pancakes",
"Pancakes made with fresh blueberries",
true, 3.59);
}
public void addItem(String name, String desc, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, desc, vegetarian, price);
menuItems.add(menuItem);
}
public ArrayList<MenuItem> getMenuItems() {
return menuItems;
}
}
餐厅菜单实现
/**
* 餐厅菜单
* @author Kwin
*
*/
public class DinnerMenu {
static final int MAX_ITEMS = 6;
int numOfItems = 0;
MenuItem[] menuItems;
public DinnerMenu() {
menuItems = new MenuItem[MAX_ITEMS];
addItem("Vegetarian BLT",
"(Fakin') Bacon with lettuce & tomato on whole wheat",
true, 2.99);
addItem("BLT",
"Bacon with lettuce & tomato on whole wheat",
false, 3.99);
addItem("Soup of the day",
"Soup of the day, with a side of potato salad",
false, 3.29);
addItem("Hotdog",
"A hot dog, with saurkraut, relish, onions,topped with cheese",
false, 3.00);
}
public void addItem(String name, String desc, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, desc, vegetarian, price);
if(numOfItems >= MAX_ITEMS) {
System.err.println("Sorry,menu is full");
} else {
menuItems[numOfItems++] = menuItem;
}
}
public MenuItem[] getMenuItems() {
return menuItems;
}
}
现在煎饼屋打算把他的产品作为早餐,餐厅的产品则为午餐,我们需要打印新的菜单,就需要两个不同的for循环打印出菜单,如果我们有第三家餐厅加盟(菜单实现不同),就可能需要第三个循环。
现在我们的迭代器(Iterator)就要登场了,我们利用它来封装“遍历集合内的每个对象的过程”。
让我们定义一个迭代器(Iterator)的接口:
/**
* 迭代器
* @author Kwin
*
*/
public interface Iterator {
boolean hasNext();
Object next();
}
先试试煎饼屋的迭代器:
/**
* 煎饼屋的迭代器
* @author Kwin
*
*/
public class PancakeHouseIterator implements Iterator {
ArrayList<MenuItem> menuItems;
int position = 0;
public PancakeHouseIterator(ArrayList<MenuItem> menuItems) {
this.menuItems = menuItems;
}
@Override
public boolean hasNext() {
if(position >= menuItems.size()) {
return false;
}
return true;
}
@Override
public Object next() {
return menuItems.get(position++);
}
}
改写煎饼屋的菜单
/**
* 煎饼屋菜单
* @author Kwin
*
*/
public class PancakeHouseMenu {
ArrayList<MenuItem> menuItems;
public PancakeHouseMenu() {
menuItems = new ArrayList<MenuItem>();
addItem("K&B's Pancake Breakfast",
"Pancakes with scrambled eggs, and toast",
true, 2.99);
addItem("Regular Pancake Breakfast",
"Pancakes with scrambled eggs, and sausage",
false, 2.99);
addItem("Blueberry Pancakes",
"Pancakes made with fresh blueberries",
true, 3.59);
}
public void addItem(String name, String desc, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, desc, vegetarian, price);
menuItems.add(menuItem);
}
// public ArrayList<MenuItem> getMenuItems() {
// return menuItems;
// }
public Iterator createIterator() {
return new PancakeHouseIterator(menuItems);
}
}
接下来,我们实现餐厅的迭代器:
/**
* 餐馆的迭代器
* @author Kwin
*
*/
public class DinerMenuIterator implements Iterator {
MenuItem[] items;
int position = 0;
public DinerMenuIterator(MenuItem[] items) {
this.items = items;
}
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
if(position >= items.length || null == items[position]) {
return false;
}
return true;
}
@Override
public Object next() {
MenuItem menuItem = items[position++];
return menuItem;
}
}
改写餐馆的菜单
/**
* 餐厅菜单
* @author Kwin
*
*/
public class DinnerMenu {
static final int MAX_ITEMS = 6;
int numOfItems = 0;
MenuItem[] menuItems;
public DinnerMenu() {
menuItems = new MenuItem[MAX_ITEMS];
addItem("Vegetarian BLT",
"(Fakin') Bacon with lettuce & tomato on whole wheat",
true, 2.99);
addItem("BLT",
"Bacon with lettuce & tomato on whole wheat",
false, 3.99);
addItem("Soup of the day",
"Soup of the day, with a side of potato salad",
false, 3.29);
addItem("Hotdog",
"A hot dog, with saurkraut, relish, onions,topped with cheese",
false, 3.00);
}
public void addItem(String name, String desc, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, desc, vegetarian, price);
if(numOfItems >= MAX_ITEMS) {
System.err.println("Sorry,menu is full");
} else {
menuItems[numOfItems++] = menuItem;
// numOfItems++;
}
}
// public MenuItem[] getMenuItems() {
// return menuItems;
// }
public Iterator createIterator() {
return new DinerMenuIterator(menuItems);
}
}
女招待有打印菜单的功能,让我们通过迭代器来实现菜单的打印吧:
/**
* 女侍者
* @author Kwin
*
*/
public class Waitress {
private PancakeHouseMenu pancakeHouseMenu;
private DinnerMenu dinnerMenu;
public Waitress(PancakeHouseMenu pancakeHouseMenu, DinnerMenu dinnerMenu) {
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinnerMenu = dinnerMenu;
}
public void printMenu() {
Iterator pancakeIterator = pancakeHouseMenu.createIterator();
Iterator dinnerIterator = dinnerMenu.createIterator();
System.out.println("MENU\n----\nBREAKFAST");
printMenu(pancakeIterator);
System.out.println("\nLUNCH");
printMenu(dinnerIterator);
}
private void printMenu(Iterator iterator) {
while(iterator.hasNext()) {
MenuItem menuItem = (MenuItem)iterator.next();
System.out.print(menuItem.getName() + ", ");
System.out.print(menuItem.getPrice() + " -- ");
System.out.println(menuItem.getDesc());
}
}
}
让我们测试下我们的代码:
/**
* 测试
* @author Kwin
*
*/
public class MenuTestDrive {
public static void main(String[] args) {
PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
DinnerMenu dinnerMenu = new DinnerMenu();
Waitress waitress = new Waitress(pancakeHouseMenu, dinnerMenu);
waitress.printMenu();
}
}
结果:
当然,Java有自己的Iterator接口,我们自己创建Iterator接口,只是为了了解如何从头创建迭代器。
这是Java的接口
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
利用java.util.Iterator实现我们的菜单
实现菜单接口:
import java.util.Iterator;
/**
* 菜单接口
* @author Kwin
*
*/
public interface Menu<E> {//
public Iterator<E> createIterator();
}
煎饼屋菜单改动很简单:
import java.util.ArrayList;
import java.util.Iterator;
/**
* 煎饼屋菜单
* @author Kwin
*
*/
public class PancakeHouseMenu implements Menu<MenuItem> {
ArrayList<MenuItem> menuItems;
public PancakeHouseMenu() {
menuItems = new ArrayList<MenuItem>();
addItem("K&B's Pancake Breakfast",
"Pancakes with scrambled eggs, and toast",
true, 2.99);
addItem("Regular Pancake Breakfast",
"Pancakes with scrambled eggs, and sausage",
false, 2.99);
addItem("Blueberry Pancakes",
"Pancakes made with fresh blueberries",
true, 3.59);
}
public void addItem(String name, String desc, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, desc, vegetarian, price);
menuItems.add(menuItem);
}
// public ArrayList<MenuItem> getMenuItems() {
// return menuItems;
// }
@Override
public Iterator<MenuItem> createIterator() {
return menuItems.iterator();
}
}
餐厅的迭代器:
import java.util.Iterator;
/**
* 餐馆的迭代器
* @author Kwin
*
*/
public class DinerMenuIterator implements Iterator<MenuItem> {
MenuItem[] items;
int position = 0;
public DinerMenuIterator(MenuItem[] items) {
this.items = items;
}
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
if(position >= items.length || null == items[position]) {
return false;
}
return true;
}
@Override
public MenuItem next() {
MenuItem menuItem = items[position++];
return menuItem;
}
@Override
public void remove() {
if(position <= 0) {
throw new IllegalStateException("You can't remove an item util you've done at least one next()");
}
if(items[position-1] != null) {
for(int i = position - 1, len = items.length - 1; i < len; i++) {
items[i] = items[i + 1];
}
items[items.length - 1] = null;
}
}
}
餐厅菜单
import java.util.Iterator;
/**
* 餐厅菜单
* @author Kwin
*
*/
public class DinnerMenu implements Menu<MenuItem> {
static final int MAX_ITEMS = 6;
int numOfItems = 0;
MenuItem[] menuItems;
public DinnerMenu() {
menuItems = new MenuItem[MAX_ITEMS];
addItem("Vegetarian BLT",
"(Fakin') Bacon with lettuce & tomato on whole wheat",
true, 2.99);
addItem("BLT",
"Bacon with lettuce & tomato on whole wheat",
false, 3.99);
addItem("Soup of the day",
"Soup of the day, with a side of potato salad",
false, 3.29);
addItem("Hotdog",
"A hot dog, with saurkraut, relish, onions,topped with cheese",
false, 3.00);
}
public void addItem(String name, String desc, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, desc, vegetarian, price);
if(numOfItems >= MAX_ITEMS) {
System.err.println("Sorry,menu is full");
} else {
menuItems[numOfItems++] = menuItem;
// numOfItems++;
}
}
// public MenuItem[] getMenuItems() {
// return menuItems;
// }
@Override
public Iterator<MenuItem> createIterator() {
return new DinerMenuIterator(menuItems);
}
}
女侍者类
import java.util.Iterator;
/**
* 女侍者
* @author Kwin
*
*/
public class Waitress {
private Menu<MenuItem> pancakeHouseMenu;
private Menu<MenuItem> dinnerMenu;
public Waitress(Menu<MenuItem> pancakeHouseMenu, Menu<MenuItem> dinnerMenu) {
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinnerMenu = dinnerMenu;
}
public void printMenu() {
Iterator<MenuItem> pancakeIterator = pancakeHouseMenu.createIterator();
Iterator<MenuItem> dinnerIterator = dinnerMenu.createIterator();
System.out.println("MENU\n----\nBREAKFAST");
printMenu(pancakeIterator);
System.out.println("\nLUNCH");
printMenu(dinnerIterator);
}
private void printMenu(Iterator<MenuItem> iterator) {
while(iterator.hasNext()) {
MenuItem menuItem = iterator.next();
System.out.print(menuItem.getName() + ", ");
System.out.print(menuItem.getPrice() + " -- ");
System.out.println(menuItem.getDesc());
}
}
}
测试类
/**
* 测试
* @author Kwin
*
*/
public class MenuTestDrive {
public static void main(String[] args) {
Menu<MenuItem> pancakeHouseMenu = new PancakeHouseMenu();
Menu<MenuItem> dinnerMenu = new DinnerMenu();
Waitress waitress = new Waitress(pancakeHouseMenu, dinnerMenu);
waitress.printMenu();
}
}
java.util.Enumeration(枚举)是一个有次序的迭代器实现。
现在咖啡屋也被并购进来,供应晚餐。
我们看看咖啡屋菜单类
public class CafeMenu implements Menu<MenuItem> {
Hashtable<String, MenuItem> menuItems = new Hashtable<>();
public CafeMenu() {
addItem("Veggie Burger and Air Fries",
"Veggie burger on a whole wheat bun",
true, 3.29);
addItem("Soup of the day",
"Acup of the soup of the day",
false, 4.29);
}
public void addItem(String name, String desc, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, desc, vegetarian, price);
menuItems.put(menuItem.getName(), menuItem);
}
@Override
public Iterator<MenuItem> createIterator() {
return menuItems.values().iterator();
}
}
现在我们希望能够添加一份餐后甜点的子菜单,这个时候,我们必须重新实现各家的菜单了。
我们需要一种树形结构,可以容纳菜单、子菜单和菜单项。
我们需要确定能够在每个菜单的各个项之间游走,而且至少要像现在用迭代器一样方便。
我们也需要能够更有弹性地在菜单项之间游走。比方说,可能只需要遍历甜点菜单,或者可以遍历餐厅的整个菜单。
我们要用组合模式(Composite Pattern)实现这一部分
组合模式让我们能够用树形方式创建对象的结构,树里面包含了组合以及个别的对象。
使用组合结构,我们能够把相同的操作应用于组合和个别对象上。换句话说,在大多数情况下,我们可以忽略对象组合和个别对象之间的差别。
具体实现下面链接展示的更为清楚,这里就偷懒不在赘述了
组合模式:https://blog.csdn.net/lemon_tree12138/article/details/51437883
应用场景
优缺点
设计原则
封装变化
多用组合,少用继承
针对接口编程,不针对实现编程
为交互对象之间的松耦合设计而努力
类应该对扩展开放,对修改关闭
依赖抽象,不依赖具体类
最少知识原则:只和朋友交谈
单一责任:一个类应该只有一个引起变化的原因
总结
在多线程情况下可能会有多个迭代器引用同一个对象集合,remove()可能会造成不可测的影响,必须谨慎。
小知识
单一责任原则:一个类应该只有一个引起变化的原因。
内聚(cohesion)用来度量一个类或模块紧密地达到单一目的或责任。
当一个模块或类被设计成只支持一组相关的功能时,我们说它具有高内聚;反之,当被设计为支持一组不相关的功能时,我们说它具有低内聚。
内聚是一个比单一责任原则更普遍的概念,但两者的关系是很密切的。遵循这个原则的类很容易具有很高的凝聚力,而且比背负许多责任的低内聚类更容易维护。