码迷,mamicode.com
首页 > 其他好文 > 详细

headFirst学习笔记之九:迭代器与组合模式(5.1)

时间:2015-05-02 06:14:03      阅读:229      评论:0      收藏:0      [点我收藏+]

标签:

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),他们彼此之间有整体和部分的关系,并且你想要用一致的方式对待这些对象,就使用组合模式。

headFirst学习笔记之九:迭代器与组合模式(5.1)

标签:

原文地址:http://www.cnblogs.com/liyuhui21310122/p/4471427.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!