一、迭代器模式
迭代器模式应用了封装的原理,封装了可迭代组件的具体实现,游走于聚合内每一个元素,而又不暴露内部的表示。举个例子吧,有两家餐馆,它们现在合并了,这样提供的菜品就是原来的2倍。但是餐馆A使用数组来存储菜品,而餐馆B用列表来存储。如果想看所有的菜品,还得通过两种方式便利得到全部内容,这样很不方便,于是就想到了通过迭代器模式封装它们。
1、菜单接口,两家餐馆都实现了其中的迭代器创建方法
1
2
3
public interface Menu {
public Iterator createIterator ();
}
餐馆A
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class DinerMenu implements Menu {
static final int MAX_ITEMS = 6 ;
int numberOfItems = 0 ;
MenuItem [] menuItems ;
public DinerMenu () {
menuItems = new MenuItem [ MAX_ITEMS ];
addItem ( "Vegetarian BLT" , "Bacon with lettuce" , true , 2.99 );
addItem ( "BLT" , "Bacon with tomato" , false , 2.99 );
addItem ( "Soup of the day" , "With a side of potato salad" , false , 3.49 );
addItem ( "hotdog" , "hot dog with relish" , false , 2.99 );
}
private void addItem ( String name , String description , boolean vegetarian , double price ) {
MenuItem item = new MenuItem ( name , description , vegetarian , price );
if ( numberOfItems > MAX_ITEMS ) {
System . err . println ( "Sorry, menu is full" );
}
menuItems [ numberOfItems ] = item ;
numberOfItems ++;
}
@Override
public Iterator createIterator () {
return new DinerMenuIterator ( menuItems );
}
}
餐馆B
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class PancakeHouseMenu implements Menu {
private List < MenuItem > menuItem ;
public PancakeHouseMenu () {
this . menuItem = new ArrayList < MenuItem >();
addItem ( "K&B's pancake breakfast" , "Pancake with scrambled eggs, sausage" , true , 2.99 );
addItem ( "Regular pancake breakfast" , "Pancake with fried eggs, sausage" , false , 2.99 );
addItem ( "Blueberry pancake" , "Pancake made with fresh blueberries" , true , 3.49 );
addItem ( "Wafles" , "Wafles, with your choice of blueberries or strawberries" , true , 3.49 );
}
private void addItem ( String name , String description , boolean vegetarian , double price ) {
MenuItem item = new MenuItem ( name , description , vegetarian , price );
menuItem . add ( item );
}
@Override
public Iterator createIterator () {
return new PancakeHouseMenuIterator ( menuItem );
}
}
3、两家餐馆的迭代器实现
餐馆A的迭代器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class DinerMenuIterator implements Iterator {
private MenuItem [] menuItems ;
private int currentIndex ;
public DinerMenuIterator ( MenuItem [] menuItems ) {
this . menuItems = menuItems ;
currentIndex = 0 ;
}
@Override
public boolean hasNext () {
return menuItems [ currentIndex + 1 ] != null ;
}
@Override
public Object next () {
MenuItem item = menuItems [ currentIndex ];
currentIndex ++;
return item ;
}
@Override
public void remove () {
}
}
餐馆B的迭代器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class PancakeHouseMenuIterator implements Iterator {
private List < MenuItem > pancakeHouseMenus ;
private int currentIndex ;
public PancakeHouseMenuIterator ( List < MenuItem > pancakeHouseMenus ) {
this . pancakeHouseMenus = pancakeHouseMenus ;
currentIndex = 0 ;
}
@Override
public boolean hasNext () {
return pancakeHouseMenus . size () > currentIndex && pancakeHouseMenus . get ( currentIndex ) != null ;
}
@Override
public Object next () {
MenuItem item = pancakeHouseMenus . get ( currentIndex );
currentIndex ++;
return item ;
}
@Override
public void remove () {
}
}
我们可以看到,它们其实都实现自Iterator接口,这个接口是在java.util包中,相当于迭代器模式也是java内置的,我们只需要直接拿出来用就可以了。两个迭代器类提供给外界的接口都一样,所以客户并不需要知道具体实现,而只关心自己接收的类是否实现了Iterator就行了。
4、女服务员提供点菜功能,他需要打印给客户所有的菜品
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Waitress {
private PancakeHouseMenu pancakeHouseMenu ;
private DinerMenu dinerMenu ;
private CafeMenu cafeMenu ;
public Waitress ( PancakeHouseMenu pancakeHouseMenu , DinerMenu dinerMenu , CafeMenu cafeMenu ) {
this . pancakeHouseMenu = pancakeHouseMenu ;
this . dinerMenu = dinerMenu ;
this . cafeMenu = cafeMenu ;
}
public void printMenu () {
System . out . println ( "MENU\n----\nBREAKFAST" );
printMenu ( pancakeHouseMenu . createIterator ());
System . out . println ( "\nLUNCH" );
printMenu ( dinerMenu . createIterator ());
System . out . println ( "\nCAFE TIME" );
printMenu ( cafeMenu . createIterator ());
}
public void printMenu ( Iterator iterator ) {
while ( iterator . hasNext ()) {
MenuItem menuItem = ( MenuItem ) iterator . next ();
System . out . println ( menuItem . getName () + ", " + menuItem . getPrice () + " -- " + menuItem . getDescription ());
}
}
}
女服务员拿到迭代器,她从菜单项的实现中解耦了。这样对她来说是很好的,因为她能使用同样的代码去遍历容易组能的元素。
5、开始点菜
1
2
3
4
5
6
7
8
9
10
public class WaitressAction {
public static void main ( String [] args ) {
PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu ();
DinerMenu dinerMenu = new DinerMenu ();
CafeMenu cafeMenu = new CafeMenu ();
Waitress waitress = new Waitress ( pancakeHouseMenu , dinerMenu , cafeMenu );
waitress . printMenu ();
}
}
整个程序的构造如下图所示:
二、组合模式
组合模式的定义是允许将对象组合成树形结构来表现“整体/部分”层次结构。组合让客户以一致的方式去处理个别对象以及对象组合。例如,开始我们遍历的是菜单里的菜品,但是现在要求向菜单里加入子菜单,这样一个菜单里也许不光有菜品,还可能有子菜单。如果想要以一种相同的方式去处理菜品和菜单(也许是打印名称、描述),就需要定义一个组件接口来作为菜单和菜单项的共同接口,这样就能够用统一的做法来处理菜单和菜单项。
1、统一接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public abstract class MenuComponent {
public void add ( MenuComponent menuComponent ) {
throw new UnsupportedOperationException ();
}
public void remove ( MenuComponent menuComponent ) {
throw new UnsupportedOperationException ();
}
public MenuComponent getChild ( int i ) {
throw new UnsupportedOperationException ();
}
public String getName () {
throw new UnsupportedOperationException ();
}
public String getDescription () {
throw new UnsupportedOperationException ();
}
public double getPrice () {
throw new UnsupportedOperationException ();
}
public boolean isVegetarian () {
throw new UnsupportedOperationException ();
}
public void print () {
throw new UnsupportedOperationException ();
}
public Iterator createIterator () {
throw new UnsupportedOperationException ();
}
}
2、Menu(菜单)继承自统一接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class Menu2 extends MenuComponent {
private List < MenuComponent > menuComponents = new ArrayList < MenuComponent >();
private String name ;
private String description ;
public Menu2 ( String name , String description ) {
this . name = name ;
this . description = description ;
}
public void add ( MenuComponent component ) {
menuComponents . add ( component );
}
public void remove ( MenuComponent component ) {
menuComponents . remove ( menuComponents );
}
public MenuComponent getChild ( int index ) {
return menuComponents . get ( index );
}
public String getName () {
return name ;
}
public String getDescription () {
return description ;
}
public Iterator createIterator () {
return new CompositeIterator ( menuComponents . iterator ());
}
public void print () {
System . out . println ( "\n" + getName ());
System . out . println ( ", " + getDescription ());
System . out . println ( "----------------------" );
Iterator iterator = menuComponents . iterator ();
while ( iterator . hasNext ()) {
MenuComponent component = ( MenuComponent ) iterator . next ();
component . print ();
}
}
}
然后对应的DinerMenu和PancakeHouseMenu再继承Menu,这里就不写出来了。
3、(MenuItem)菜品继承统一接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class MenuItem extends MenuComponent {
String name ;
String description ;
boolean vegetarian ;
double price ;
public MenuItem ( String name , String description , boolean vegetarian , double price ) {
this . name = name ;
this . description = description ;
this . vegetarian = vegetarian ;
this . price = price ;
}
public String getName () {
return name ;
}
public String getDescription () {
return description ;
}
public boolean isVegetarian () {
return vegetarian ;
}
public double getPrice () {
return price ;
}
public Iterator createIterator () {
return new NullIterator ();
}
public void print () {
System . out . println ( " " + getName ());
if ( isVegetarian ()) {
System . out . print ( "(v)" );
}
System . out . println ( ", " + getPrice ());
System . out . println ( " -- " + getDescription ());
}
}
这样,菜单和菜品都会被统一对待。尽管他们由于继承,会得到一些不相干的方法,但是这个已经无关紧要。下面是结构图:
总而言之,当与偶数个对象的组合,他们彼此之间有“整体/部分”的关系,并且你想用一致的方式对待这些对象时,就需要用到组合模式。这样客户就不再需要操心面对的是组合对象还是叶节点对象。不必写一大堆if语句来保证他们对正确的对象调用了正确的方法。通常,他们只需要对整个结构调用一个方法并执行操作就可以了。