迭代模式:
现在考虑,一家早餐厅和一家汉堡店要合并,早餐听的菜单用来提供早餐,汉堡店中餐,
public class BreakfirstMenu{
private List menuItem;
public List getMenuItem(){
return menuItem;
}
public void addMenuItem(MenuItem item){
menu.add(item);
}
...
}
这里认为MenuItem 有getName(),getPrice(),getDes()方法
public class HanbergerMenu{
private MenuItem[] menuItem;
public MenuItem[] getMenuItem(){
return menuItem;
}
....//set 方法the same
}
现在要做的是waitress的事,当每天早上的时候她需要提供一份早餐单,中午午餐单,Is that so much trouble?
yes!! obvious
我们现在要做的是使两个菜单合并起来,It's easy . you may say. give them a interface to implements
yes that we need to do(but 只是一部分)
你能看到上面两个类的区别? 他们包含MenuItem集合的数据结构不同
对,因此两个类的get方法有区别
当waitress要遍历,比如打印菜单时需要两部走,先List在array?
怎么能把它们合成一步呢?
这里需要用到迭代器模式:
什么是迭代器?
JAVA中其实已经提供了迭代器Iterator,你可能已经注意到了,像List Set Vector等实现Collection接口的都有.Iterator()返回相应的迭代器,然后你就可以通过hasNext()和next()来处理
这里我实现一个array的迭代器给HanbergerMenu使用
public BergerIterator implements Iterator{
private MenuItem[] menuItem;
private int pos;
public boolean hasNext(){
if(pos >= menuItem.length || menuItem[pos] == null){
return false;
}else{
return true;
}
}
public Object next(){
MenuItem item = menuItem[pos];
pos = pos + 1;
return item;
}
public void remove(){
throw new UnSupportException();
}
}
其他的集合实现起来也差不多是这样
now
改一下HanbergerMenu
public class HanbergerMenu{
public Iterator createIterator(){
return new BergerIterator(menuItem);
}
....
}
可以在abstract Menu/Menu 接口中有createIterator() 就像Collection中一样
这样waitress在遍历是就不用些两个for循环了,只用封装一个遍历Iterator的方法,调用就行了
problem soft
更不用提,现在大部分的集合都提供了自己的Iterator实现,因此使用起来很方便都不需要实现自己的BergerIterator(数组还是要的)
甚至HashMap也可以 只要得到map.value().iterator()就行了(map.value()返回Set)
now I have a question?
为什么BreakfirstMenu 和HanbergerMenu不继承Menu在实现Iterator 呢?
这里我们就再次碰到了一个OO 原则:
单一责任,尽量使你的类保持责任单 一
Why ? 因为,单一责任,意味着变化更少,内聚更高,责任越多说明了类的变化因素越多,可能一些变动本来不会影响此类,但是因此责任牵连而此类也需要更改。
同时,可以看见当使用了迭代模式后,waitress不在了解Menu的数据结构,不论是array还List 对她来说都是iterator
因此使用迭代器的另一个好处是,隐藏了内部实现方式
组合模式:
上面的Menu 中包含了MenuItem的集合,当我们不仅需要MenuItem , 而Menu中还需要SubMenu时,怎么办
整个数据结构看上去就破坏了??
So, I think about the tree!!!
树形结构可以很好的解释上面的数据结构(SubMenu是子节点,MenuItem是leaf)
但是怎么实现它??我们要求的是:
1.能够很好的在全树的各个节点之间遍历,游走
2.和每个菜单的菜单项和子菜单之间也要能够很好的遍历
实现方式,这并不太难,使用一共公共的父类Component
public abstract class Component{
public add(Component component);
public remove();
public getChild();
public void operation();//for the MenuItem;
}
public class MenuItem implements Component{
public void operation(){
//....
}
public void add(...){
throw unsupport...
}
...
}
这里可以看到MenuItem的实现,只是它需要的,一些Node的方法,它不支持(throw就行了)
public class Menu implements Component{
List components = new ArrayList();
....
}
这里的Menu的包含了List<Component>
组合的特点:
1.以树的方式包含对象组合和个别对象的结构
2.对象的组合和个别对象实现了一样的接口,因此他们的操作一样(方法),把两者有机的结合起来,不需要区别处理,忽略了两者的区别(这是很好的一点)
再看Menu,其中包含的组合是List,万一其他的Menu不是List呢?这就是前面迭代碰到的问题
因此使用迭代来解决,把组合和迭代结合起来
public abstract class Component{
...
public Iterator createIterator();
...
}
提供一个返回Iterator的方法
这里我在实现一个外面迭代的例子:
public class ComposistIterator implements Iterator{
Stack stack = new Stack();
public ComposistIterator (Iterator iterator){
stack.push(iterator);
}
public boolean hasNext(){
if(stack.empty()){
return false;
}else{
Iterator it = (Iterator)stack.peek();
if(!it.hasNext()){
stack.pop();
return hasNext();
}else{
return true;
}
}
}
publc Object next(){
if(hasNext()){
Iterator it = (Iterator)stack.peek();
MyComponent component = (MYComponent)it.next();
if(component instanceof MyComponent){
stack.push(component.createIterator));//一旦是Menu则将它的it,push到stack中
}
return component;
}else{
return null;
}
}
在menuItem的createIterator()中我们可以简单的返回一个null
但是更好的一种处理是,返回一个hasNext永远是false的空迭代器
public class NullIterator implements Iterator{
public Object next(){
return null;
}
public boolean hasNext(){
return false;
}
public void remove(){
}
}
组合模式我们看到它违反了前面迭代模式讲到的单一责任原则,这又如何解释呢?
可以看到,现在不论对于wairtess还是Menu内部,MenuItem和SubMenu的操作是一样的,遍历集合的时候可以
直接操作,这就是透明性
一旦我们将责任分离了,意味着MenuItem和Menu时要不停的使用instanceof来if判断是十分麻烦的,因此组合是
牺牲一旦责任来换取透明性的一种设计模式