Head First Design Mode(11)-迭代器与组合模式

该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!

 

迭代器与组合模式: 

    管理良好的集合;

    有许多方法可以把对象堆起来成为一个集合(collection),使用数组、堆栈、列表或者是散列表(Hashtable)中;

    本章学习如何让客户端遍历你的对象而又无法窥视你存储对象的方式;

    也将学习一些对象超集合(super collection),还有一些关于对象职责的知识;

 

对象村餐厅和煎饼村合并了:

    现在需要合并菜单项,但是餐厅的菜单项是用数组来维护,而煎饼屋用的是ArrayList;

 

我们来看下菜单项的类:这个类是通用的;

【Code MenuItem.java】

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 boolean isVegetarian(){
        return vegetarian;
    }
    public double getPrice(){
        return price;
    }
}

我们再来看两个餐厅是如何对菜单项进行维护的:

【Code PancakeHouseMenu.java】

import java.util.*;

public class PancakeHouseMenu{
    ArrayList<MenuItem> menuItems;
    
    public PancakeHouseMenu(){
        menuItems = new ArrayList<MenuItem>();
        
        addItem("Pancake1","Pancake1 is nice!",true,2.00);
        addItem("Pancake2","Pancake2 is nice!",true,2.00);

    }
    
    public void addItem(String name, String description, boolean vegetarian, double price){
        MenuItem menuItem = new MenuItem(name,description,vegetarian,price);
        menuItems.add(menuItem);
    }
    
    public ArrayList getMenuItems(){
        return menuItems;
    }
    
    //other methods
    
}

【Code DinerMenu.java】

public class DinerMenu{
    static final int MAX_ITEMS = 6;
    int numberOfItems = 0;
    
    MenuItem[] menuItems;
    
    public DinerMenu(){
        menuItems = new MenuItem[MAX_ITEMS];
        
        addItem("Diner1","Diner1 is nice!",true,2.00);
        addItem("Diner2","Diner2 is nice!",true,2.00);

    }
    
    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("Menu is full");
        }else{
            menuItems[numberOfItems] = menuItem;
            numberOfItems = numberOfItems + 1;
        }
    }
    
    public MenuItem[] getMenuItems(){
        return menuItems;
    }
    
    public int getLength(){
        return numberOfItems;
    }
    
    //other methods
    
}

合并之后给女招待带来了麻烦,而且女招待有一套自己的标准:

她希望:

    printMenu()-打印菜单的每一项

    printBreakfastMenu()-只打印早餐项

    printLunchMenu()-只打印午餐项

    printVegetarianMenu()-打印所有的素食菜单项

    isItemVegetarian(name)-指定项的名称,素食返回true

 

按照现在两个Menu提供的getMenuItems()方法,为满足女招待的printMenu()则需要两个for循环,分别遍历ArrayList和数组;

【Code Waiter.java】

import java.util.*;

public class Waiter{
    
    void printMenu(){
        //-打印菜单的每一项
        PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
        ArrayList breakfastItems = pancakeHouseMenu.getMenuItems();
        
        DinerMenu dinerMenu = new DinerMenu();
        MenuItem[] lunchItems = dinerMenu.getMenuItems();
        int lunchItemsLength = dinerMenu.getLength();
        
        System.out.println(breakfastItems.size());
        System.out.println(lunchItems.length);//不能这样 获取 长度 
            
        for (int i = 0; i < breakfastItems.size(); i++){
            MenuItem menuItem = (MenuItem)breakfastItems.get(i);
            System.out.println(menuItem.getName() + " " + menuItem.getDescription() + " " + menuItem.getPrice());
        }
            
        for (int i = 0; i < lunchItemsLength; i++){
            MenuItem menuItem = lunchItems[i];
            System.out.println(menuItem.getName() + " " + menuItem.getDescription() + " " + menuItem.getPrice());
        }
    
        
    }
    void printBreakfastMenu(){
        //-只打印早餐项
    
        
    }
    void printLunchMenu(){
        //-只打印午餐项
        
    }
    
