如果餓了就喫,困了就睡,渴了就喝,人生就太無趣了
代碼地址: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();
}
}