设计模式--迭代器模式
Posted dxj1016
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式--迭代器模式相关的知识,希望对你有一定的参考价值。
设计模式分为三种类型,共23种
- 创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
- 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
- 行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式)。
创建型模式主要用于创建对象
结构型模式主要用于处理类或对象的组合
行为型模式主要用于描述类或对象如何交互和怎样分配职责
1、基本介绍
- 迭代器模式(Iterator Pattern)是常用的设计模式,属于行为型模式
- 如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,
或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历
方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。 - 迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,
不需要知道集合对象的底层表示,即:不暴露其内部的结构。
2、迭代器模式的注意事项和细节
优点
- 提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以
遍历对象了。 - 隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚
合的具体组成。 - 提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任
原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集
合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变
的话,只影响到了迭代器。 - 当要展示一组相似对象,或者遍历一组相同对象时使用, 适合使用迭代器模式
缺点
- 每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类
3、例子
餐厅和煎饼屋合并,现在想要煎饼屋菜单作为早餐的菜单,餐厅的菜单做午餐的菜单。但是两者有一个使用了ArrayList记录菜单,有一个使用了数组记录菜单,
菜单项
package designpattern.iterator.improve;
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;
}
}
采用数组记录菜单的类
package designpattern.iterator.improve;
import java.util.Iterator;
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 sauerkraut, 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 MenuItem[] getMenuItems() {
return menuItems;
}
// other menu methods here(如果这里有新的方法,也是依赖于数组)
}
采用ArrayList记录菜单的类
package designpattern.iterator.improve;
import java.util.ArrayList;
import java.util.Iterator;
public class PancakeHouseMenu {
ArrayList<MenuItem> menuItems;
public PancakeHouseMenu() {
// 这里使用ArrayList存储菜单项
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 and blueberry syrup",
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)
{
// 要加入一个菜单项,创建一个新的菜单项对象,传入每一个变量,然后将他加入ArrayList中
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
menuItems.add(menuItem);
}
public ArrayList<MenuItem> getMenuItems() {
return menuItems;
}
// other menu methods here(这里如果有新的方法,那么也是依赖于ArrayList)
}
要想有个方法打印菜单项,那么上面有两个菜单使用不同的方式记录菜单,所有需要不同的方式遍历菜单,因此需要两个循环,如果还有第三家餐厅采用不同的实现出现,那么就得多加一个循环
这里的问题是不同对接会类型所造成的遍历需要多次,解决:创建一个对象,称为迭代器,利用他来封装‘’遍历集合内的每个对象的过程”
迭代器接口
package designpattern.iterator.improve;
public interface Iterator {
boolean hasNext();
MenuItem next();
}
具体的迭代器–为餐厅菜单服务
package designpattern.iterator.improve;
public class DinerMenuIterator implements Iterator {
MenuItem[] items;
int position = 0;//记录当前数据遍历的位置
// 构造器需要 传入一个菜单项的数组当做参数
public DinerMenuIterator(MenuItem[] items) {
this.items = items;
}
// next方法返回数组内的下一项,并递增其位置
public MenuItem next() {
/*
MenuItem menuItem = items[position];
position = position + 1;
return menuItem;
*/
// or shorten to
return items[position++];
}
public boolean hasNext() {
/*
if (position >= items.length || items[position] == null) {
return false;
} else {
return true;
}
*/
// or shorten to
return items.length > position;
}
}
具体的迭代器–煎饼屋菜单服务
package designpattern.iterator.improve;
import java.util.ArrayList;
import java.util.List;
public class PancakeHouseMenuIterator implements Iterator {
List<MenuItem> items;
int position = 0;//记录list的位置
// 构造器需要 传入一个菜单项的list当做参数
public PancakeHouseMenuIterator(List<MenuItem> items) {
this.items = items;
}
public MenuItem next() {
/*
MenuItem item = items.get(position);
position = position + 1;
return item;
*/
// or shorten to:
return items.get(position++);
}
public boolean hasNext() {
/*
if (position >= items.size()) {
return false;
} else {
return true;
}
*/
// or shorten to:
return items.size() > position;
}
}
修改上面的两个餐厅菜单项
package designpattern.iterator.improve;
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 sauerkraut, 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 MenuItem[] getMenuItems() {
// return menuItems;
// }
public Iterator createIterator() {
return new DinerMenuIterator(menuItems);
}
// other menu methods here(如果这里有新的方法,也是依赖于数组)
}
package designpattern.iterator.improve;
import java.util.ArrayList;
public class PancakeHouseMenu {
ArrayList<MenuItem> menuItems;
public PancakeHouseMenu() {
// 这里使用ArrayList存储菜单项
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 and blueberry syrup",
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)
{
// 要加入一个菜单项,创建一个新的菜单项对象,传入每一个变量,然后将他加入ArrayList中
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
menuItems.add(menuItem);
}
// public ArrayList<MenuItem> getMenuItems() {
// return menuItems;
// }
public Iterator createIterator() {
return new PancakeHouseMenuIterator(menuItems);
}
// other menu methods here(这里如果有新的方法,那么也是依赖于ArrayList)
}
整合迭代器代码的类
package designpattern.iterator.improve;
public class Waitress {
PancakeHouseMenu pancakeHouseMenu;
DinerMenu dinerMenu;
public Waitress(PancakeHouseMenu pancakeHouseMenu, DinerMenu dinerMenu) {
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinerMenu = dinerMenu;
}
public void printMenu() {
Iterator pancakeIterator = pancakeHouseMenu.createIterator();
Iterator dinerIterator = 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 = iterator.next();
System.out.print(menuItem.getName() + ", ");
System.out.print(menuItem.getPrice() + " -- ");
System.out.println(menuItem.getDescription());
}
}
// 其它的方法
}
/*
MENU
----
BREAKFAST
K&B's Pancake Breakfast, 2.99 -- Pancakes with scrambled eggs and toast
Regular Pancake Breakfast, 2.99 -- Pancakes with fried eggs, sausage
Blueberry Pancakes, 3.49 -- Pancakes made with fresh blueberries and blueberry syrup
Waffles, 3.59 -- Waffles with your choice of blueberries or strawberries
LUNCH
Vegetarian BLT, 2.99 -- (Fakin') Bacon with lettuce & tomato on whole wheat
BLT, 2.99 -- Bacon with lettuce & tomato on whole wheat
Soup of the day, 3.29 -- Soup of the day, with a side of potato salad
Hotdog, 3.05 -- A hot dog, with sauerkraut, relish, onions, topped with cheese
Steamed Veggies and Brown Rice, 3.99 -- Steamed vegetables over brown rice
Pasta, 3.89 -- Spaghetti with Marinara Sauce, and a slice of sourdough bread
*/
测试类
package designpattern.iterator.improve;
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();
}
}
好处:
- 菜单的实现已经被封装起来了,Waitress类不知道菜单是如何存储器菜单项集合的;
- 只要实现了迭代器,我们只需要一个循环,就可以多态地处理任何项的集合
缺点:
- Waitress类中仍然捆绑了两个具体的菜单类
目前的设置:
- 使用一个共同的迭代器接口,实现了两个具体的类。
- 两个菜单都实现了一样的方法createIterator,但是没有实现相同接口,修改成实现相同的接口后,waitress就不会依赖于具体的菜单了。
- 迭代器接口让waitess能够从具体类的实现中解耦。他不需要知道菜单是使用数组还是ArrayList,他只需要关心他能否拿到迭代器。
- 还有我们目前是自定义一个迭代器接口,也可以直接使用java中的Iterator接口。
添加一个menu接口,两个菜单项都实现他
package designpattern.iterator.improve;
public interface Menu {
public Iterator createIterator();
}
修改Waitress代码
如果要添加新的餐厅过来的时候,可添加具体的菜单迭代器 实现迭代器接口,添加新的菜单项类,然后在Waitress中加入
以上是关于设计模式--迭代器模式的主要内容,如果未能解决你的问题,请参考以下文章