    void printVegetarianMenu(){
        //-打印所有的素食菜单项
        
    }
    
    boolean isItemVegetarian(String name){
        //-指定项的名称,素食返回true
        
        return true;
        
    }
    
    public static void main(String[] args){
        Waiter waiter = new Waiter();
        waiter.printMenu();
    }
    
}

更糟糕的是如果还有第三家的话,就需要三个循环;而且每家店都不想改变自身维护菜单项的方式

如果能找到一个方法,让他们的菜单实现一个接口,该有多好!我们可以尝试封装遍历;

 

封装遍历:

    创建一个对象,称之为 迭代器(Iterator),利用它来封装“遍历集合内的每一个对象的过程”。

 

实际的调用可能是这样的:

 

迭代器模式:

    如上所示的对遍历的封装对应的设计模式就称为迭代器模式(Iterator Pattern);

    首先我们需要知道的是:迭代器模式依赖于一个名为迭代器的接口;

    用此接口,我们可以为各种对象集合实现迭代器:数组、列表、散列表…

 

集合(collection)指的是一群对象,有时候也被称为聚合(aggregate);

 

我们接下来尝试下:在餐厅菜单中加入一个迭代器;

    首先我们定义一个接口:Iterator;

    然后与DinerMenu挂钩,这需要一个具体的迭代器类;(PancakeHouseMenu的我们也一起实现下)

 

【Code Iterator.java】

public interface Iterator{
    boolean hasNext();
    Object next();
    
}

【Code DinerMenuIterator.java】

public class DinerMenuIterator implements Iterator{

    MenuItem[] menuItems;
    int position = 0;

    
    public DinerMenuIterator(MenuItem[] items){
        this.menuItems = items;
    }
    
    public boolean hasNext(){
        //使用固定长度的数组 既要检查数组长度 还要检查下一项是否是null
        if (position >= menuItems.length || menuItems[position] == null){
            return false;
        }else{
            return true;
        }
    }
    public Object next(){
        MenuItem menuItem = menuItems[position];
        position = position + 1;
        return menuItem;
    }
    
    //如果使用 java.utl.Iterator 这个remove方法需要实现
    public void remove(){
        if (position <= 0){
            throw new IllegalStateException("null items can not be removed");
        }
        if (list[position - 1] != null){
            for (int i = position - 1; i < (list.length - 1); i++){
                list[i] = list[i + 1]
            }
            list[list.length - 1] = null;
        }
        
    }
    
}

【Code PancakeHouseMenuIterator.java】

import java.util.*;

public class PancakeHouseMenuIterator implements Iterator{

    ArrayList<MenuItem> menuItems;
    int position = 0;

    public PancakeHouseMenuIterator(ArrayList<MenuItem> items){
        this.menuItems = items;
    }
    
    public boolean hasNext(){
        if (position >= menuItems.size()){
            return false;
        }else{
            return true;
        }
    }
    public Object next(){
        MenuItem menuItem = menuItems.get(position);
        position = position + 1;
        return menuItem;
    }
    
}

    接下来用迭代器改写餐厅菜单:

【Code DinerMenu.java】

public class DinerMenu{
    static final int MAX_ITEMS = 6;
    int numberOfItems = 0;
    
    MenuItem[] menuItems;
    
    public DinerMenu(){
        menuItems = new MenuItem[MAX_ITEMS];
        
        addItem("Diner1","Diner1 is nice!",true,2.00);
        addItem("Diner2","Diner2 is nice!",true,2.00);

    }
    
    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("Menu is full");
        }else{
            menuItems[numberOfItems] = menuItem;
            numberOfItems = numberOfItems + 1;
        }
    }
    
    //
    public Iterator createIterator(){
        return new DinerMenuIterator(menuItems);
    }
    
    //other methods
    
}

【Code PancakeHouseMenu.java】

import java.util.*;

public class PancakeHouseMenu{
    ArrayList<MenuItem> menuItems;
    
