如果饿了就吃,困了就睡,渴了就喝,人生就太无趣了
代码地址:https://github.com/keer123456789/MY_STUDY_LIFE/tree/master/src/main/java/Head_First/Module/Module_Iterator
一、迭代器模式
1.概念
1.1 定义
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
1.2 类图
如图1,接口Iterator
是一个迭代器接口,所有迭代器需要实现的接口,一般使用java.util.Iterator
接口,也可以自己构造接口。接口Aggregate
为所有聚合所使用,提供一个返回迭代器的方法createIterator()
;各个具体的聚合ConcreteAggregate
实现这个接口。从而实现顺序访问聚合中的对象。
2.实例(餐饮店合并)
现在有一家提供早餐的蛋糕店和提供正餐的餐厅需要合并,他们的菜单系统中的每一个菜单项都是使用MenuItem.java
实现的。如图2:一共有四个成员变量和相应的getter方法。
-
name:菜品的名字,类型是
String
-
description:菜品描述,类型是
String
-
vegetarian:是否为素食,类型是
boolean
-
price:菜品价格,类型是
double
现在需要将两家餐厅的菜单合并成为一个,并进行打印输出,出现了问题:
因为菜单是菜品的集合形式,两家采用的集合不同:
- 早餐店的菜单是使用
ArrayList
实现的 - 餐厅使用的是数组实现的
由于合并,服务员代码中打印菜单的方法printMenu()
是要把所有菜品全都列出来,因为两种集合遍历的方式不同,需要两个不同的循环。
如图3:ArrayList
集合的遍历,通过内置的get()
方法获取某个位置的元素
如图4:数组的遍历通过下标来实现获取元素
现在的做法如果采用两种循环来遍历集合,但是抽象出来就是遍历集合的动作,所以采用了迭代器模式,将不同的遍历动作抽象出相同的动作。这样以后还有别的菜单集合加入,不用修改太多的代码。
2.1 DIY迭代器
2.1.1 类图
如图5,
- 迭代器接口
Iterator
只有hasNext()
和next()
两个函数 - 接口
Iterator
有两个实例分别是PancakeHouseMenuIterator
和DinerMenuItator
,是两家餐厅的菜单迭代器。 PancakeHouseMenu
和DinerMenu
是两家餐厅的菜单,可以看到,他们都有MenuItem
的集合,只是表现形式不同,都有一个共同的方法createIterator()
,都是用来创建相应的迭代器。- 这两个菜单有这么多的相似之处,在后面会将其改进。
2.1.2 代码
1.迭代器接口和实现,连个实例实现相应的hasNext()
和next()
方法
public interface Iterator {
boolean hasNext();
Object next();
}
public class DinerMenuIterator implements Iterator {
MenuItem[] items;
int position = 0;
public DinerMenuIterator(MenuItem[] items) {
this.items = items;
}
public Object next() {
MenuItem menuItem = items[position];
position = position + 1;
return menuItem;
}
public boolean hasNext() {
if (position >= items.length || items[position] == null) {
return false;
} else {
return true;
}
}
}
public class PancakeHouseIterator implements Iterator {
ArrayList<MenuItem> list;
int position = 0;
public PancakeHouseIterator(ArrayList<MenuItem> list) {
this.list = list;
}
@Override
public boolean hasNext() {
if (position >= list.size() || list.get(position) == null) {
return false;
}else{
return true;
}
}
@Override
public Object next() {
MenuItem menuItem = list.get(position);
position = position + 1;
return menuItem;
}
}
2.菜单项和两个菜单代码
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 double getPrice() {
return price;
}
public boolean isVegetarian(){
return vegetarian;
}
}
public class DinerMenu {
static final int MAX_ITEMS = 6;
int numberOfItems = 0;
MenuItem[] menuItems;
public DinerMenu() {
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, 2.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.05);
addItem("Steamed Veggies and Brown Rice",
"Steamed vegetables over brown rice", true, 3.99);
addItem("Pasta",
"Spaghetti with Marinara Sauce, and a slice of sourdough bread",
true, 3.89);
}
public void addItem(String name, String description, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
if (numberOfItems >= MAX_ITEMS) {
System.err.println("Sorry, menu is full! Can't add item to menu");
} else {
menuItems[numberOfItems] = menuItem;
numberOfItems = numberOfItems + 1;
}
}
public Iterator createIterator() {
return new DinerMenuIterator(menuItems);
//return new AlternatingDinerMenuIterator(menuItems);
}
// other menu methods here
}
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 fried eggs, sausage",
false,
2.99);
addItem("Blueberry Pancakes",
"Pancakes made with fresh blueberries",
true,
3.49);
addItem("Waffles",
"Waffles, with your choice of blueberries or strawberries",
true,
3.59);
}
public void addItem(String name, String description,
boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
menuItems.add(menuItem);
}
public Iterator createIterator() {
return new PancakeHouseIterator(menuItems);
//return new AlternatingDinerMenuIterator(menuItems);
}
}
3.服务员代码和测试
public class Waitress {
PancakeHouseMenu pancakeHouseMenu;
DinerMenu dinerMenu;
public Waitress(PancakeHouseMenu pancakeHouseMenu, DinerMenu dinerMenu) {
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinerMenu = dinerMenu;
}
public void printMenu() {
Iterator pancakeIterator = this.pancakeHouseMenu.createIterator();
Iterator dinerIterator = this.dinerMenu.createIterator();
System.out.println("MENU\n--------\nBREAKFAST");
printMenu(pancakeIterator);
System.out.println("\nLUNCH");
printMenu(dinerIterator);
}
private void printMenu(Iterator iterator){
while (iterator.hasNext()){
MenuItem menuItem= (MenuItem) iterator.next();
System.out.println(menuItem.getName()+",");
System.out.print(menuItem.getPrice()+"--");
System.out.println(menuItem.getDescription());
}
}
}
public class MenuTestDrive {
public static void main(String[] args) {
PancakeHouseMenu pancakeHouseMenu=new PancakeHouseMenu();
DinerMenu dinerMenu=new DinerMenu();
Waitress waitress=new Waitress(pancakeHouseMenu,dinerMenu);
waitress.printMenu();
}
}
2.2 使用内部Iterator
实现
java内部提供了一个迭代器接口,比上一个迭代器接口多了一个remove()
方法。该方法是用来删除当前位置的元素。这让菜单的菜品增加和删除提高了灵活性。
2.2.1 类图
如图6:
- 早餐店的菜单是用
ArrayList
实现的,ArrayList
已经实现了Iterator
接口,所以不需PancakeHouseMenuIterator
这个类。 - 午餐店的菜单是数组实现的,没有实现迭代器接口,所以还是需要
DinerMenuIterator
类实现Interator
接口。只是这次需要多实现一个remove
方法。 - 因为刚才发现每个菜单都需要使用
createIterator()
,所以将其抽象出来,形成接口Menu
,此接口只有一个createIterator()
方法。
2.2.2 代码
1.菜单接口和实现类
public interface Menu {
public Iterator<?> createIterator();
}
public class DinerMenu implements Menu{
static final int MAX_ITEMS = 6;
int numberOfItems = 0;
MenuItem[] menuItems;
public DinerMenu() {
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, 2.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.05);
addItem("Steamed Veggies and Brown Rice",
"Steamed vegetables over brown rice", true, 3.99);
addItem("Pasta",
"Spaghetti with Marinara Sauce, and a slice of sourdough bread",
true, 3.89);
}
public void addItem(String name, String description, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
if (numberOfItems >= MAX_ITEMS) {
System.err.println("Sorry, menu is full! Can't add item to menu");
} else {
menuItems[numberOfItems] = menuItem;
numberOfItems = numberOfItems + 1;
}
}
public Iterator createIterator() {
return new DinerMenuIterator(menuItems);
//return new AlternatingDinerMenuIterator(menuItems);
}
// other menu methods here
}
public class PancakeHouseMenu implements Menu{
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 fried eggs, sausage",
false,
2.99);
addItem("Blueberry Pancakes",
"Pancakes made with fresh blueberries",
true,
3.49);
addItem("Waffles",
"Waffles, with your choice of blueberries or strawberries",
true,
3.59);
}
public void addItem(String name, String description,
boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
menuItems.add(menuItem);
}
public Iterator createIterator() {
return menuItems.iterator();
//return new AlternatingDinerMenuIterator(menuItems);
}
}
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 double getPrice() {
return price;
}
public boolean isVegetarian(){
return vegetarian;
}
}
2.DinerMenuIterator
类
public class DinerMenuIterator implements Iterator {
MenuItem[] items;
int position = 0;
public DinerMenuIterator(MenuItem[] items) {
this.items = items;
}
public Object next() {
MenuItem menuItem = items[position];
position = position + 1;
return menuItem;
}
public boolean hasNext() {
if (position >= items.length || items[position] == null) {
return false;
} else {
return true;
}
}
public void remove() {
if (position <= 0) {
throw new IllegalStateException("You can't remove an item until you've done at least one next()");
}
if (items[position] != null) {
for (int i = position - 1; i < (items.length - 1); i++) {
items[i] = items[i + 1];
}
items[items.length - 1] = null;
}
}
}
3.服务员和测试代码
public class Waitress {
Menu pancakeHouseMenu;
Menu dinerMenu;
public Waitress(Menu pancakeHouseMenu, Menu dinerMenu) {
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinerMenu = dinerMenu;
}
public void printMenu() {
Iterator pancakeIterator = this.pancakeHouseMenu.createIterator();
Iterator dinerIterator = this.dinerMenu.createIterator();
System.out.println("MENU\n--------\nBREAKFAST");
printMenu(pancakeIterator);
System.out.println("\nLUNCH");
printMenu(dinerIterator);
}
private void printMenu(Iterator iterator){
while (iterator.hasNext()){
MenuItem menuItem= (MenuItem) iterator.next();
System.out.println(menuItem.getName()+",");
System.out.print(menuItem.getPrice()+"--");
System.out.println(menuItem.getDescription());
}
}
}
public class MenuTestDrive {
public static void main(String[] args) {
Menu pancakeHouseMenu=new PancakeHouseMenu();
Menu dinerMenu=new DinerMenu();
Waitress waitress=new Waitress(pancakeHouseMenu,dinerMenu);
waitress.printMenu();
}
}
二、组合模式
1.概念
1.1 定义
允许你将对象组合成树形结构来表现整体/部分层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
如图7:
- Node节点时带有子元素的节点
- 没有子元素的节点成为叶子节点
- 这样就忽略了组合项和组合的差异
1.2 类图
如图8,
- 客户使用
Component
接口操作组合中的对象 - 定义
Component
抽象类,适用于组合和组合项的一个接口,提供默认实现。 - 叶子结点
Leaf
和Composite
组合对于接口中的某些方法没有实现,可能对它本身没有意义。
2. 引例(继续餐厅合并例子)
2.1 合并咖啡厅
刚刚合并的餐厅因为资金到位,又合并一家咖啡厅,咖啡厅的菜单使用的Hashtable
集合实现,这种集合的是一种key-value
键值对的数据格式实现的。如图9:
咖啡厅的菜单同样实现Menu
接口就可以,但是实现createIterator()
的代码如下:
public Iterator createIterator(){
return menuItems.values().iterator();
}
这样服务员中的print()
方法如下:
public void printMenu() {
Iterator pancakeIterator = this.pancakeHouseMenu.createIterator();
Iterator dinerIterator = this.dinerMenu.createIterator();
Iterator cafeIterator = this.cafeMenu.createIterator();
System.out.println("MENU\n--------\nBREAKFAST");
printMenu(pancakeIterator);
System.out.println("\nLUNCH");
printMenu(dinerIterator);
System.out.println("\nCafe");
printMenu(cafeIterator);
}
出现问题:
每一次有新的菜单加入,就会改动服务员代码,违反了开放-关闭原则
,每一次增加和删除菜单,就会改动print()
方法。
2.2 优化餐厅菜单
将餐厅的三种菜单都放入ArrayList
集合中。这样就解决了三次打印的菜单的重复操作,也解决了每一次增加菜单的对服务员代码的修改。优化之后的服务员代码
public class Waitress{
ArrayList menus;
public Waitress(ArrayList menus){
this.menus = menus;
}
public void printMenu(){
Iterator menuIterator = menus.iterator;
while(menuIterator.hasNext()){
Menu menu = (Menu)menuIterator.next();
printMenu(menu.createIterator());
}
}
private void printMenu(Iterator iterator){
while (iterator.hasNext()){
MenuItem menuItem= (MenuItem) iterator.next();
System.out.println(menuItem.getName()+",");
System.out.print(menuItem.getPrice()+"--");
System.out.println(menuItem.getDescription());
}
}
}
2.3 增加甜品菜单
现在需要在餐厅的菜单中将甜点菜品单独列出来,形成甜点菜单,加入餐厅菜单中。如图10:
因为餐厅菜单使用的数组实现的,数组元素是菜品MenuItem
,现在加入一个数组肯定是行不通的。类型不同。
这是就需要改动现有设计了,加入组合模式。将菜单设计成为树形结构,将菜单项和菜单差异抹去,突出其共同点,如图11:
2.4新设计类图
如图12:
- 菜单和菜单项都实现
MenuComponent
抽象类,根据自己的需要,实现相应的方法。 - 菜品
MenuItem
作为叶子节点,叶子节点就只实现getName()
,getDescription()
,getPrice()
,isVegetarian()
和print()
五个方法 - 菜单
Menu
作为一个子节点,实现了与其相关的6个方法:getName()
,getDescription()
,print()
,add(Component)
,remove(Component)
和getChild(int)
2.5 代码
1.抽象类MenuComponent
类中每一个方法都有默认实现,抛出UnsupportedOperationException
异常
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 getDescription() {
throw new UnsupportedOperationException();
}
public double getPrice() {
throw new UnsupportedOperationException();
}
public boolean isVegetarian() {
throw new UnsupportedOperationException();
}
public void print() {
throw new UnsupportedOperationException();
}
}
2.菜单的实现
public class Menu extends MenuComponent {
private ArrayList<MenuComponent> menuComponents = new ArrayList<MenuComponent>();
private String name;
private String description;
public Menu(String name, String description) {
this.name = name;
this.description = description;
}
public void add(MenuComponent menuComponent) {
menuComponents.add(menuComponent);
}
public void remove(MenuComponent menuComponent) {
menuComponents.remove(menuComponent);
}
public MenuComponent getChild(int i) {
return (MenuComponent) menuComponents.get(i);
}
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 menuComponent =
(MenuComponent) iterator.next();
menuComponent.print();
}
}
}
3.菜单项的实现
public class MenuItem extends MenuComponent {
private String name;
private String description;
private boolean vegetarian;
private 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 double getPrice() {
return price;
}
public boolean isVegetarian() {
return vegetarian;
}
public void print() {
System.out.print(" " + getName());
if (isVegetarian()) {
System.out.print("(v)");
}
System.out.println(", " + getPrice());
System.out.println(" -- " + getDescription());
}
}
4.服务员和测试代码
public class Waitress {
MenuComponent allMenus;
public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
}
public void printMenu() {
allMenus.print();
}
}
public class MenuTestDrive {
public static void main(String args[]) {
MenuComponent pancakeHouseMenu =
new Menu("PANCAKE HOUSE MENU", "Breakfast");
MenuComponent dinerMenu =
new Menu("DINER MENU", "Lunch");
MenuComponent cafeMenu =
new Menu("CAFE MENU", "Dinner");
MenuComponent dessertMenu =
new Menu("DESSERT MENU", "Dessert of course!");
MenuComponent allMenus = new Menu("ALL MENUS", "All menus combined");
allMenus.add(pancakeHouseMenu);
allMenus.add(dinerMenu);
allMenus.add(cafeMenu);
pancakeHouseMenu.add(new MenuItem(
"K&B's Pancake Breakfast",
"Pancakes with scrambled eggs, and toast",
true,
2.99));
pancakeHouseMenu.add(new MenuItem(
"Regular Pancake Breakfast",
"Pancakes with fried eggs, sausage",
false,
2.99));
pancakeHouseMenu.add(new MenuItem(
"Blueberry Pancakes",
"Pancakes made with fresh blueberries, and blueberry syrup",
true,
3.49));
pancakeHouseMenu.add(new MenuItem(
"Waffles",
"Waffles, with your choice of blueberries or strawberries",
true,
3.59));
dinerMenu.add(new MenuItem(
"Vegetarian BLT",
"(Fakin') Bacon with lettuce & tomato on whole wheat",
true,
2.99));
dinerMenu.add(new MenuItem(
"BLT",
"Bacon with lettuce & tomato on whole wheat",
false,
2.99));
dinerMenu.add(new MenuItem(
"Soup of the day",
"A bowl of the soup of the day, with a side of potato salad",
false,
3.29));
dinerMenu.add(new MenuItem(
"Hotdog",
"A hot dog, with saurkraut, relish, onions, topped with cheese",
false,
3.05));
dinerMenu.add(new MenuItem(
"Steamed Veggies and Brown Rice",
"Steamed vegetables over brown rice",
true,
3.99));
dinerMenu.add(new MenuItem(
"Pasta",
"Spaghetti with Marinara Sauce, and a slice of sourdough bread",
true,
3.89));
dinerMenu.add(dessertMenu);
dessertMenu.add(new MenuItem(
"Apple Pie",
"Apple pie with a flakey crust, topped with vanilla icecream",
true,
1.59));
dessertMenu.add(new MenuItem(
"Cheesecake",
"Creamy New York cheesecake, with a chocolate graham crust",
true,
1.99));
dessertMenu.add(new MenuItem(
"Sorbet",
"A scoop of raspberry and a scoop of lime",
true,
1.89));
cafeMenu.add(new MenuItem(
"Veggie Burger and Air Fries",
"Veggie burger on a whole wheat bun, lettuce, tomato, and fries",
true,
3.99));
cafeMenu.add(new MenuItem(
"Soup of the day",
"A cup of the soup of the day, with a side salad",
false,
3.69));
cafeMenu.add(new MenuItem(
"Burrito",
"A large burrito, with whole pinto beans, salsa, guacamole",
true,
4.29));
Waitress waitress = new Waitress(allMenus);
waitress.printMenu();
}
}