设计模式(十七)----迭代器模式

有一个项目,要求对电视机的电视台频道,电影频道的菜单进行统一管理,建立一个统一的菜单管理界面,能够看到所有的电视台界面和电影界面。该项目由两位开发人员进行开发。
主菜单JavaBean,用于显示每个模块的菜单

public class MenuItem {
	//频道号
	private int channel;
	//名称
	private String name;
	//描述
	private String description;
	//省略无参构造、代参构造,各个属性的setter和getter方法
}

人员一负责电视台频道菜单的实现,他使用List实现,他认为这样可方便的扩展菜单

public class TvChannelMenu {
	List<MenuItem> menuItems;

	//构造函数初始化
	public TvChannelMenu() {
		menuItems = new MenuItem();
		addItem(1,"CCTV1","中央电视台综合频道");
		addItem(2,"CCTV3","中央电视台综艺频道");
		addItem(3,"CCTV4","中央电视台中文国际频道");
		addItem(4,"CCTV6","中央电视台电影频道");
		addItem(5,"CCTV9","中央电视台记录频道");
		addItem(6,"CCTV10","中央电视台科教频道");
	}
	//将电视台频道节目添加到菜单集合中
	public void addItem(int channel,String name,String description) {
		MenuItem menuItem = new MenuItem(channel,name,description);
		menuItems.add(menuItem);
	}
	//获取菜单集合
	public List<MenuItem> getMenuItems() {
		return menuItems;
	}
}

人员二负责电影频道菜单的实现,他使用数组完成,他认为数组的效率要高一些,而且可以控制菜单长度

public class MovieChannelMenu {
	//菜单(数组最大长度)
	static final int MAX_LENGTH = 6;
	int numOfItems = 1;
	MenuItem[] menuItems;
	//构造函数初始化
	public MovieChannelMenu() {
		menuItems = new MenuItem[MAX_LENGTH];
		addItem(1,"城市猎人","主演:成龙,邱淑贞,王祖贤");
		addItem(2,"超级警察","主演:成龙,元华,杨紫琼");
		addItem(3,"功夫","主演:周星驰,梁小龙,陈国坤");
		addItem(4,"少林寺","主演:李连杰,于承惠,计春华");
		addItem(5,"大内密探零零狗","主演:古天乐,吴君如,樊少皇");
		addItem(6,"X战警","主演:休杰克曼,伊恩麦克莱恩,帕特里克斯图尔特");
	}
	//将电影频道添加到菜单中
	public void addItem(int channel,String name,String description) {
		MenuItem menuItem = new MenuItem(channel,name,description);
		//判断数组是否越界
		if(numOfItems < MEX_LENGTH) {
			menuItems[numOfItems] = menuItem;
		} else {
			System.out.println("菜单已满!!");
		}
	}
	//获取电影菜单
	public MenuItem[] getMenuItems() {
		return menuItems;
	}
}

当做两个菜单的统一显示时,须调用他们的getMenuItems()方法,来取得各个菜单里面的值,但它们的返回值都不同,要想展示须这样

public class MainMenu {
	public void printMenu() {
		//打印电视菜单
		TvChannelMenu tvChannelMenu = new TvChannelMenu();
		List<MenuItem> menuItems = tvChannelMenu.getMenuItems();
		System.out.println("电视节目有:");
		for(int i = 0;i < menuItems.size();i++) {
			MenuItem menuItem = menuItems.get(i);
			printMenu(menuItem);
		}
		System.out.println("==========================");
		
		//打印电影菜单
		MovieChannelMenu movieChannelMenu = new MovieChannelMenu();
		MenuItem[] menuItems2 = movieChannelMenu.getMenuItems();
		System.out.println("电影节目有:");
		for(int i = 0;i < menuItems.length;i++) {
			MenuItem menuItem = menuItems2[i];
			printMenu(menuItem);
		}
		//打印菜单方法
		private void printMenu(MenuItem menuItem) {
			System.out.print("频道:" menuItem.getChannel() + "----");
			System.out.print("名称:" menuItem.getName() + "----");
			System.out.println("描述:" + menuItem.getDescription());
		}
	}
}

测试类

public class Test {
	public static void main(String[] args) {
		MainMenu mainMenu = new MainMenu();
		mainMenu.printMenu();
	}
}

打印结果
在这里插入图片描述
从上面的代码可以看出,需要两个循环遍历这些菜单选项。
可以封装遍历,也就是迭代器模式的动机–能够游走于聚合内的每个元素,同时还可以提供多种不同的遍历方式。

概述

迭代器模式(Iterator Pattern),时Java和.Net编程环境常用的设计模式。该模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。属于行为型模式。
实际开发中,可能需要针对不同的需求,以不同的方式来遍历整个对象,但不希望在聚合对象的抽象接口层中充斥着各种不同的遍历操作。这时,就需要这样一种东西,它应具备如下三个功能:
能够遍历一个聚合对象;
不需要知道聚合对象的内部结构;
能够提供多种不同的遍历方式。
上面三个功能就是迭代器模式需要解决的问题。迭代器模式把在元素间游走的责任交给迭代器,而非聚合对象。这样做就简化了聚合的接口和实现,也可以让聚合更专注于其应该专注的事情上,更加符合单一责任原则。
迭代器模式有如下几个角色
Iterator:抽象迭代器。所有迭代器都需要实现的接口,提供了游走聚合对象元素之间(遍历元素)的方法,一般来说会有三个方法:取得下一个元素的方法next(),判断是否遍历结束的方法hasNext()和移除当前对象的方法remove()。
ConcreteIterator:具体迭代器。实现抽象迭代器中定义的方法。利用具体迭代器能够对具体的聚合对象进行遍历。每个聚合对象都应对应一个具体迭代器。
Aggregate:抽象聚合类。一般是一个接口,提供一个createIterator()方法,如Java中的Cellection接口,List接口,Set接口等。
ConcreteAggregate:具体聚合类。实现Aggregate中的createIterator()方法,返回该聚合对象的迭代器。如List接口的有序列表实现ArrayList,List接口的链表实现LinkedList,Set接口的哈希列表的实现HashSet等。