    public PancakeHouseMenu(){
        menuItems = new ArrayList<MenuItem>();
        
        addItem("Pancake1","Pancake1 is nice!",true,2.00);
        addItem("Pancake2","Pancake2 is nice!",true,2.00);

    }
    
    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 PancakeHouseMenuIterator(menuItems);
    }
    
    //other methods
    
}

    修正招待的代码:

【Code Waiter.java】    

import java.util.*;

public class Waiter{
    
    void printMenu(){
        //-打印菜单的每一项
        PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
        Iterator iterator1 = pancakeHouseMenu.createIterator();
        while (iterator1.hasNext()){
            MenuItem menuItem = (MenuItem)iterator1.next();
            System.out.println(menuItem.getName() + " " + menuItem.getDescription() + " " + menuItem.getPrice());

        }
        
        DinerMenu dinerMenu = new DinerMenu();
        Iterator iterator2 = dinerMenu.createIterator();
        
        while (iterator2.hasNext()){
            MenuItem menuItem = (MenuItem)iterator2.next();
            System.out.println(menuItem.getName() + " " + menuItem.getDescription() + " " + menuItem.getPrice());

        }
        
    }
    void printBreakfastMenu(){
        //-只打印早餐项
    
        
    }
    void printLunchMenu(){
        //-只打印午餐项
        
    }
    
    void printVegetarianMenu(){
        //-打印所有的素食菜单项
        
    }
    
    boolean isItemVegetarian(String name){
        //-指定项的名称,素食返回true
        
        return true;
        
    }
    
    public static void main(String[] args){
        Waiter waiter = new Waiter();
        waiter.printMenu();
    }
    
}

现在客户只需要知道一件事:直接使用迭代器就可以遍历菜单项;

    我们给了各自menu两个迭代器,menu只需要加入一个createIterator()方法即可;

    遍历的结果与使用两个for循环的结果一致:

bogon:迭代器模式 huaqiang$ java Waiter
Pancake1 Pancake1 is nice! 2.0
Pancake2 Pancake2 is nice! 2.0
Diner1 Diner1 is nice! 2.0
Diner2 Diner2 is nice! 2.0

使用迭代器之后:

    招待不再需要知道如何存储菜单项集合,只要实现迭代器,通过一个循环,就可以多态地处理任何项 的集合;

    现在招待仍然捆绑了两个菜单类,当然我们还可以进一步的修改;

 


现在我们已经熟悉了如何从头创建一个迭代器,再来看看Java为我们提供了什么;

 

Java的Iterator接口:

    java.util.Iterator接口;

    remove方法允许我们从聚合中删除由next()方法返回的最后一项;

    如果不实现remove()方法,可以抛出一个java.lang.UnsupportedOperationException运行时异常;

java.util有迭代器接口;ArrayList也提供了返回一个迭代器的iterator()方法;换句话说,我们并不需要ArrayList实现自己的迭代器;

餐厅的迭代器还是需要我们自己实现的,因为餐厅使用的是数组,而数组不支持iterator()方法(或其他创建数组迭代器的方法);

 

值得注意的是:在多线程中使用迭代器时,必须特别小心;(没有指明后果)

 

使用Java提供的Iterator接口:

1)修改PancakeHouseMenu.java的代码:

    导入import java.utl.Iterator;

    再把createIterator()中的代码return menuItems.iterator();即可;

    这样就修改完成了;

 

2)修改DinerMenu.java的代码:

    实现的接口不再是我们实现,导入包,改用import java.utl.Iterator;

    现在还需要实现remove()方法,由于操作的是固定长度的数组,所以在remove()被调用的时候,我们将后面的所有元素往前移一位;

    Code DinerMenuIterator.java 中有该方法的实现;

 

最后,为了统一menu,我们将createIterator()进一步抽象成一个Menu接口;


 

定义迭代器模式:

    迭代器模式提供了一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示;

 

这样,有了一个统一的方法访问聚合中的每一个对象,你就可以编写多态的代码和这些聚合搭配;

同时,迭代器模式把在元素之间游走的责任交给了迭代器,而不是聚合对象;

    这可以让聚合的接口 实现更简洁,让聚合更专注在它的事情尚,而不必理会遍历;

 

