你项目组接到一个项目:对电视机的电视频道、电影和收音机菜单进行统一管理,建立一个统一的菜单管理界面,能够看到所有的电视界面、电影界面和收音机频道。你有三个手下:小李子、小杏子、小安子,他们分别就每个模块做开发工作,看他们都做了哪些工作。
这是主菜单JavaBean,用于显示每个模块的菜单。
public class MenuItem {
private String name;
private String description;
private int channe;
public MenuItem(int channe,String name,String description){
this.name = name;
this.description = description;
this.channe = channe;
}
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 int getChanne() {
return channe;
}
public void setChanne(int channe) {
this.channe = channe;
}
}
小李子负责电视频道菜单的实现。他是使用List实现的,他认为这样就可以非常方便的扩展菜单。
public class TVChanneMenu implements TelevisionMenu{
List<MenuItem> menuItems;
/**
* 构造函数完成初始化
*/
public TVChanneMenu(){
menuItems = new ArrayList<MenuItem>();
addItem(1, "CCTV-1", "This is CCTV-1");
addItem(2, "CCTV-2", "This is CCTV-2");
addItem(3, "CCTV-3", "This is CCTV-3");
addItem(4, "CCTV-4", "This is CCTV-4");
addItem(5, "CCTV-5", "This is CCTV-5");
}
/**
* @desc 将电视频道节目添加菜单集合中
* @param channe 频道
* @param name 名称
* @param description 描述
* @return void
*/
public void addItem(int channe,String name,String description){
MenuItem tvMenuItem = new MenuItem(channe, name, description);
menuItems.add(tvMenuItem);
}
public List<MenuItem> getMenuItems() {
return menuItems;
}
}
小杏子负责电影菜单模块的开发。她是使用数组完成的,他认为数组的效率高些,而且能够控制菜单的长度。
public class FilmMenu implements TelevisionMenu{
static final int MAX_ITEMS = 5; //菜单最大长度
MenuItem[] menuItems;
int numberOfItems = 0;
/**
* 构造函数完成初始化
*/
public FilmMenu(){
menuItems = new MenuItem[MAX_ITEMS];
addItem(1, "绝世天劫", "这是布鲁斯威利斯主演的...");
addItem(2, "达芬奇密码", "这是我最喜欢的电影之一...");
addItem(3, "黑客帝国123", "不知道你能够看懂不???");
addItem(4, "我的女友是机器人", "一部我不反感的经典爱情电影...");
addItem(5, "肖申克的救赎", "自由,幸福,离你有多远");
}
/**
* @desc 将电影解决添加到菜单项中
* @param channe
* @param name
* @param description
* @return void
*/
public void addItem(int channe,String name,String description){
MenuItem tvmenuiItem = new MenuItem(channe, name, description);
//判断数组是否越界
if(numberOfItems > MAX_ITEMS){
System.out.println("不好意思,菜单满了....");
}
else{
menuItems[numberOfItems] = tvmenuiItem;
numberOfItems ++;
}
}
public MenuItem[] getMenuItems() {
return menuItems;
}
}
这里就只介绍两个了。他们完成了各自菜单功能的实现,当你在做三个菜单的统一显示时,你必须要要调用他们的getMenuItems(),来取得他们各个菜单里面的值,但是这个时候问题出现了,你发现他们的返回值都不相同,要想展示出来你必须这么干。
List<MenuItem> tvMenuItems = tvMenu.getMenuItems();
for(int i = 0 ; i < tvMenuItems.size() ; i++){
MenuItem menuItem = tvMenuItems.get(i);
............
}
FilmMenu filmMenu = new FilmMenu();
MenuItem[] filmItems = filmMenu.getMenuItems();
for(int i = 0 ; i < filmItems.length ; i++){
MenuItem menuItem = filmItems[i];
............
}
在这里我们总是需要来处理这个两个菜单,通过循环来遍历这些菜单选项,如果小安子的实现也不同呢?是不是得要三个循环呢?如果下次有需要增加一个模块呢?在这里你就恼火了,他们为什么不采用相同的实现方式,但是这个时候你也不可能让他们去更改了,他们有太多的代码在依赖着这个两个菜单了,如果更改就意味着要重写很多的代码了。所以你就想是不是可以来封装循环呢?不错就是封装遍历。这就是迭代器模式的动机–能够游走于聚合内的每一个元素,同时还可以提供多种不同的遍历方式。
一:定义
迭代器模式(Iterator Pattern):提供一种方法顺序访问一个聚合对象中的各个元素,而不是暴露其内部的表示。
意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
主要解决:不同的方式来遍历整个整合对象。
何时使用:遍历一个聚合对象。
如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。
二:UML
从上面可以看书迭代器模式有如下几个角色:
Iterator: 抽象迭代器:所有迭代器都需要实现的接口,提供了游走聚合对象元素之间的方法。
ConcreteIterator: 具体迭代器。利用这个具体的迭代器能够对具体的聚合对象进行遍历。每一个聚合对象都应该对应一个具体的迭代器。
Aggregate: 抽象聚合类。
ConcreteAggregate: 具体聚合类。实现creatorIterator()方法,返回该聚合对象的迭代器。
三:简单代码实现
首先我们需要定义迭代器接口。Iterator.java
public interface Iterator {
boolean hasNext();
Object next();
}
然后是我们两个具体的迭代器。一个迭代器遍历电视界面、一个迭代器遍历电影界面。
电影节目的迭代器:FilmMenuIterator.java
public class FilmMenuIterator implements Iterator{
MenuItem[] menuItems;
int position = 0;
public FilmMenuIterator(MenuItem[] menuItems){
this.menuItems = menuItems;
}
public boolean hasNext() {
if(position > menuItems.length-1 || menuItems[position] == null){
return false;
}
return true;
}
public Object next() {
MenuItem menuItem = menuItems[position];
position ++;
return menuItem;
}
}
电视界面的迭代器:TVChanneMenuIterator.java
public class TVChanneMenuIterator implements Iterator{
List<MenuItem> menuItems;
int position = 0;
public TVChanneMenuIterator(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() {
MenuItem menuItem = menuItems.get(position);
position ++;
return menuItem;
}
}
然后是菜单接口,该接口提供返回具体迭代器的方法:createIterator()。
public interface TelevisionMenu {
public void addItem(int channe,String name,String description);
public Iterator createIrerator();
}
两个具体聚合类。这个两个聚合类实现createIterator()方法,分别返回电视界面的聚合类和电影界面的聚合类。
public class FilmMenu implements TelevisionMenu{
static final int MAX_ITEMS = 5; //菜单最大长度
MenuItem[] menuItems;
int numberOfItems = 0;
/**
* 构造函数完成初始化
*/
public FilmMenu(){
menuItems = new MenuItem[MAX_ITEMS];
addItem(1, "绝世天劫", "这是布鲁斯威利斯主演的...");
addItem(2, "达芬奇密码", "这是我最喜欢的电影之一...");
addItem(3, "黑客帝国123", "不知道你能够看懂不???");
addItem(4, "我的女友是机器人", "一部我不反感的经典爱情电影...");
addItem(5, "肖申克的救赎", "自由,幸福,离你有多远");
}
/**
* @desc 将电影解决添加到菜单项中
* @param channe
* @param name
* @param description
* @return void
*/
public void addItem(int channe,String name,String description){
MenuItem tvmenuiItem = new MenuItem(channe, name, description);
//判断数组是否越界
if(numberOfItems > MAX_ITEMS){
System.out.println("不好意思,菜单满了....");
}
else{
menuItems[numberOfItems] = tvmenuiItem;
numberOfItems ++;
}
}
public Iterator createIrerator() {
return new FilmMenuIterator(menuItems);
}
}
public class TVChanneMenu implements TelevisionMenu{
List<MenuItem> menuItems;
/**
* 构造函数完成初始化
*/
public TVChanneMenu(){
menuItems = new ArrayList<MenuItem>();
addItem(1, "CCTV-1", "This is CCTV-1");
addItem(2, "CCTV-2", "This is CCTV-2");
addItem(3, "CCTV-3", "This is CCTV-3");
addItem(4, "CCTV-4", "This is CCTV-4");
addItem(5, "CCTV-5", "This is CCTV-5");
}
/**
* @desc 将电视频道节目添加菜单集合中
* @param channe 频道
* @param name 名称
* @param description 描述
* @return void
*/
public void addItem(int channe,String name,String description){
MenuItem tvMenuItem = new MenuItem(channe, name, description);
menuItems.add(tvMenuItem);
}
public Iterator createIrerator() {
return new TVChanneMenuIterator(menuItems);
}
}
终于完成了,现在就可以来实现主菜单了,用来展示、遍历所有的电视、电影界面咯。
public class MainMenu {
TelevisionMenu tvMenu;
FilmMenu filmMenu;
public MainMenu(TelevisionMenu tvMenu,FilmMenu filmMenu){
this.tvMenu = tvMenu;
this.filmMenu = filmMenu;
}
public void printMenu(){
Iterator tvIterator = tvMenu.createIrerator();
Iterator filmIterator = filmMenu.createIrerator();
System.out.println("电视节目有:");
printMenu(tvIterator);
System.out.println("----------------------------------------------------------------");
System.out.println("电影节目有:");
printMenu(filmIterator);
}
private void printMenu(Iterator iterator) {
while(iterator.hasNext()){
MenuItem menuItem = (MenuItem) iterator.next();
System.out.print("channe:"+menuItem.getChanne()+", ");
System.out.print("name:"+menuItem.getName()+", ");
System.out.println("description :"+menuItem.getDescription());
}
}
}
测试程序
public class Test {
public static void main(String[] args) {
TVChanneMenu tvMenu = new TVChanneMenu();
FilmMenu filmMenu = new FilmMenu();
MainMenu mainMenu = new MainMenu(tvMenu, filmMenu);
mainMenu.printMenu();
}
}
四:优点
1、它支持以不同的方式遍历一个聚合对象。
2、迭代器简化了聚合类。
3、在同一个聚合上可以有多个遍历。
4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
五:缺点
由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
六:适用场景
1、访问一个聚合对象的内容而无须暴露它的内部表示。
2、需要为聚合对象提供多种遍历方式。
3、为遍历不同的聚合结构提供一个统一的接口。