实例

利用迭代器模式对上面的案例进行优化整改
定义迭代器接口(抽象迭代器)

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

具体迭代器,分别用于遍历电视界面和电影界面
电视频道迭代器

public class TvChannelMenuIterator implements Iterator {
	List<MenuItem> menuItems;
	int position = 0;
	
	//构造方法
	public TvChannelMenuIterator(List<MenuItem> menuItems) {
		this.menuItems = menuItems;
	}
	
	//判断是否还有下一元素
	public boolean hasNext() {
		if(position > menuItems.size() -1 || menuItems.get(position) == null) {
			return false;
		} else {
			return true;
		}
	}
	
	//获取下一元素
	public Object next() {
		return menuItems.get(position++);
	}
}

电影频道迭代器

public class MovieChennelIterator implements Iterator {
	MenuItem[] menuItems;
	int position = 0;
	
	//构造方法
	public MovieChannelIterator(MenuItem[] menuItems) {
		this.menuItems = menuItems;
	} 
	
	//是否还有下个元素
	public boolean hasNext() {
		if(position > menuItems.length -1 || menuItems[position] == null) {
			return false;
		} else {
			return true;
		}
	}
	
	//获取下一元素
	public Object next() {
		return menuItems[position++];
	}
}

两个具体聚合类TvChannelMenu和MovieChannelMenu实现菜单接口,在类中实现createIterator()方法
TvChannelMenu类中,其他不变,新增createIterator()方法

public Iterator createIterator() {
	return new TvChannelIterator(menuItems);
}

MovieChannelMenu类中,其他不变,新增createIterator()方法

public Iterator createIterator() {
	return new MovieChannelIterator(menuItems);
}

主菜单类,用于遍历、显示所有的电视、电影界面

public class MainMenu {
	TvChannelMenu tvChannelMenu;
	MovieChannelMenu movieChannelMenu;
	
	//构造方法
	public MainMenu(TvChannelMenu tvChannelMenu,MovieChannelMenu movieChannelMenu) {
		this.tvChannelMenu = tvChannelMenu;
		this.movieChannelMenu = movieChannelMenu;
	}
	
	public void printMenu() {
		Iterator tvIterator = tvChannelMenu.createIterator();
		Iterator movieIterator = movieChannelMenu.createIterator();
		System.out.println("电视节目有:");
		printMenu(tvIterator);
		System.out.println("---------------------");
		System.out.println("电影节目有:");
		printMenu(movieIterator);
	}
	
	private void printMenu(Iterator iterator) {
		while(iterator.hasNext) {
			MenuItem menuItem = (MenuItem) iterator.next();
			System.out.print("频道:" + menuItem.getChannel() + "----");
			System.out.print("频道名称:" + menuItem.getName() + "----");
			System.out.println("频道描述:" + menuItem.getDescription());
		}
	}
}

测试类

public class IteratorPatternDemo {
	public static void main(String[] args) {
		TvChannelMenu tvChannelMenu = new TvChannelMenu();
		MovieChannelMenu movieChannelMenu = new MovieChannelMenu();
		
		MainMenu mainMenu = new MainMenu(tvChannelMenu,movieChannelMenu);
		mainMenu.printMenu();
	}
}

打印结果
在这里插入图片描述

总结

优点:支持以不同的方式遍历一个聚合对象;迭代器简化了聚合类;在同一个聚合上可有多个遍历;增加新的聚合类和迭代器类都很方便,无须修改原有代码。
缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类须增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统复杂性。

使用场景:方法一个聚合对象的内容而无需暴露其内部表示;需要为聚合对象提供多种遍历方式;为遍历不同的聚合结构提供一个统一的接口。

将遍历聚合对象中数据的行为分离出来,封装到一个迭代器中,通过专门的迭代器来遍历聚合对象的内部数据,这就是迭代器模式的本质。该模式是"单一职责原则"的经典体现。
迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器来负责,这样既可以做到不暴露集合内部结构,又可让外部代码透明的访问集合内部数据。
使用迭代器时,依赖聚合提供遍历。
一般来说,只要实现一个集合,就需同时提供该集合的迭代器,像java中的Collection、List、Set、Map等,这些集合都有自己的迭代器。
抽象迭代器设计难度较大,需充分考虑到系统将来的扩展,如JDK内置迭代器Iterator就无法实现逆向遍历,若需实现逆向遍历,只能通过其子类ListIterator等来实现,而ListIterator无法用于Set类型的聚合对象,在自定义迭代器时,创建一个考虑全面的抽象迭代器不是件容易的事。

QKRJ THE END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值