类图:

 

内部迭代器 和 外部迭代器:

    我们实现的是外部的迭代器,也就是说,客户通过调用next()取得下一个元素;

    而内部的迭代器则是由迭代器自行在元素之间游走;

 


单一职责设计原则:

    一个类应该只有一个引起变化的原因;

    

类的每个责任都有改变的潜在区域;超过一个责任,意味着超过一个改变的区域;这个原则告诉我们尽量让每个类保持单一职责;

 

内聚(cohesion):

    用来度量一个类或模块紧密地达到单一目的或责任;

    当一个模块或一个类被设计成只支持一组相关的功能时,我们说他具有高内聚;

    反之,当被设计成支持一组不相关的功能时,我们说它具有低聚合;

 


Hashtable对于迭代器的支持是“间接的”:

    比如对于Hashtable实现其迭代器可以直接返回menuItems.values().iterator();该方法返回一个java.util.Iterator类型的对象;

 

迭代器和集合:

    我们所使用的这些类都属于Java Collection Framework的一部分;Framework(框架)指的是一群类和接口,其中包括ArrayList、Stack Overflow等;这些类都实现了java.util.collection接口;这个接口包含了许多有用的方法,可以操作一群对象;

    其中就包含iterator()方法,利用这个方法你可以取得任意类的迭代器,该迭代器实现了Iterator接口;

    其中还有其他方法,包括size()可以取得元素的个数;toArray()用来将集合转成数组;

 

Collection和Iterator的好处在于,每个Collection都知道如何创建自己的Iterator,只要调用ArrayList上的iterator(),就可以返回一个具体的Iterator,而你不需要知道或关心到底使用了哪个具体类,你只要使用它的Iterator接口就可以了;

 

Java的迭代器和集合:

    for/in语句:可以让你在一个集合或者一个数组中遍历,而且不需要显示地创建迭代器;

【Code Test_for_in.java】

import java.util.*;

public class Test_for_in{
    public static void main(String[] args){
        ArrayList<MenuItem> items = new ArrayList<MenuItem>();
        items.add(new MenuItem("p1","description",true,5.9));
        items.add(new MenuItem("p2","description",true,5.9));
        items.add(new MenuItem("p3","description",true,5.9));
        items.add(new MenuItem("p4","description",true,5.9));
        
        for (MenuItem item : items){
            System.out.println(item.name);
        }
    }
}

现在的问题:

    每一个菜单,都需要招待使用一个迭代器,迫使我们不断的修改代码;

    我们可以将诸多菜单放到一个ArrayList中,然后通过list.iterator()这个迭代器,遍历数组中的菜单,进而通过菜单的迭代器进行菜单项的遍历;这是里一个两层的结构;

 

我们还需要餐厅菜单可以提供一个子菜单:

    它可以是甜点菜单,而且需要插入到餐厅菜单中;现在糟糕的是,我们不得不继续修改代码;

    事实上,我们已经达到了一个复杂的级别,如果不重新设计,可能很难容纳未来的菜单或子查单需求;

    

我们来看看在新的设计中需要哪些:

1)我们需要某种树形结构,可以容纳菜单、子菜单和菜单项;

2)我们需要确定能够在每个菜单的各个项之间游走,而且至少要像现在用迭代器一样方便;

3)我们也需要能够更有弹性地在菜单项之间游走,可能只需要遍历甜点菜单,或者可以遍历餐厅的整个菜单(包括甜点子菜单);

 

定义组合模式:

    为了解决这个问题,我们介绍另一模式;迭代器模式仍然是我们解决问题的一部分;

    组合模式(Composite Pattern):

        允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合;

    其中没有子元素的称为叶节点,有子元素的称为节点;

 

使用一个遵照组合模式的设计,让我们能够写出简单的代码,就能对整个菜单结构应用相同的操作;

    把相同的操作应用在组合和个别对象上,多数情况下,可以忽略对象组合和个别对象之间的差别;

    比如print操作,适用于整个菜单,也适用于局部的子菜单或菜单项;

 

组合模式类图:

 

