设计模式--迭代器模式

Posted dxj1016

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式--迭代器模式相关的知识,希望对你有一定的参考价值。

设计模式分为三种类型,共23种

  1. 创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
  2. 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
  3. 行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式)。

创建型模式主要用于创建对象
结构型模式主要用于处理类或对象的组合
行为型模式主要用于描述类或对象如何交互和怎样分配职责

1、基本介绍

  1. 迭代器模式(Iterator Pattern)是常用的设计模式,属于行为型模式
  2. 如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,
    或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历
    方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
  3. 迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,
    不需要知道集合对象的底层表示,即:不暴露其内部的结构。

2、迭代器模式的注意事项和细节

优点

  1. 提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以
    遍历对象了。
  2. 隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚
    合的具体组成。
  3. 提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任
    原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集
    合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变
    的话,只影响到了迭代器。
  4. 当要展示一组相似对象,或者遍历一组相同对象时使用, 适合使用迭代器模式

缺点

  1. 每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类

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中加入

以上是关于设计模式--迭代器模式的主要内容,如果未能解决你的问题,请参考以下文章

Java源代码-迭代器模式

Python 设计模式 — 行为型模式 — 迭代器模式

设计模式-迭代器模式

Python 设计模式 — 行为型模式 — 迭代器模式

设计模式学习

设计模式之迭代器模式