设计模式之迭代器模式
- 迭代器模式总结
- 两个餐馆合并案例
- 传统设计
- 迭代器模式设计
- Java内置迭代器设计
- 单一责任原则
迭代器模式总结
- 迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
- 提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。把在元素之间游走的责任交给迭代器,而不是聚合对象。
- 优点: 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
- 迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
两个餐馆合并案例
案例:
现在有两个餐馆,一个蛋糕店,一个中餐厅,都有各自的菜单,现在要合并,也就是说要通过一个女服务员直接点单:
传统设计
首先两个餐厅:
package iterator.bad;
import java.util.ArrayList;
/**
* 蛋糕店
*/
public class CakeHouse {
private ArrayList<MenuItem> menuItems;
public CakeHouse() {
menuItems = new ArrayList<>();
addItem("Cakefood1",33.3);
addItem("Cakefood2",44.4);
}
private void addItem(String name,double price){
MenuItem menuItem = new MenuItem(name,price);
menuItems.add(menuItem);
}
//获取菜单项 //暴露在外面 //耦合度高, 封装性不好
public ArrayList<MenuItem> getMenuItems() {
return menuItems;
}
}
package iterator.bad;
/**
* 中餐厅
*/
public class DinerHouse {
private final static int Max_Item = 5;//最多只有五个菜单项
private int cur; //游标
public int getCur() {
return cur;
}
private MenuItem[] menuItems;
public DinerHouse() {
menuItems = new MenuItem[Max_Item];
addItem("Dinerfood1",11.1);
addItem("Dinerfood2",22.2);
}
public void addItem(String name,double price){
MenuItem menuItem = new MenuItem(name,price);
if(cur >= Max_Item){
System.out.println("sorry! menu is full! can't add any item!");
}else {
menuItems[cur++] = menuItem;
}
}
//暴露出来
public MenuItem[] getMenuItems() {
return menuItems;
}
}
菜单项:
package iterator.bad;
/**
* 每个餐厅的菜单项
*/
public class MenuItem {
private String name;
private double price;
public MenuItem(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
女招待:
package iterator.bad;
import java.util.ArrayList;
/**
* 女招待员
* 必须同时有两个对象的引用
*/
public class Waitress {
//蛋糕店的
private CakeHouse cakeHouse;
private ArrayList<MenuItem> cakeItems;
//中餐厅的
private DinerHouse dinerHouse;
private MenuItem[] dinerItems;
public Waitress() {
cakeHouse = new CakeHouse();
cakeItems = cakeHouse.getMenuItems();
dinerHouse = new DinerHouse();
dinerItems = dinerHouse.getMenuItems();
}
public void printMenu(){
MenuItem item = null;
for(int i = 0; i < cakeItems.size(); i++){
item = cakeItems.get(i);
System.out.println(item.getName() + " " + item.getPrice());
}
for(int i = 0; i < dinerHouse.getCur(); i++){
item = dinerItems[i];
System.out.println(item.getName() + " " + item.getPrice());
}
}
}
测试:
package iterator.bad;
public class MyTest {
public static void main(String[] args) {
Waitress waitress = new Waitress();
waitress.printMenu();
}
}
输出:
Cakefood1 33.3
Cakefood2 44.4
Dinerfood1 11.1
Dinerfood2 22.2
这中方案存在的问题:
- 如果再来一个餐厅要合并,比如说菜单是HashTable的,这样那边也要暴露自己的菜单,而且女招待要修改代码;
- 开发要针对接口的开发,而不是具体实现的开发,而且这样设计耦合性增加;
迭代器模式设计
先看迭代器:
package iterator.good;
/**
* 迭代器的接口
*/
public interface Iterator {
public boolean hasNext();
public Object next();
}
两个餐厅:
package iterator.good;
import java.util.ArrayList;
/**
* 蛋糕店
*/
public class CakeHouse {
private ArrayList<MenuItem> menuItems;
public CakeHouse() {
menuItems = new ArrayList<>();
addItem("Cakefood1",33.3);
addItem("Cakefood2",44.4);
}
private void addItem(String name,double price){
MenuItem menuItem = new MenuItem(name,price);
menuItems.add(menuItem);
}
//实现迭代器的设计 不能设计为静态的
private class CakeHouseIterator implements Iterator{
private int pos;
public CakeHouseIterator() {
pos = 0;
}
@Override
public boolean hasNext() {
if(pos < menuItems.size()){
return true;
}
return false;
}
@Override
public Object next() {
MenuItem menuItem = menuItems.get(pos++);
return menuItem;
}
}
public Iterator getIterator(){
return new CakeHouseIterator();
}
}
package iterator.good;
/**
* 中餐厅
*/
public class DinerHouse {
private final static int Max_Item = 5;//最多只有五个菜单项
private int cur; //游标
private MenuItem[] menuItems;
public DinerHouse() {
menuItems = new MenuItem[Max_Item];
addItem("Dinerfood1",11.1);
addItem("Dinerfood2",22.2);
}
public void addItem(String name,double price){
MenuItem menuItem = new MenuItem(name,price);
if(cur >= Max_Item){
System.out.println("sorry! menu is full! can't add any item!");
}else {
menuItems[cur++] = menuItem;
}
}
private class DinerHouseIterator implements Iterator{
private int pos;
public DinerHouseIterator() {
pos = 0;
}
@Override
public boolean hasNext() {
if(pos < cur){
return true;
}
return false;
}
@Override
public Object next() {
MenuItem menuItem = menuItems[pos++];
return menuItem;
}
}
public Iterator getIterator(){
return new DinerHouseIterator();
}
}
菜单项(不变的):
package iterator.good;
/**
* 每个餐厅的菜单项
*/
public class MenuItem {
private String name;
private double price;
public MenuItem(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
女招待:
package iterator.good;
import iterator.good.MenuItem;
import java.util.ArrayList;
/**
* 女招待 直接遍历Iterator
*/
public class Waitress {
private ArrayList<Iterator> iterators = new ArrayList<>();
public Waitress() {
}
//放入迭代器
public void addIterator(Iterator iterator){
iterators.add(iterator);
}
//不需要知道有些什么餐厅 完全解耦
public void printMenu(){
Iterator iterator = null;
MenuItem menuItem = null;
for(int i = 0; i < iterators.size(); i++){
iterator = iterators.get(i); //每一个集合容器
while(iterator.hasNext()){
menuItem = (MenuItem)iterator.next();
System.out.println(menuItem.getName() + " " + menuItem.getPrice());
}
}
}
}
测试类:
package iterator.good;
public class MyTest {
public static void main(String[] args) {
CakeHouse cake = new CakeHouse();
DinerHouse diner = new DinerHouse();
Waitress waitress = new Waitress();
waitress.addIterator(cake.getIterator());
waitress.addIterator(diner.getIterator());
waitress.printMenu();
}
}
输出效果(和之前的一样):
Cakefood1 33.3
Cakefood2 44.4
Dinerfood1 11.1
Dinerfood2 22.2
使用迭代器设计模式很好的满足了扩容性和解耦。
Java内置迭代器设计
三家餐厅: (其中只有中餐厅使用的是数组,所以还是自己要添加一个,并实现Java中的Iterator接口)
package iterator.java;
import java.util.ArrayList;
import java.util.Iterator;
/**
* 蛋糕店
*/
public class CakeHouse {
private ArrayList<MenuItem> menuItems; //因为arrayList在java内部已经是实现了Iterator接口,所以可以直接得到
public CakeHouse() {
menuItems = new ArrayList<>();
addItem("Cakefood1",33.3);
addItem("Cakefood2",44.4);
}
private void addItem(String name,double price){
MenuItem menuItem = new MenuItem(name,price);
menuItems.add(menuItem);
}
public Iterator getIterator(){
return menuItems.iterator(); //内部的
}
}
package iterator.java;
import java.util.Iterator;
/**
* 中餐厅
*/
public class DinerHouse {
private final static int Max_Item = 5;//最多只有五个菜单项
private int cur; //游标 注意这里设置成public 的是为了要Waitress中使用
private MenuItem[] menuItems;
public DinerHouse() {
menuItems = new MenuItem[Max_Item];
addItem("Dinerfood1",11.1);
addItem("Dinerfood2",22.2);
}
public void addItem(String name,double price){
MenuItem menuItem = new MenuItem(name,price);
if(cur >= Max_Item){
System.out.println("sorry! menu is full! can't add any item!");
}else {
menuItems[cur++] = menuItem;
}
}
/**
* 实现的是 Java内置的util包里面的
*/
private class DinerHouseIterator implements Iterator {
private int pos;
public DinerHouseIterator() {
pos = 0;
}
@Override
public boolean hasNext() {
if(pos < cur){
return true;
}
return false;
}
@Override
public Object next() {
MenuItem menuItem = menuItems[pos++];
return menuItem;
}
@Override
public void remove() {
}
}
public Iterator getIterator(){
return new DinerHouseIterator();
}
}
新加的咖啡馆: (使用HashTable):
package iterator.java;
import java.util.Hashtable;
import java.util.Iterator;
/**
* 新增的咖啡餐厅
*/
public class CafeHouse {
private Hashtable<String,MenuItem> menuItems;
public CafeHouse() {
menuItems = new Hashtable<>();
addItem("Cafefood1",55.5);
addItem("Cafefood2",66.6);
}
public void addItem(String name,double price){
MenuItem menuItem = new MenuItem(name,price);
menuItems.put(name,menuItem);
}
public Iterator getIterator(){
return menuItems.values().iterator(); //都有一个聚类 包括keys()和values()
}
}
菜单项和女服务完全不需要改动:
package iterator.java;
/**
* 每个餐厅的菜单项
*/
public class MenuItem {
private String name;
private double price;
public MenuItem(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
package iterator.java;
import java.util.ArrayList;
import java.util.Iterator;
/**
* 女招待 直接遍历Iterator
*/
public class Waitress {
private ArrayList<Iterator> iterators = new ArrayList<>();
public Waitress() {
}
//放入迭代器
public void addIterator(Iterator iterator){
iterators.add(iterator);
}
//不需要知道有些什么餐厅 完全解耦
public void printMenu(){
Iterator iterator = null;
MenuItem menuItem = null;
for(int i = 0; i < iterators.size(); i++){
iterator = iterators.get(i); //每一个集合容器
while(iterator.hasNext()){
menuItem = (MenuItem)iterator.next();
System.out.println(menuItem.getName() + " " + menuItem.getPrice());
}
}
}
}
测试:
package iterator.java;
public class MyTest {
public static void main(String[] args) {
CakeHouse cake = new CakeHouse();
DinerHouse diner = new DinerHouse();
CafeHouse cafe = new CafeHouse();
Waitress waitress = new Waitress();
waitress.addIterator(cake.getIterator());
waitress.addIterator(diner.getIterator());
waitress.addIterator(cafe.getIterator());
waitress.printMenu();
}
}
效果:
注意这里的Hash表输出和添加的顺序无关
再次体会到这样设计的好处:
- 不需要修改女招待的代码;
- 解耦和封装;
单一责任原则
概念: 一个类应该只有一个引起变化的原因。
- 一个类只解决一个问题或者一个变化,比如说菜单项这个类,只是用来存储菜单的,至于遍历以及其他,由其他类完成;
- 就是尽量不要一个类牵扯太多东西,目的还是为了降低耦合性;