利用组合设计菜单:

    招待使用菜单组件接口MenuConponent访问菜单和菜单项;

    菜单项实现菜单组件接口MenuConponent,覆盖对自己有意义的方法;

    菜单也覆盖对自己有意义的方法;

 

实现菜单组件:

1)菜单组件抽象类:

【Code MenuComponent】

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)实现菜单项:

【Code MenuItem.java】

public class MenuItem extends MenuComponent{
    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 boolean isVegetarian(){
        return vegetarian;
    }
    public double getPrice(){
        return price;
    }
    
    public void print(){
        System.out.print("\n        " + getName());
    }
}

3)实现组合菜单:

4)修正print():

【Code Menu.java】

import java.util.*;

public class Menu extends MenuComponent{
    ArrayList<MenuComponent> menuComponents = new ArrayList<MenuComponent>();
    String name;
    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){
        menuComponent.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());
        
        Iterator iterator = menuComponents.iterator();
        while(iterator.hasNext()){
            MenuComponent menuComponent = (MenuComponent)iterator.next();
            menuComponent.print();
        }
    }
    
}

5)招待 调用组合:

【Code Waiter.java】

public class Waiter{
    
    MenuComponent allMenus;
    
    public Waiter(MenuComponent allMenus){
        this.allMenus = allMenus;
    }
    
    public void printMenu(){
        allMenus.print();
        System.out.print("\n        ");
    }
    
}

6)运行测试程序:

【Code MenuTestDrive.java】

import java.util.*;

public class MenuTestDrive{

    
    public static void main(String[] args){
        
        MenuComponent pancakeMenu = new Menu("pancakeMenu","breakfast");
        MenuComponent dinerMenu = new Menu("dinerMenu","lunch");
        
        MenuComponent dessertMenu = new Menu("dessertMenu","dessertMenu sub");
        
        MenuComponent allMenus = new Menu("allMenus","allMenus");
        
        allMenus.add(pancakeMenu);
        allMenus.add(dinerMenu);
        
        dinerMenu.add(new MenuItem("diner1","diner1",true,4.00));
        dinerMenu.add(new MenuItem("diner2","diner2",false,4.00));
        
        dessertMenu.add(new MenuItem("dessert1","dessert1",true,4.00));
        dessertMenu.add(new MenuItem("dessert2","dessert2",false,4.00));
        
        dinerMenu.add(dessertMenu);
        
        pancakeMenu.add(new MenuItem("pancakeMenu1","pancakeMenu1",true,4.00));
        pancakeMenu.add(new MenuItem("pancakeMenu2","pancakeMenu2",false,4.00));

        
        Waiter waiter = new Waiter(allMenus);
        waiter.printMenu();
    }
    
}

7)执行结果:

 

透明性和安全性:

    我们在MenuConponent类中同时定义了两种类型的操作;

    一个元素是组合还是叶节点对客户来说是透明的;

    但是也有可能对同一个元素做了不恰当/没有意义的操作,失去了一些安全性;

    满足安全性,又会失去透明性;

 

这是一个很典型的折中案例;

 


 

组合模式的应用:帮助招待找到菜单中的素食项

1)为每个组件加上createIterator()方法,修改MenuConponent增加方法:

    这样,每个菜单和菜单项都必须实现这个方法;

    对一个组合调用createIterator()方法,将会应用于该组合的所有孩子;(为区分栈层次,使维护的栈只有1层 ,这里新增两个参数,分别是当前组合名以及组合树的根节点名;二者只在生成根节点的迭代器时候才相等,生成其他节点使用具体的节点名,二者不相等)

import java.util.*;

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();   
    }
    
    public Iterator createIterator(String subName, String totalName){
        throw new UnsupportedOperationException(); 
    }
}

2)新建一个迭代器:CompositeIterator

    他知道如何遍历任何组合;

    将目前组合的迭代器传入它的构造器;

 

这个CompositeIterator可以遍历组件内的菜单项以及所有的子菜单;

    我们实现的是外部迭代器,他必须维护当前遍历的位置;在这个例子中,必须维护组合递归结构的位置;这里使用堆栈来维护我们的位置;

