wordpress 安装路径,哈尔滨排名seo公司,中英文切换网站,wordpress 瀑布流布局迭代器模式
因为这一章涉及到两个模式#xff0c;内容有点多#xff0c;还有一个组合模式留到下一篇写吧。
有许多种方法可以把对象堆起来成为一个集合#xff08;collection#xff09;。你可以把它们放进数组、堆栈、列表或者是散列表#xff08;Hashtable#xff09…迭代器模式
因为这一章涉及到两个模式内容有点多还有一个组合模式留到下一篇写吧。
有许多种方法可以把对象堆起来成为一个集合collection。你可以把它们放进数组、堆栈、列表或者是散列表Hashtable中这是你的自由。每一种都有它自己的优点和适合的使用时机但总有一个时候你的客户想要遍历这些对象而当他这么做时你打算让客户看到你的实现吗我们当然希望最好不要这太不专业了。本章的迭代器模式将能让客户遍历你的对象而又无法窥视你存储对象的方式。
先来看看迭代器模式的定义
提供一种方法顺序访问一个聚合对象中的各个元素而又不暴露其内部的表示。
题例有两家餐厅披萨店和煎饼店它们合并了虽然可以在一个地方同时想用煎饼屋的早餐和餐厅的午餐但是煎饼屋的菜单用用的ArrayList记录菜单的而餐厅用的是数组而两家餐馆都不愿意修改自己的实现。毕竟有很多代码依赖它们。
幸好两家都统一实现了MenuItem 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 //菜单项保存了菜单信息 public class MenuItem { private String name; private String description; private boolean vegetarian; private double price; public MenuItem(String name, String description, boolean vegetarian, double price) { super(); this.name name; this.description description; this.vegetarian vegetarian; this.price price; } public String getName() { return name; } public void setName(String name) { this.name name; } public String getDescription() { return description; } public void setDescription(String description) { this.description description; } public boolean isVegetarian() { return vegetarian; } public void setVegetarian(boolean vegetarian) { this.vegetarian vegetarian; } public double getPrice() { return price; } public void setPrice(double price) { this.price price; } }
再来看看两家店各自的菜单实现
煎饼店用ArrayList 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // 煎饼餐店对象用ArrayList保存了菜单。 public class PancakeHouseMenu { ArrayListMenuItem menuItems; public PancakeHouseMenu() { menuItems new ArrayListMenuItem(); addItem(煎饼1号, 牛肉煎饼, false, 2.99); addItem(煎饼2号, 素食煎饼, true, 1.49); } public void addItem(String name, String description, boolean vegetarian, double price) { MenuItem menu new MenuItem(name, description, vegetarian, price); menuItems.add(menu); } public ArrayListMenuItem getMenuItems() { return menuItems; } }
披萨店用数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // 披萨餐厅对象用数组保存了菜单信息。 public class PizzaHouseMenu { static final int MAX_ITEMS 2; int numberOfItems 0; MenuItem[] menuItems; public PizzaHouseMenu() { menuItems new MenuItem[MAX_ITEMS]; } public void addItem(String name, String description, boolean vegetarian, double price) { MenuItem menu new MenuItem(name, description, vegetarian, price); if (numberOfItems MAX_ITEMS) System.out.println(对不起菜单数量已满); else menuItems[numberOfItems] menu; } public MenuItem[] getMenuItems() { return menuItems; } }
有两种不同的菜单表现方式这会带来什么问题
假设你是一个女招待下面是你做的事你会怎么办
printMenu(); 打印出菜单上的每一项printBreakfastMenu(); 只打印早餐printLunchMenu(); 只打印午餐printVegetarianMenu(); 打印所有的素食菜单isItemVegetarian(name); 查询指定的菜品是否是素食
指定项的名称如果该项是素食的话返回true否则返回false 打印没分菜单上的所有项必须调用PancakeHouseMenu和PizzaHouseMenu的getMenuItenm方法来取得它们各自的菜单项两者返回类型是不一样的。 1 2 3 4 5 PancakeHouseMenu pancakeHouseMenunew PancakeHouseMenu(); ArrayList breakfastItemspancakeHouseMenu.getMenuItems(); PizzaHouseMenu pizzaHouseMenunew PizzaHouseMenu(); MenuIten[] linchItenmspizzaHouseMenu.getMenuItens();
打印菜单需要的数组和集合用循环将数据一一列出来 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Client { public static void main(String[] args) { // 先获得煎饼餐厅的菜单集合 PancakeHouseMenu pancakeHouseMenu new PancakeHouseMenu(); ArrayListMenuItem menusOfPancake pancakeHouseMenu.getMenuItems(); // 在获得披萨餐厅的菜单数组 PizzaHouseMenu pizzaHouseMenu new PizzaHouseMenu(); MenuItem[] menusOfPizza pizzaHouseMenu.getMenuItems(); //我们用循环将数据一一列出来 for (int i 0; i menusOfPancake.size(); i) { MenuItem menu menusOfPancake.get(i); System.out.print(menu.getName() ,价格:); System.out.print(menu.getPrice() ,); System.out.print(menu.getDescription() \n); } System.out.println(); for (int i 0; i menusOfPizza.length; i) { MenuItem menu menusOfPizza[i]; System.out.print(menu.getName() ,价格:); System.out.print(menu.getPrice() ,); System.out.print(menu.getDescription() \n); } } }
我们总是需要处理这两个菜单的遍历如果还有第三家餐厅以不同的方式实现菜单集合我们就需要有第三个循环。
可以封装遍历吗
可以封装变化的部分。很明显这里发生的变化是由不同的集合类型所造成的遍历。但是这能够被封装吗让我们来看看这个想法…… 要便利煎饼餐厅我们需要使用ArrayList的size()和get()方法 要便利披萨餐厅我们需要使用数组的length字段和在中括号中输入索引 现在我们创建一个对象将它称为迭代器(Iterator)利用它来封装“遍历集合内的每个对象的过程” 想要在餐厅菜单中加入一个迭代器我们需要先定义迭代器接口然后为披萨餐厅创建一个迭代器类 1 2 3 4 public interface Iterator { boolean hasNext(); Object next(); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public class PizzaIterator implements Iterator { MenuItem[] items; int position 0; public PizzaIterator(MenuItem[] items) { this.items items; } // 判断数组下一个索引是否还有元素 public boolean hasNext() { if(position items.length || items[position] null) return false; else return true; } // 获得当前索引位置的元素 public Object next() { MenuItem item items[position]; return item; } } public class PancakeIterator implements Iterator { ArrayListMenuItem items; int position 0; public PancakeIterator(ArrayListMenuItem items) { this.items items; } // 判断数组下一个索引是否还有元素 public boolean hasNext() { if(position items.size() || items.get(position) null) return false; else return true; } // 获得当前索引位置的元素 public Object next() { MenuItem item items.get(position); return item; } }
创建好迭代器后改写披萨餐厅的代码创建一个PizzaMenuIterator并返回给客户 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class PizzaHouseMenu { static final int MAX_ITEMS 2; int numberOfItems 0; MenuItem[] menuItems; public PizzaHouseMenu() { menuItems new MenuItem[MAX_ITEMS]; addItem(披萨1号, 素食披萨, true, 4.99); addItem(披萨2号, 海鲜蛤蜊披萨, true, 5.99); } public void addItem(String name, String description, boolean vegetarian, double price) { MenuItem menu new MenuItem(name, description, vegetarian, price); if (numberOfItems MAX_ITEMS) System.out.println(对不起菜单数量已满); else menuItems[numberOfItems] menu; } public Iterator createIterator() { return new PizzaIterator(menuItems); } } public class PancakeHouseMenu { ArrayListMenuItem menuItems; public PancakeHouseMenu() { menuItems new ArrayListMenuItem(); addItem(煎饼1号, 牛肉煎饼, false, 2.99); addItem(煎饼2号, 素食煎饼, true, 1.49); } public void addItem(String name, String description, boolean vegetarian, double price) { MenuItem menu new MenuItem(name, description, vegetarian, price); menuItems.add(menu); } public Iterator createIterator() { return new PancakeIterator(menuItems); } }
我们不再需要getMenuItems()方法而是用createIterator()方法代替用来从菜单项数组创建一个迭代器并把他返回给客户返回迭代器接口。客户不需要知道餐厅菜单使如何实现维护的也不需要知道迭代器是如何实现的。客户只需直接使用这个迭代器遍历菜单即可。下面修改一下客户类的调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Waitress { PancakeHouseMenu pancake; PizzaHouseMenu pizza; public Waitress(PancakeHouseMenu pancake, PizzaHouseMenu pizza) { this.pancake pancake; this.pizza pizza; } public void printMenu() { Iterator pizzaIterator pizza.createIterator(); printMenu(pizzaIterator); Iterator pancakeIterator pancake.createIterator(); printMenu(pancakeIterator); } private void printMenu(Iterator iterator) { while(iterator.hasNext()) { MenuItem menu (MenuItem)iterator.next(); System.out.print(menu.getName() ,价格:); System.out.print(menu.getPrice() ,); System.out.print(menu.getDescription() \n); } } } 1 2 3 4 5 6 7 8 9 public class Client { public static void main(String[] args) { PancakeHouseMenu pancake new PancakeHouseMenu(); PizzaHouseMenu pizza new PizzaHouseMenu(); Waitress waitress new Waitress(pancake, pizza); waitress.printMenu(); } }
输出结果 到目前为止我们将客户调用与餐厅的菜单数据接口解耦了客户调用再也不用为每一个不同数据结构的菜单编写一套遍历的代码了。
到目前为止我们做了些什么 我们现在使用一个共同的迭代器接口(Iteraotr)实现了两个具体类(PizzaIterator和PancakeIterator)。这两个具体类都实现了各自的hasNext()方法和next()方法。
然后再PancakeHouseMenu和PizzaHouseMenu两个类中创建一个createIterator()方法返回各自的迭代器在Waitress类中使用这两个餐厅对象返回的迭代器打印菜单。这时Waitress类和Client类再也不需要关心存放菜单的数据结构之关心能从迭代器中获得菜单就好。
迭代器模式给你提供了一种方法可以顺序访问一个聚集对象的元素而又不用知道内部是如何表示的。你已经在前面的两个菜单实现中看到了这一点。在设计中使用迭代器的影响是明显的如果你有一个统一的方法访问聚合中的每一个对象你就可以编写多态的代码和这些聚合搭配使用如同前面的printMenu()方法一样只要有了迭代器这个方法根本不用管菜单究竟是由数组还是集合或者其他的数据结构来保存的。
另外一个对你的设计造成重要影响的是迭代器模式把在元素之间游走的责任交给迭代器而不是聚合对象。这不仅让聚合的接口和实现变得更简洁也可以让聚合更专注它所应该专注的事情上面而不必去理会遍历的事情。
让我们检查类图将来龙去脉拼凑出来…… 先看看Aggregate接口有一个共同的接口提供所有的聚合使用这对客户代码是很方便的将客户代码从集合对象的实现解耦。
接下来看看ConcreteAggregate类这个具体聚合持有一个对象的集合并实现一个方法利用此方法返回集合的迭代器。每一个具体聚合都要负责实例化一个具体的迭代器次迭代器能够便利对象集合。
接下来是Iterator接口这是所有迭代器都必须实现的接口它包含一些方法利用这些方法可以在集合元素之间游走。你可以自己设计或者使用java.util.Iterator接口。
最后是具体的迭代器负责遍历集合。
单一责任
如果我们允许我们的聚合实现他们内部的集合以及相关的操作和遍历的方法又会如何我们已经知道这回增加聚合中的方法个数但又怎么样呢为什么这么做不好
想知道为什么首选需要认清楚当我们允许一个类不但要完成自己的事情还同时要负担更多的责任时我们就给这个类两个变化的原因。如果这个集合变化的话这个类也必须要改变如果我们遍历的方式改变的话这个类也必须跟着改变。所以引出了设计原则的中心
单一责任一个类只有一个引起变化的原因。
类的每个责任都有改变的潜在区域。超过一个责任意味着超过一个改变区域。这个原则告诉我们尽量让每一个类保持单一责任。
内聚(cohesion)这个术语你应该听过它用来度量一个类或者模块紧密地达到单一目的或责任。
当一个模块或一个类被设计成只支持一组相关的功能时我们说它具有高内聚反之当被设计成支持一组不相关的功能时我们说它具有低内聚。
内聚是一个比单一职责更普遍的概念但两者其实关系是很密切的。遵守这个原则的类更容易有很高的凝聚力而且比背负许多职责的低内聚类更容易维护。
以上就是迭代器模式的一些内容。