JAVA基础(七)迭代器及结构类模式总结


好了,这期开始介绍迭代器模式,组合模式和剩下的结构类模式,
首先介绍迭代器+组合模式,虽然前者属于行为类模式,后者属于结构模式,但Head First将这两种模式放在一起进行介绍,足见二者关系紧密。没有结构,何来迭代?这里正好一并介绍,正好为结构类设计模式起头。

一、迭代器模式Iterator:
迭代器模式Iterator:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示
在Head First中,迭代器模式用于统一不同参观菜单数据结构的输出方式,只要我们提供统一的迭代器接口,那么菜单的遍历对服务员来说就是透明的:
我们先一步到位实现统一menu接口的迭代器模式,假设menu1为数组形式,menu2为ArrayList形式,那么前者需要自己定义迭代器的方法,而后者可以直接返回java.util.Iterator格式的迭代器,我们首先定义数组迭代器:
定义一个统一的menu接口,可以调用createIterator返回统一接口迭代器
public interface Menu {		//具备返回迭代器的菜单接口
 public abstract Iterator<String> createIterator();
}
因为数组没有迭代器,所以我们要创建一个:
public class Menu1Iterator implements Iterator<String>{
	Menu1 menu1;
	int i;
	public Menu1Iterator(Menu1 menu1){
		this.menu1 = menu1;	
		i=0;
	}
	public boolean hasNext() {
		if(i<=menu1.itemnum)
			return true;
		return false;
	}
	public String next() {
		i++;
		return menu1.menu[i-1];
	}
	public void remove() {
		for(int j = i;j<menu1.itemnum;j++){
			menu1.menu[j-1]=menu1.menu[j];
		}
		menu1.itemnum--;
	}
}
继承接口的菜单menu1
public class Menu1 implements Menu {
	static final int MAX_ITEM = 2;
	int itemnum = 0;
	String[] menu = new String[MAX_ITEM];
	public Menu1(){
		menu[0]="menu1item1";
		itemnum++;
		menu[1]="menu1item2";
		itemnum++;
	}
	public Iterator<String> createIterator() {
		return new Menu1Iterator(this);
	}
}
菜单menu2
public class Menu2 implements Menu {
	private ArrayList<String> menu = new ArrayList<String>();
	public Menu2(){
		menu.add("menu2item1");
		menu.add("menu2item2");
	}
	public Iterator<String> createIterator() {
		return menu.iterator();
	}
}
我们可以看到,通过继承统一接口menu,我们能够使用统一的迭代器方法对数据进行遍历和其它操作了,但是这个数据结构的可扩展性并不尽如人意,如果要添加菜单,就需要打开遍历代码,重新加入,如果要增加子菜单就更是不可能的,因此,就引出了下面的组合模式


二、组合模式Composite:
组合模式Composite:允许你将对象组合成树形结构来表现“整体/部分”层次结构,组合能让客户以一致的方式处理个别对象以及对象组合。
组合模式有一个很有用的特性,通过封装,组合模式能够使叶子节点和树节点具备相同的接口,使得遍历变成了相对简单的树遍历问题,可扩展性大大增强,这里我们仍然采用简化的方法,先定义好基本类型MenuComp:
public interface MenuComp {
	public abstract void add(MenuComp mc);	             //添加节点,只对菜单类有用
	public abstract void remove(MenuComp mc);	     //删除节点,只对菜单类有用
	public abstract Iterator<MenuComp> createIterator(); //创建迭代器,只对菜单类有用
}
定义NullIterator,其作用是用于返回统一的Iterator接口,这样就不需要针对Null指针进行大量的校验了(因为我们要对菜单项类能够调用相同的接口,方式程序崩溃):
public class NullIterator implements Iterator<MenuComp> {
	public boolean hasNext() {return false;}	//没有下一个
	public MenuComp next() {return null;}		//下一个是空的
	public void remove() {}				//什么也不做
}