【Code  CompositeIterator.java】

import java.util.*;

public class CompositeIterator implements Iterator{
    
    Stack<Iterator> stack = new Stack<Iterator>();
    String subName;
    String totalName;

    public CompositeIterator(Iterator iterator, String subName, String totalName){
        this.subName = subName;
        this.totalName = totalName;
        stack.push(iterator);
    }
    public Object next(){
        if (hasNext()){
            Iterator iterator = (Iterator)stack.peek();
            MenuComponent component = (MenuComponent)iterator.next();
            if (component instanceof Menu){
                stack.push(component.createIterator(component.getName(), totalName));
            }
            return component;
        }else{
            return null;
        }
    }
    
    public boolean hasNext(){
        if (stack.empty()){
            return false;
        }else{
            //取出 栈顶元素 
            Iterator iterator = (Iterator)stack.peek();
            if (!iterator.hasNext()){
                stack.pop();
                return hasNext();
            }else{
                return true;
            }
        }
    }
    
    public void remove(){
        throw new UnsupportedOperationException();
    }
    
}

【Code Menu.java】

import java.util.*;

public class Menu extends MenuComponent{
    ArrayList<MenuComponent> menuComponents = new ArrayList<MenuComponent>();
    String name;
    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){
        menuComponent.remove(menuComponent);
    }
    public MenuComponent getChild(int i){
        return (MenuComponent)menuComponents.get(i);
    }
    
    public String getName(){
        return name;
    }
    public String getDescription(){
        return description;
    }
    public Iterator createIterator(String subName, String totalName){
        if (subName == totalName) {
            //我们只需要使用栈结构在最外层的菜单中维护遍历的迭代器
            return new CompositeIterator(menuComponents.iterator(), subName, totalName);
        }else{
            return menuComponents.iterator();
        }
    }
    
    public void print(){
        System.out.print("\n" + getName());
        
        Iterator iterator = menuComponents.iterator();
        while(iterator.hasNext()){
            MenuComponent menuComponent = (MenuComponent)iterator.next();
            menuComponent.print();
        }
    }
}

【Code MenuItem.java】

import java.util.*;

public class MenuItem extends MenuComponent{
    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 boolean isVegetarian(){
        return vegetarian;
    }
    public double getPrice(){
        return price;
    }
    
    public Iterator createIterator(String subName, String totalName){
        return new NullIterator();
    }
    
    public void print(){
        System.out.print("\n        " + getName());
    }
}

空迭代器:

    一个没作用的迭代器,NullIterator:这个迭代器的hasNext()永远返回false;

    它的next()方法返回null,remove方法由于不被支持,抛出UnsupportedOperationException()即可;

【Code NullIterator.java】

import java.util.*;

public class NullIterator implements Iterator{

    public Object next(){
        return null;
    }
    
    public boolean hasNext(){
        return false;
    }
    
    public void remove(){
        throw new UnsupportedOperationException();
    }
    
}

打印素食菜单:

【Code Waiter.java】

import java.util.*;


public class Waiter{
    
    MenuComponent allMenus;
    
    public Waiter(MenuComponent allMenus){
        this.allMenus = allMenus;
    }
    
    public void printMenu(){
        allMenus.print();
        System.out.print("\n        ");
    }
    
    //遍历 符合条件的菜单项 
    public void printVegetarianMenu(){
        Iterator iterator = allMenus.createIterator("allMenu","allMenu");

        while (iterator.hasNext()){
            MenuComponent menuComponent = (MenuComponent)iterator.next();
            try{
                if (menuComponent.isVegetarian()){
                    menuComponent.print();
                }
            }catch (UnsupportedOperationException e){
                //只有 菜单项 才可以打印
                // 捕获异常  继续打印

            }
        }
        
        System.out.print("\n        ");
    }
    
    
}

【Code MenuTestDrive.java】

import java.util.*;

