标签:
1.任务:
大新闻!对象村餐厅和对象村煎饼屋合并了!可以在同一个地方吃早饭和午饭了hohoho(有什么好开森的对象村的小伙伴们好容易满足)。
但是有一个问题出现了:煎饼屋的菜单menu是用ArrayList记录菜单项menuItem,但是餐厅的菜单menu使用数组Array记录menuItem。大家都不愿意修改自己的结构,那么java女招待的任务就很繁重了啊,因为取出菜单项的方法就要记住两种了:menuItems.get(i)和menuItems[i]。
1 public class MenuItem { 2 String name; 3 String description; 4 boolean vagetarian; 5 double price; 6 public MenuItem(String name, String description, boolean vagetarian, 7 double price) { 8 super(); 9 this.name = name; 10 this.description = description; 11 this.vagetarian = vagetarian; 12 this.price = price; 13 } 14 public String getName() { 15 return name; 16 } 17 public String getDescription() { 18 return description; 19 } 20 public boolean isVagetarian() { 21 return vagetarian; 22 } 23 public double getPrice() { 24 return price; 25 } 26 }
1 public class PancakeHouseMenu { 2 ArrayList menuItems; 3 4 public PancakeHouseMenu(){ 5 menuItems = new ArrayList(); 6 7 addItem("liyuhui‘s pancake breakfast","with eggs and toast",true,2.99); 8 addItem("zhangwuan‘s pancake breakfast","with onion",true,3.99); 9 addItem("jiangwen‘s pancake breakfast","with beaf",false,5.99); 10 } 11 12 private void addItem(String name, String description, boolean vegetarian, double price) { 13 MenuItem menuItem = new MenuItem(name, description, vegetarian, price); 14 menuItems.add(menuItem); 15 } 16 17 public ArrayList getMenuItems(){ 18 return menuItems; 19 } 20 }
1 public class DinerMenu { 2 static final int MAX_ITEMS = 6; 3 int numberOfItems = 0; 4 MenuItem[] menuItems; 5 6 public DinerMenu(){ 7 menuItems = new MenuItem[MAX_ITEMS]; 8 9 addItem("liyuhui‘s lunch","with eggs",true,6.99); 10 addItem("zhangwuan‘s lunch","with potatoes",true,6.99); 11 addItem("jiangwen‘s lunch","with beaf",false,5.99); 12 } 13 14 private void addItem(String name, String description, boolean vegetarian, double price) { 15 MenuItem menuItem = new MenuItem(name, description, vegetarian, price); 16 if(numberOfItems >= MAX_ITEMS){ 17 System.out.println("menu is full"); 18 }else{ 19 menuItems[numberOfItems] = menuItem; 20 numberOfItems++; 21 } 22 } 23 24 public MenuItem[] getMenuItems(){ 25 return menuItems; 26 } 27 }
1 public class Waitress { 2 PancakeHouseMenu ohMenu = new PancakeHouseMenu(); 3 ArrayList breakfirst = ohMenu.getMenuItems(); 4 5 DinerMenu dMenu = new DinerMenu(); 6 MenuItem[] lunch = dMenu.getMenuItems(); 7 8 public void printMenu(){ 9 for(int i=0;i<breakfirst.size();i++){ 10 MenuItem menuItem = (MenuItem) breakfirst.get(i); 11 System.out.println(menuItem.getName()); 12 System.out.println(menuItem.getPrice()); 13 System.out.println(menuItem.getDescription()); 14 } 15 16 for(int i=0;i<lunch.length;i++){ 17 MenuItem menuItem = (MenuItem) lunch[i]; 18 System.out.println(menuItem.getName()); 19 System.out.println(menuItem.getPrice()); 20 System.out.println(menuItem.getDescription()); 21 } 22 } 23 }
那么问题来了:java女招待的任务是打印出所有的菜单项,显然需要两个for循环,因为元素类型不一样。(1)重复代码太多,显然不是好事情。(2)如果要加一份菜单,就需要再加一个循环,waitress这个类就需要无休止的change维护。
2.如何解决大部分重复的for循环问题:
思路:原则永远是封装变化的部分。这里变化的就是for循环,不同的集合类型,会造成遍历中的部分差别。那么我们就需要封装这个遍历,即使用迭代器模式~迭代器模式提供一种方法,顺序访问collection对象中的每一个元素,而又不暴露其内部的表示。
1 for(int i=0;i<breakfirst.size();i++){ 2 MenuItem menuItem = (MenuItem) breakfirst.get(i); 3 } 4 5 for(int i=0;i<lunch.length;i++){ 6 MenuItem menuItem = (MenuItem) lunch[i]; 7 }
1 Iterator iterator = (某一个menu).createIterator(); 2 while(iterator.hasNext()){ 3 MenuItem menuItem = (MenuItem)iterator.next(); 4 }
3.迭代器模式下的修改:
发生的变化:
(1)以前需要两个for循环来遍历,现在有了迭代器,可以多态的处理任何项的集合。
(2)waitress类以前依赖于具体类MenuItem[]和ArrayList,现在只使用一个接口Iterator(也就是说,不管以后item是以数组、list还是hashTable存储,都不用修改waitress的代码,这就是解耦)。
(3)waitress类依赖于具体类PancakeHouseMenu和DinerMenu类,现在还是如此(结构体那里需要传入,如果修改了类,比如是另外两种菜单类型,waitress类需要修改,所以还没解耦),待改进。
1 public interface Iterator { 2 boolean hasNext(); 3 Object next(); 4 } 5 6 public class DinerMenuIterator implements Iterator { 7 MenuItem[] items; 8 int position = 0; 9 10 public DinerMenuIterator(MenuItem[] items) { 11 super(); 12 this.items = items; 13 } 14 15 public boolean hasNext() { 16 if(items[position] == null || position >= items.length){ 17 return false; 18 }else{ 19 return true; 20 } 21 } 22 23 public Object next() { 24 MenuItem menuItem = items[position]; 25 position++; 26 return menuItem; 27 } 28 }
1 public class DinerMenu { 2 ... 3 // public MenuItem[] getMenuItems(){ 4 // return menuItems; 5 // } 6 7 public Iterator creatIterator(){ 8 return new DinerMenuIterator(menuItems); 9 } 10 }
1 public class Waitress { 2 PancakeHouseMenu phMenu = new PancakeHouseMenu(); 3 //ArrayList breakfirst = ohMenu.getMenuItems(); 4 5 DinerMenu dMenu = new DinerMenu(); 6 //MenuItem[] lunch = dMenu.getMenuItems(); 7 8 public Waitress(PancakeHouseMenu phMenu, DinerMenu dMenu) { 9 super(); 10 this.phMenu = phMenu; 11 this.dMenu = dMenu; 12 } 13 14 public void printMenu(){ 15 // for(int i=0;i<breakfirst.size();i++){ 16 // MenuItem menuItem = (MenuItem) breakfirst.get(i); 17 // System.out.println(menuItem.getName()); 18 // System.out.println(menuItem.getPrice()); 19 // System.out.println(menuItem.getDescription()); 20 // } 21 // 22 // for(int i=0;i<lunch.length;i++){ 23 // MenuItem menuItem = (MenuItem) lunch[i]; 24 // System.out.println(menuItem.getName()); 25 // System.out.println(menuItem.getPrice()); 26 // System.out.println(menuItem.getDescription()); 27 // } 28 29 Iterator pancakeIterator = phMenu.createIterator(); 30 Iterator dinerIterator = dMenu.creatIterator(); 31 printMenuItems(pancakeIterator); 32 printMenuItems(dinerIterator); 33 } 34 35 private void printMenuItems(Iterator iterator) { 36 while(iterator.hasNext()){ 37 MenuItem menuItem = (MenuItem) iterator.next(); 38 System.out.println(menuItem.getName()); 39 System.out.println(menuItem.getPrice()); 40 System.out.println(menuItem.getDescription()); 41 } 42 } 43 }
1 public class MenuTestDrive { 2 public static void main(String[] args){ 3 PancakeHouseMenu phMenu = new PancakeHouseMenu(); 4 DinerMenu dMenu = new DinerMenu(); 5 6 Waitress waitress = new Waitress(phMenu,dMenu); 7 waitress.printMenu(); 8 9 } 10 }
4.使用java的iterator接口:
前面从头到尾都是自己在创建Iterator接口,然后实现具体类。但是实际上很多类在java里已经实现了Iterator接口,比如ArrayList,但是数组没有(所以还是要自己实现,但是不能使用刚刚的DinerMenuIterator,因为两个menu的Iterator要实现同一个接口!!!!)
1 import java.util.Iterator; 2 public class PancakeHouseMenu { 3 ... 4 public Iterator createIterator(){ 5 //return new PancakeMenuIterator(menuItems); 6 return menuItems.iterator(); 7 } 8 }
1 import java.util.Iterator; 2 public class DinerMenuIterator implements Iterator { 3 ... 4 public void remove() { 5 if(position<=0){ 6 throw new IllegalStateException 7 ("you can‘t remove an item until you‘ve done at least one next()"); 8 } 9 if(items[position-1]!=null){ 10 //数组删除元素,要将后面的全体前移一个单位 11 for(int i=position-1;i<items.length-1;i++){ 12 items[i] = items[i+1]; 13 } 14 items[items.length-1]=null; 15 } 16 } 17 }
5.改进3中的(3)问题:waitress还依赖于两个具体的菜单类PancakeHouseMenu和DinerMenu。增加接口Menu,waitress中全部只出现Menu,所以实现了针对接口编程。
1 import java.util.Iterator; 2 public interface Menu { 3 public Iterator createIterator(); 4 } 5 6 public class PancakeMenu implements Menu{} 7 8 public class DinerMenu implements Menu{}
1 import java.util.Iterator; 2 public class Waitress { 3 Menu phMenu = new PancakeHouseMenu(); 4 Menu dMenu = new DinerMenu(); 5 6 public Waitress(Menu phMenu, Menu dMenu) { 7 super(); 8 this.phMenu = phMenu; 9 this.dMenu = dMenu; 10 } 11 ... 12 }
1 public class MenuTestDrive { 2 public static void main(String[] args){ 3 Menu phMenu = new PancakeHouseMenu(); 4 Menu dMenu = new DinerMenu(); 5 6 Waitress waitress = new Waitress(phMenu,dMenu); 7 waitress.printMenu(); 8 9 } 10 }
6.现在咖啡厅也要作为合伙人啦 ,这样对象村的小伙伴们可以在同一个地方喝咖啡,吃早饭然后吃午饭了!(这样真的好吗~-_-)而咖啡厅一向逼格比较高,它的菜单项item是用hashtable存储的,该怎么做?
原码如下:
1 public class CafeMenu{ 2 Hashtable menuItems = new Hashtable(); 3 4 public CafeMenu(){ 5 addItem("liyuhui‘s cafe","with sugar",true,6.99); 6 addItem("zhangwuan‘s cafe","with milk",true,6.99); 7 addItem("jiangwen‘s cafe","with nothing",false,5.99); 8 } 9 10 private void addItem(String name, String description, boolean vegetarian, double price) { 11 MenuItem menuItem = new MenuItem(name, description, vegetarian, price); 12 menuItems.put(menuItem.getName(), menuItem); 13 } 14 15 public Hashtable getItems(){ 16 return menuItems; 17 } 18 }
(2)修改后:(注意hashtable是menuItems.values().iterator())
1 public class CafeMenu implements Menu { 2 ... 3 // public Hashtable getItems(){ 4 // return menuItems; 5 // } 6 7 public Iterator createIterator() { 8 // TODO Auto-generated method stub 9 return menuItems.values().iterator(); 10 } 11 }
(3)waitress的结构体增加一个menu CafeMenu即可。
那么问题来了:增加一份菜单,就要将waitress的结构体修改下,并且测试程序也要修改,要调用print三次,显然也不是件很妙的事情。
解决办法:将菜单也打包遍历。
1 public class Waitress { 2 ArrayList menus; 3 4 public Waitress(ArrayList menus) { 5 super(); 6 this.menus = menus; 7 } 8 9 public void printMenu(){ 11 Iterator menuIterator = menus.iterator(); 12 while(menuIterator.hasNext()){ 13 Menu menu = (Menu) menuIterator.next(); 14 printMenuItems(menu.createIterator()); 15 } 16 } 17 ... 18 }
7.“三合一吃饭地”又要增加新功能了,希望能够加上一份餐后甜点的“子菜单”,即dinerMenu中有一项是餐后甜点,这一项里面又有很多菜单项,比如说臭豆腐,辣条和广东蛋筒(嗯~我会选广东蛋筒)。
明确任务:之前的结构已经不足以支持这个功能了。那么我们需要什么呢?(我们需要组合模式!!!)
(1)需要某种树形结构,可以容纳菜单,子菜单,子子菜单...然后菜单项。
(2)我们要能够在每个菜单的各个item中游走遍历,也能够选择是遍历所有item还是只遍历某一个菜单的item。
解决办法:创建一个组件接口component作为菜单menu和菜单项menuItem的公共接口,就能用统一的做法来处理菜单menu和菜单项menuItem,然后分别实现menu和menuItem。
1 public abstract class MenuComponent { 2 public void add(MenuComponent menuComponent){ 3 throw new UnsupportedOperationException(); 4 } 5 public void remove(MenuComponent menuComponent){ 6 throw new UnsupportedOperationException(); 7 } 8 public MenuComponent getChild(int i){ 9 throw new UnsupportedOperationException(); 10 } 11 12 public String getName(){ 13 throw new UnsupportedOperationException(); 14 } 15 public String getDescription(){ 16 throw new UnsupportedOperationException(); 17 } 18 public double getPrice(){ 19 throw new UnsupportedOperationException(); 20 } 21 public boolean isVegetarian(){ 22 throw new UnsupportedOperationException(); 23 } 24 25 public void print(){ 26 throw new UnsupportedOperationException(); 27 } 28 }
1 public class MenuItem extends MenuComponent{ 2 //和以前一样的省略 3 ... 4 //超类中的所有方法都是默认实现,只有需要更改的子类中才覆盖 5 public void Print(){ 6 System.out.print(" "+getName()); 7 if(isVegetarian()){ 8 System.out.print("(v)"); 9 } 10 System.out.println(","+getPrice()); 11 System.out.print(" ---"+getDescription()); 12 } 13 }
1 import java.util.ArrayList; 2 import java.util.Iterator; 3 public class Menu extends MenuComponent{ 4 ArrayList menuComponents = new ArrayList(); 5 String name; 6 String description; 7 public Menu(String name, String description) { 8 super(); 9 this.name = name; 10 this.description = description; 11 } 12 13 public void add(MenuComponent menuComponent){ 14 menuComponents.add(menuComponents); 15 } 16 public void remove(MenuComponent menuComponent){ 17 menuComponents.remove(menuComponents); 18 } 19 public MenuComponent getChild(int i){ 20 return (MenuComponent) menuComponents.get(i); 21 } 22 23 public String getName(){ 24 return name; 25 } 26 public String getDescription(){ 27 return description; 28 } 29 30 public void print(){ 31 System.out.print("\n"+getName()); 32 System.out.print(" ---"+getDescription()); 33 System.out.print("----------------------------");34 } 35 }
1 import java.util.ArrayList; 2 import java.util.Iterator; 3 public class Waitress { 4 //waitress需要接触的只有最上层菜单,一个元素。 5 MenuComponent allMenus; 6 7 public Waitress(MenuComponent allMenus) { 8 super(); 9 this.allMenus = allMenus; 10 } 11 12 public void printMenu(){ 13 allMenus.print(); 14 } 15 }
1 public class MenuTestDrive { 2 public static void main(String[] args){ 3 MenuComponent allMenus = new Menu("ALL MENUS","All menus together"); 4 5 MenuComponent pancakeHouseMenu = new PancakeHouseMenu("pancake menu","breakfast"); 6 MenuComponent dinerMenu = new DinerMenu("diner menu","lunch"); 7 MenuComponent cafeMenu = new CafeMenu("cafe menu","cafe"); 8 MenuComponent dessertMenu = new DessertMenu("dessert menu","dessert"); 9 10 //addItem的就略了 11 12 dinerMenu.add(dessertMenu); 13 14 allMenus.add(pancakeHouseMenu); 15 allMenus.add(dinerMenu); 16 allMenus.add(cafeMenu); 17 18 Waitress waitress = new Waitress(allMenus); 19 waitress.printMenu(); 20 } 21 }
8.如何使用迭代器遍历整个组合:(有错误,没找出原因)
为了实现遍历封装,在menuComponent中添加createIterator,并且在item和menu中都分别实现各自的iterator。这里用到了nullIterator,如果不这样,而是return null,这样客户代码就不需要if来判断返回值是否为null。(前面也出现过这种手法,就是建立一个什么都不做的方法或者类,这样就不用if判断了)
1 //之前的menu 2 public void print(){ 3 System.out.print("\n"+getName()); 4 System.out.print(" ---"+getDescription()); 5 System.out.print("----------------------------"); 6 } 7 8 //现在的menu 9 public void print(){ 10 System.out.print("\n"+getName()); 11 System.out.print(" ---"+getDescription()); 12 System.out.print("----------------------------"); 13 //递归遍历 14 Iterator iterator = menuComponents.iterator(); 15 while(iterator.hasNext()){18 //在这里出现了错误:java.util.ArrayList cannot be cast to unit6.iterator.MenuComponent 19 MenuComponent menuComponent = (MenuComponent) iterator.next(); 20 menuComponent.print();//如果下面还有子菜单,会递归调用,直至到菜单项而非菜单 21 } 22 }
1 import java.util.Iterator; 2 3 public abstract class MenuComponent{ 4 ... 5 public abstract Iterator createIterator(); 6 } 7 8 public class MenuItem extends MenuComponent{ 9 ... 10 public Iterator createIterator(){ 11 return new NullIterator(); 12 } 13 } 14 15 public class Menu extends MenuComponent{ 16 ... 17 public void print(){ 18 System.out.print("\n"+getName()); 19 System.out.print(" ---"+getDescription()); 20 System.out.print("----------------------------"); 21 //递归遍历 22 Iterator iterator = menuComponents.iterator(); 23 while(iterator.hasNext()){ 26 //在这里出现了错误:java.util.ArrayList cannot be cast to unit6.iterator.MenuComponent 27 MenuComponent menuComponent = (MenuComponent) iterator.next(); 28 menuComponent.print();//如果下面还有子菜单,会递归调用,直至到菜单项而非菜单 29 } 30 } 31 32 public Iterator createIterator(){ 33 //return menuComponents.iterator(); 34 return new CompositeIterator(menuComponents.iterator()); 35 } 36 }
1 public class CompositeIterator implements Iterator { 2 Stack stack = new Stack(); 3 public CompositeIterator(Iterator iterator) { 4 stack.push(iterator); 5 } 6 7 public boolean hasNext() { 8 if(stack.empty()){ 9 return false; 10 }else{ 11 Iterator iterator = (Iterator) stack.peek(); 12 if(!iterator.hasNext()){ 13 stack.pop(); 14 return hasNext(); 15 }else{ 16 return true; 17 } 18 } 19 } 20 21 public Object next() { 22 if(hasNext()){ 23 Iterator iterator = (Iterator) stack.peek(); 24 MenuComponent component = (MenuComponent) iterator.next(); 25 if(component instanceof Menu){ 26 stack.push(component.createIterator()); 27 } 28 //一定要返回component,不然又会出现类型错误 29 return component; 30 }else{ 31 return null; 32 } 33 34 } 35 36 public void remove() { 37 throw new UnsupportedOperationException(); 38 } 39 40 }
1 public class NullIterator implements Iterator { 2 3 public boolean hasNext() { 4 return false; 5 } 6 7 public Object next() { 8 return null; 9 } 10 11 public void remove() { 12 throw new UnsupportedOperationException(); 13 } 14 15 }
综上所述:当你有数个对象的集合(collection),他们彼此之间有整体和部分的关系,并且你想要用一致的方式对待这些对象,就使用组合模式。
标签:
原文地址:http://www.cnblogs.com/liyuhui21310122/p/4471427.html