定义Menu类,即菜单类,需要实现所有功能:
public class Menu implements MenuComp {
	private String menu;
	private ArrayList<MenuComp> menulist = new ArrayList<MenuComp>();	//ArrayList列表,其实任何格式都可以
	public Menu(String s){  menu = s;  }					//弄个列表名,以后要打印可以调用
	public void print(){  System.out.println(menu);  }			//列表名打印
	public void add(MenuComp mc) {  menulist.add(mc);  }			//往列表里直接加元素
	public void remove(MenuComp mc) {  menulist.remove(mc);  }		//往列表里删除元素
	public Iterator<MenuComp> createIterator() {  return new CompositeIterator(menulist.iterator());  }	//组合迭代器生成,后面介绍
}
MenuItem类:
public class MenuItem implements MenuComp {
	private String menuitem;
	public MenuItem(String s){  menuitem = s;  }
	public void print(){  System.out.println(menuitem);  }
	public void add(MenuComp mc) {	}    //什么都不做
	public void remove(MenuComp mc) {  } //什么都不做
	public Iterator<MenuComp> createIterator(){  return new NullIterator();  }	//返回之前定义的空指针
}
定义CompositeIterator类,这个类通过树形结构,实现Menu的前序遍历,这段代码逻辑可说是Head First书本中最为抽象的代码之一(要是有指针那真是方便太多啦。。。)
public class CompositeIterator implements Iterator<MenuComp> {
	Stack<Iterator<MenuComp>> s = new Stack<Iterator<MenuComp>>();		//记录遍历深度及每一个层次的遍历位置
	public CompositeIterator(Iterator<MenuComp> iterator){  s.push(iterator);  }	//创建迭代器,存储此menu的迭代器
	public boolean hasNext() {
		Iterator<MenuComp> iterator = s.peek();				//取出此层迭代器	
		if(iterator == null)  return false;				//没有迭代器了,遍历结束
		else{
			if(!iterator.hasNext()){				//此层没有记录了
				s.pop();					//此层迭代器出栈,准备迭代
				return hasNext();				//迭代取上一层迭代器,确认上层是否有记录
			}
			else return true;					//此层找到记录,直接返回成功
		}}
	public MenuComp next() {
		if(hasNext()){							//有下条记录
			Iterator<MenuComp> iterator = s.peek();			//取用此层迭代器
			MenuComp mc = iterator.next();				//此层迭代器取下一个记录
			if(mc instanceof Menu) s.push(mc.createIterator());	//下个记录是一个menu,把这个menu的迭代器放入栈,以进一步深层遍历
		}
		return null;							//没元素了,直接退出
	}
	public void remove() {}							//什么也不做
}
CompositeIterator的定义和书中有些差别,除了适度的简化之外,还将Iterator进行了显化,这是因为,根据Effective Java23条,应尽量避免使用原生态模型,这样可以有效避免数据格式问题导致的各种运行时问题,在编译时就能够发现数据格式的错误。当然,我们也可以用Iterator<?>来代替MenuComp,这里只是为了显示方便,代码还是有点抽象的,需要认真理解,不知道递归有没有成为你的朋友^_^

下面几个设计模式本来想单独开贴进行介绍,但是写出来发现实在比较简单,所以就直接写在后面了:


三、装饰者模式Decorator:
装饰者模式Decorator:动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案
假设我们开了一个饮料店,为了能够有效扩展描述和费用,我们需要这么一个基本类,作为作料和主体饮料的基类:
public abstract class Item {
	public abstract double cost();		//消费金额
	public abstract String description();	//产品描述
}
好了,我们来定义一个5块钱的饮料:
public class Beverage extends Item {
	public double cost(){  return 5;  }		//五块钱
	public String description() {  return "Beverage";  }	//一个饮料
}
下面我们需要定义一个作料,为了能保有原功能,我们将通过装饰着模式对饮料进行扩展:
public class Cordiment extends Item {
	Item it;			//在作料中封装饮料对象
	public Cordiment(Item it){  this.it = it;  }
	public double cost() {  return it.cost()+1;  }	//原饮料费用+1
	public String description() {  return it.description()+"+Cordiment";  }	//原描述补上作料
}
直接执行以下代码,我们就实现了将作料加入饮料的过程:
public static void main(String[] args) {
	Beverage b = new Beverage();
	Item it = new Cordiment(b);	//此步最为关键,通过传入原饮料对象,实现了cost和description的装饰功能
	System.out.println(it.cost());
	System.out.println(it.description());
}

可以看到,通过装饰者模式,可以在不修改原基础类的情况下,快速的实现功能的叠加,这种设计模式在JAVA的IO类库种应用十分广泛



四、适配器模式Adapter:
适配器模式Adapter:将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
适配器用于解决接口格式不一致的问题,考虑Head First中的例子,我们想要将土鸡放到鸭子类中,该怎么做呢,只需要给土鸡包一个鸭子皮儿~~~
先看看鸭子接口(鸭子皮):
public interface Duck {
	public abstract void quack();
	public abstract void fly();
}
再来看看土鸡接口:
public interface Turkey {
	public abstract void gobble();	//土鸡叫换了个名字
	public abstract void fly();
}
一个土鸡的实现:
public class WildTurkey implements Turkey {
	public void gobble() { System.out.println("gogogo!"); }
	public void fly() {System.out.println("weak fly!");}
}
让我们把土鸡塞到鸭子接口中~~~
public class TurkeyAdapter implements Duck {
	Turkey tk;			//里面有一只土鸡!!
	public TurkeyAdapter(Turkey tk) { this.tk = tk;}	//土鸡传入鸭子接口
	public void quack() { tk.gobble(); }			//实际上执行土鸡的叫法
	public void fly() { tk.fly(); }				//执行土鸡的飞翔
}
适配器模式非常直观,当我们要转换接口的时候,只需要做两步,包皮儿,新接口调用旧接口~~~


五、外观模式Facade:
外观模式Facade:提供了一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层接口,让子系统更容易使用。这个模式结构十分简单,我们可以理解成通过一个函数接口,我们能够调用多个功能,在Head First中甚至只是放在适配器模式章节的内部,我甚至认为这根本就不能称为一个模式。。。下面看个例子
public class WildTurkey implements Turkey {
	public void gobble() { System.out.println("gogogo!"); }
	public void fly() {System.out.println("weak fly!");}
	public void turkeymethod(){		//好了,外观模式实现了╮(╯▽╰)╭
		gobble();
		fly();
	}
}
好吧,没什么好说得,这个模式真是水出了新境界。。。


设计模式是程序设计的基础,也是编写高质量代码的前提,由于篇幅及能力限制,目前只能介绍几种基本的设计类型,在以后的学习中,有机会会继续总结更高级的复合设计模式,现阶段就先到此为止了~~~
完结,撒花╮(╯▽╰)╭



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值