public class MenuTestDrive{

    
    public static void main(String[] args){
        
        MenuComponent pancakeMenu = new Menu("pancakeMenu","breakfast");
        MenuComponent dinerMenu = new Menu("dinerMenu","lunch");
        
        MenuComponent dessertMenu = new Menu("dessertMenu","dessertMenu sub");
        
        MenuComponent allMenus = new Menu("allMenus","allMenus");
        
        
        dessertMenu.add(new MenuItem("  dessert1","dessert1",true,4.00));
        dessertMenu.add(new MenuItem("  dessert2","dessert2",false,4.00));

        dinerMenu.add(new MenuItem(" diner1","diner1",true,4.00));
        dinerMenu.add(new MenuItem(" diner2","diner2",false,4.00));


        dinerMenu.add(dessertMenu);
        
        pancakeMenu.add(new MenuItem(" pancakeMenu1","pancakeMenu1",true,4.00));
        pancakeMenu.add(new MenuItem(" pancakeMenu2","pancakeMenu2",false,4.00));


        allMenus.add(dinerMenu);
        allMenus.add(pancakeMenu);

        Waiter waiter = new Waiter(allMenus);
         waiter.printMenu();
        System.out.print("\n ——————————————————————————————————————");
        waiter.printVegetarianMenu();
    }
    
}

当有整个对象的集合,彼此之间有整体/部分的关系,并且想用一致的方式对待这些对象时,就需要组合模式;

一些已学习的模式匹配:

    策略:封装可互换的行为,并使用委托决定使用哪一个;

    适配器:改变一个或多各类的接口;

    迭代器:提供一个方式来遍历集合;

    外观:简化一群类的接口;

    组合:客户可以将对象的组合以及个别的对象一视同仁;

    观察者:当某个状态改变时,允许一群对象能被通知到;

 

总结:

1.迭代器允许访问聚合的元素,而不需要暴露它的内部结构;

2.迭代器将遍历聚合的工作封装进一个对象中;

3.当使用迭代器的时候,我们依赖聚合提供遍历;

4.迭代器提供了一个通用的接口,可以使用多态机制;

5.努力让一个类只分配一个责任;

6.组合模式提供一个结构,可同时包含个别对象和组合对象;

7.组合模式允许客户对个别对象以及组合对象一视同仁;

8.组合结构内的任意对象称为组件,组件可以是组合也可以是叶节点;

9.在实现组合模式时,有许多设计上的折中,要根据需要平衡透明性和安全性;

 

OO基础:

    抽象;

    封装

    继承;

    多态;

OO原则:

    封装变化

    多用组合,少用继承

    针对接口编程,不针对实现编程

    为交互对象之间的松耦合设计而努力;

    类应该对扩展开放,对修改关闭;

    依赖抽象,不要依赖具体类;

    只和朋友交谈(最少知识原则);

    别找我,我会找你(好莱坞原则:由超类主控一切,需要的时候自然会去调用子类);

    ——类应该只有一个改变的理由(单一职责原则);

OO模式:

    策略模式:定义算法族,分别封装起来,让他们之间互相替换,此模式让算法的变化独立于使用算法的客户;

    观察者模式:在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新;

    装饰者模式:动态地将责任附加到对象上;想要扩展功能,装饰者提供有别于继承的另一种选择;

    简单工厂模式;

    工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个;工厂方法让类把实例化推迟到子类;

    抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确具体的类;

    单件模式:确保一个类只有一个实例,并提供全局访问点;

    命令模式:将请求封装成对象,这可以让你使用不同的请求,队列或者日志请求来参数化其他对象;命令模式也支持撤销操作;

    适配器模式:将一个类的接口转换成客户期待的另一个接口,适配器让原来不兼容的类可以合作无间;

    外观模式:提供了一个统一的接口,用来访问子系统中的一群接口;外观模式定义了高层接口,让子系统更容易使用;

    模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中;模板方法使得子类可以在不改变算法结构的情况下,重新定义/捕获算法中的某些步骤;

    ——迭代器模式:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示;

    ——组合模式:允许你将对象组成树形结构来表现整体/部分的层次结构;组合能让客户以一致的方式处理个别对象和对象组合;

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值