《Head first设计模式》学习笔记 – 迭代器模式

代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

爆炸性新闻:对象村餐厅和对象村煎饼屋合并了!
真是个好消息!现在我们可以在同一个地方,享用煎饼屋美味的煎饼早餐,和好吃的餐厅午餐了。但是,好像有一点小麻烦:
新的餐厅想用煎饼屋菜单当作早餐的菜单,使用餐厅的菜单当做午餐的菜单,大家都同意了这样实现菜单项。但是大家无法同意菜单的实现。煎饼屋使用ArrayList记录他的菜单项,而餐厅使用的是数组。他们两个都不愿意改变他们的实现,毕竟有太多代码依赖于它们了。

检查菜单项

让我们先检查每份菜单上的项目和实现。


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

public class MenuItem {

// 名称

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 double getPrice() {

return price;

}

public boolean isVegetarian() {

return vegetarian;

}

}

两个餐厅的菜单实现

我们先来看看两个餐厅的菜单实现


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

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

// 这是煎饼屋的菜单实现

public class PancakeHouseMenu {

// 煎饼屋使用一个ArrayList存储他的菜单项

ArrayList menuItems;

public PancakeHouseMenu() {

menuItems = new ArrayList();

// 在菜单的构造器中,每一个菜单项都会被加入到ArrayList中

// 每个菜单项都有一个名称、一个描述、是否为素食、还有价格

addItem("K&B‘s Pancake Breakfast",

"Pancakes with scrambled eggs, and toast",

true,

2.99);

addItem("Regular Pancake Breakfast",

"Pancakes with fried eggs, sausage",

false,

2.99);

addItem("Blueberry Pancakes",

"Pancakes made with fresh blueberries",

true,

3.49);

addItem("Waffles",

"Waffles, with your choice of blueberries or strawberries",

true,

3.59);

}

// 要加入一个菜单项,煎饼屋的做法是创建一个新的菜单项对象,

// 传入每一个变量,然后将它加入ArrayList中

public void addItem(String name, String description,

boolean vegetarian, double price) {

MenuItem menuItem = new MenuItem(name, description, vegetarian, price);

menuItems.add(menuItem);

}

// 这个方法返回菜单项列表

public ArrayList getMenuItems() {

return menuItems;

}

// 这里还有菜单的其他方法,这些方法都依赖于这个ArrayList,所以煎饼屋不希望重写全部的代码!

// ...

}

// 餐厅的菜单实现

public class DinnerMenu {

// 餐厅采用使用的是数组,所以可以控制菜单的长度,

// 并且在取出菜单项时,不需要转型

static final int MAX_ITEMS = 6;

int numberOfItems = 0;

MenuItem[] menuItems;

public DinnerMenu() {

menuItems = new MenuItem[MAX_ITEMS];

// 和煎饼屋一样,餐厅使用addItem()辅助方法在构造器中创建菜单项的

addItem("Vegetarian BLT",

"(Fakin‘) Bacon with lettuce & tomato on whole wheat",

true,

2.99);

addItem("BLT",

"Bacon with lettuce & tomato on whole wheat",

false,

2.99);

addItem("Soup of the day",

"Soup of the day, with a side of potato salad",

false,

3.29);

addItem("Hotdog",

"A hot dog, with saurkraut, relish, onions, topped with cheese",

false,

3.05);

}

public void addItem(String name, String description,

boolean vegetarian, double price) {

// 餐厅坚持让菜单保持在一定的长度之内

MenuItem menuItem = new MenuItem(name, description, vegetarian, price);

if (numberOfItems >= MAX_ITEMS) {

System.err.println("Sorry, menu is full! Can‘t add item to menu");

} else {

menuItems[numberOfItems] = menuItem;

numberOfItems = numberOfItems + 1;

}

}

// getMenuItems()返回一个菜单项的数组

public MenuItem[] getMenuItems() {

return menuItems;

}

// 正如煎饼屋那样,这里还有很多其他的菜单代码依赖于这个数组

// ...

}

两种不同的菜单表现方式,会带来什么问题?

想了解为什么有两种不同的菜单表现方式会让事情变得复杂化,让我们试着实现一个同时使用这两个菜单的客户代码。假设你已经被两个餐厅合租的新公司雇用,你的工作是创建一个Java版本的女招待,她能应对顾客的需要打印定制的菜单,甚至告诉你是否某个菜单项是素食的,而无需询问厨师。跟我们来看看这份关于女招待的规格,然后看看如何实现她。

Java版本的女招待规格:

printMenu(): 打印出菜单上的每一项
printBreakfastMenu(): 只打印早餐项
printLunchMenu(): 只打印午餐项
printVegetarianMenu(): 打印所有的素食菜单项
isItemVegetarian(name): 指定项的名称,如果该项是素食,返回true,否则返回false

我们先从实现printMenu()方法开始:
1.打印每份菜单上的所有项,必须调用PancakeHouseMenu和DinnerMenu的getMenuItems()方法,来取得它们各自的菜单项。请注意,两者的返回类型是不一样的。


1

2

3

4

5

6

7

// getMenuItems()方法看起来是一样的,但是调用所返回的结果却是不一样的类型。

// 早餐项是在一个ArrayList中,午餐项则是在一个数组中

PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();

ArrayList breakfastItems = pancakeHouseMenu.getMenuItems();

DinnerMenu dinnerMenu = new DinnerMenu();

MenuItem[] lunchItems = dinnerMenu.getMenuItems();

2.现在,想要打印PancakeHouseMenu的项,我们用循环将早餐ArrayList内的项一一列出来。想要打印DinnerMenu的项目,我们用循环将数组内的项一一列出来。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

// 现在,我们必须实现两个不同的循环,个别处理这两个不同的菜单

for (int i = 0; i < breakfastItems.size(); i++) {

MenuItem menuItem = (MenuItem) breakfastItems.get(i);

System.out.print(menuItem.getName() + " ");

System.out.print(menuItem.getPrice() + " ");

System.out.println(menuItem.getDescription() + " ");

}

for (int i = 0; i < lunchItems.length; i++) {

MenuItem menuItem = lunchItems[i];

System.out.print(menuItem.getName() + " ");

System.out.print(menuItem.getPrice() + " ");

System.out.println(menuItem.getDescription() + " ");

}

3.实现女招待中的其他方法,做法也都和这里的方法相类似。我们总是需要处理两个菜单,并且用两个循环遍历这些项。如果还有第三家餐厅以不同的实现出现,我们就需要有三个循环。

下一步呢?

这两个餐厅让我们很为难,他们都不想改变自身的实现,因为意味着要重写许多代码。但是如果他们其中一人不肯退让,我们就很难办了,我们所写出来的女招待程序将难以维护、难以扩展。
如果我们能够找到一个方法,让他们的菜单实现一个相同的接口,该有多好!这样一来,我们就可以最小化女招待代码中的具体引用,同时还有希望摆脱遍历这两个菜单所需的多个循环。
听起来很棒!但要怎么做呢?
如果你从本书中学到了一件事情,那就是封装变化的部分。很明显,在这里发生变化的是:由不同的集合(collection)类型所造成的遍历。但是,这能够被封装吗?让我们来看看这个想法:
1.要遍历早餐项,我们需要使用ArrayList的size()和get()方法:


1

2

3

for (int i = 0; i < breakfastItems.size(); i++) {

MenuItem menuItem = (MenuItem) breakfastItems.get(i);

}

2.要遍历午餐项,我们需要使用数组的length字段和中括号:


1

2

3

for (int i = 0; i < lunchItems.length; i++) {

MenuItem menuItem = lunchItems[i];

}

3.现在我们创建一个对象,把它称为迭代器(Iterator),利用它来封装“遍历集合内的每个对象的过程”。先让我们在ArrayList上试试:


1

2

3

4

5

6

7

// 我们从breakfastMenu中取得一个菜单项迭代器

Iterator iterator = breakfastMenu.createIterator();

// 当还有其他项时

while (iterator.hasNext()) {

// 取得下一项

MenuItem menuItem = (MenuItem) iterator.next();

}

4.将它也在数组上试试:


1

2

3

4

5

6

// 这里的情况也是一样的:客户只需要调用hasNext()和next()即可,

// 而迭代器会暗中使用数组的下标

Iterator iterator = lunchMenu.createIterator();

while (iterator.hasNext()) {

MenuItem menuItem = (MenuItem) iterator.next();

}

会见迭代器模式

看起来我们对遍历的封装已经奏效了;你大概也已经猜到,这正是一个设计模式,称为迭代器模式。
关于迭代器模式,你所需要知道的第一件事情,就是它依赖于一个名为迭代器的接口。


1

2

3

4

5

6

public interface Iterator {

// hasNext()方法返回一个布尔值,让我们知道是否还有更多的元素

boolean hasNext();

// next()方法返回下一个元素

Object next();

}

现在,一旦我们有了这个接口,就可以为各种对象集合实现迭代器:数组、列表、散列表……
让我们继续实现这个迭代器,并将它挂钩到DinnerMenu中,看它是如何工作的。

用迭代器改写餐厅菜单

现在我们需要实现一个具体的迭代器,为餐厅菜单服务:


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 DinnerMenuIterator implements Iterator {

MenuItem[] items;

// position记录当前数组遍历的位置

int position = 0;

// 构造器需要被传入一个菜单项的数组当做参数

public DinnerMenuIterator(MenuItem[] items) {

this.items = items;

}

// next()方法返回数组内的下一项,并递增其位置

public Object next() {

MenuItem menuItem = items[position];

position = position + 1;

return menuItem;

}

// hasNext()方法会检查我们是否已经取得数组内所有的元素。

// 如果还有元素待遍历,则返回true

public boolean hasNext() {

if (position >= items.length || items[position] == null) {

return false;

} else {

return true;

}

}

}

好了,我们已经有了迭代器。现在就利用它来改写餐厅菜单:我们只需要加入一个方法创建一个DinnerMenuIterator,并将它返回给客户:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public class DinnerMenu {

static final int MAX_ITEMS = 6;

int numberOfItems = 0;

MenuItem[] menuItems;

// ...

// 我们不再需要getMenuItems()方法,事实上,我们根本不想要这个方法,

// 因为它会暴露我们内部的实现。

// 这是createIterator()方法,用来从菜单项数组创建一个DinnerMenuIterator,

// 并将它返回给客户

public Iterator createIterator() {

return new DinnerMenuIterator(menuItems);

}

// ...

}

现在将迭代器代码整合进女招待中。


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

public class Waitress {

PancakeHouseMenu pancakeHouseMenu;

DinnerMenu dinnerMenu;

// 在构造器中,女招待照顾两个菜单

public Waitress(PancakeHouseMenu pancakeHouseMenu, DinnerMenu dinnerMenu) {

this.pancakeHouseMenu = pancakeHouseMenu;

this.dinnerMenu = dinnerMenu;

}

public void printMenu() {

// 这个printMenu()方法为每一个菜单各自创建一个迭代器

Iterator pancakeIterator = pancakeHouseMenu.createIterator();

Iterator dinnerIterator = dinnerMenu.createIterator();

// 然后调用重载的printMenu(),将迭代器传入

printMenu(pancakeIterator);

printMenu(dinnerIterator);

}

// 这个重载的printMenu()方法,使用迭代器来遍历菜单项并打印出来

private void printMenu(Iterator iterator) {

while (iterator.hasNext()) {

MenuItem menuItem = (MenuItem) iterator.next();

System.out.println(menuItem.getName() + " " +

menuItem.getPrice() + " " + menuItem.getDescription());

}

}

}

到目前为止,我们做了些什么?

首先,我们让对象村的厨师们非常快乐。他们可以保持他们自己的实现又可以摆平差别。只要我们给他们这两个迭代器(PancakeHouseMenuIterator和DinnerMenuIterator),他们只需要加入一个createIterator()方法,一切就大功告成了。
这个过程中,我们也帮了我们自己。女招待将会更容易维护和扩展。让我们来彻底检查一下到底我们做了哪些事,以及后果如何:

难以维护的女招待实现 由迭代器支持的新女招待
菜单封装得不好,餐厅使用的是ArrayList,而煎饼屋使用的是数组。 菜单的实现已经被封装起来了。女招待不知道菜单是如何存储菜单项集合的。
需要两个循环来遍历菜单项。 只要实现迭代器,我们只需要一个循环,就可以多态地处理任何项的集合。
女招待捆绑于具体类(MenuItem[]和ArrayList)。 女招待现在只使用一个接口(迭代器)。
女招待捆绑于两个不同的具体菜单类,尽管这两个类的接口大致上是一样的。 现在的菜单接口完全一样。但是,我们还是没有一个共同的接口,也就是说女招待仍然捆绑于两个具体的菜单类。这一点我们最好再修改一下。

做一些改良

好了,我们已经知道这两份菜单的接口完全一样,但没有为它们设计一个共同的接口。所以,接下来就要这么做,让女招待更干净一些。
Java有一个内置的Iterator接口,让我们先来看看:


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

public interface Iterator<E> {

/**

* Returns true if there is at least one more element, false otherwise.

* @see #next

*/

public boolean hasNext();

/**

* Returns the next object and advances the iterator.

*

* @return the next object.

* @throws NoSuchElementException

*             if there are no more elements.

* @see #hasNext

*/

public E next();

/**

* Removes the last object returned by {@code next} from the collection.

* This method can only be called once between each call to {@code next}.

*

* @throws UnsupportedOperationException

*             if removing is not supported by the collection being

*             iterated.

* @throws IllegalStateException

*             if {@code next} has not been called, or {@code remove} has

*             already been called after the last call to {@code next}.

*/

public void remove();

}

这个接口看起来和我们之前定义的一样,只不过多了一个附加的方法,允许我们从聚合中删除由next()方法返回的最后一项。
接下来让我们用java.util.Iterator来清理代码。
让我们先从煎饼屋菜单开始,先把它改用java.util.Iterator,这很容易,只需要删除煎饼屋菜单迭代器,然后在煎饼屋菜单的代码前面加上 import java.util.Iterator。再改变下面这一行代码就可以了:


1

2

3

public Iterator createIterator() {

return menuItems.iterator();

}

这样PancakeHouseMenu就完成了。
接着,我们处理DinnerMenu,以符合java.util.Iterator的需求。


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

public class DinnerMenuIterator implements Iterator {

MenuItem[] items;

int position = 0;

public DinnerMenuIterator(MenuItem[] items) {

this.items = items;

}

public Object next() {

MenuItem menuItem = items[position];

position = position + 1;

return menuItem;

}

public boolean hasNext() {

if (position >= items.length || items[position] == null) {

return false;

} else {

return true;

}

}

// 我们需要实现remove()方法。因为使用的是固定长度的数组,

// 所以在remove()方法被调用时,我们将后面的所有元素往前移动一个位置。

@Override

public void remove() {

if (position <= 0) {

throw new IllegalStateException("You can‘t remove

an item until you‘ve done at least one next()");

}

if (items[position - 1] != null) {

for (int i = position-1; i < (items.length - 1); i++) {

items[i] = items[i + 1];

}

items[items.length - 1] = null;

}

}

}

我们只需要给菜单一个共同的接口,然后再稍微改一下女招待。这个Menu接口相当简单:


1

2

3

public interface Menu {

public Iterator createIterator();

}

现在,我们需要让煎饼屋菜单类和餐厅菜单类都实现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

public class Waitress {

Menu pancakeHouseMenu;

Menu dinnerMenu;

// 将具体菜单类改成Menu接口

public Waitress(Menu pancakeHouseMenu, Menu dinnerMenu) {

this.pancakeHouseMenu = pancakeHouseMenu;

this.dinnerMenu = dinnerMenu;

}

// 以下的代码没有修改

public void printMenu() {

Iterator pancakeIterator = pancakeHouseMenu.createIterator();

Iterator dinnerIterator = dinnerMenu.createIterator();

printMenu(pancakeIterator);

printMenu(dinnerIterator);

}

private void printMenu(Iterator iterator) {

while (iterator.hasNext()) {

MenuItem menuItem = (MenuItem) iterator.next();

System.out.println(menuItem.getName() + " " +

menuItem.getPrice() + " " + menuItem.getDescription());

}

}

}

这为我们带来了什么好处?煎饼屋菜单和餐厅菜单的类,都实现了Menu接口,女招待可以利用接口(而不是具体类)引用每一个菜单对象。这样,通过“针对接口编程,而不针对实现编程”,我们就可以减少女招待和具体类之间的依赖。

定义迭代器模式

现在我们来看看这个模式的正式定义:

迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

迭代器模式让我们能游走于聚合内的每一个元素,而又不暴露内部的表示。把游走的任务放在迭代器上,而不是聚合上,这样简化了聚合的接口和实现,也让责任各得其所。
这很有意义:这个模式给你提供了一种方法,可以顺序访问一个聚集对象中的元素,而又不用知道内部是如何表示的。你已经在前面的两个菜单实现中看到了这一点。在设计中使用迭代器的影响是明显的:如果你有一个统一的方法访问聚合中的每一个对象,你就可以编写多态的代码和这些聚合搭配使用,如同前面的printMenu()方法一样,只要有了迭代器这个方法,根本不管菜单项究竟是由数组还是由ArrayList(或者其他能创建迭代器的东西)来保存的。
另一个对你的设计造成重要影响的,是迭代器模式把这些元素之间游走的责任交给迭代器,而不是聚合对象。这不仅让聚合的接口和实现变得更简洁,也可以让聚合更专注在它所应该专注的事情上面(也就是管理对象组合),而不必去理会遍历的事情。

单一责任

如果我们允许我们的聚合实现它们内部的集合,以及相关的操作和遍历的方法,又会如何?我们已经知道这会增加聚合中的方法个数,但又怎样呢?为什么这么做不好?
想知道为什么,首先你需要认清楚,当我们允许一个类不但要完成自己的事情(管理某种聚合),还同时要担负更多的责任(例如遍历)时,我们就给了这个类两个变化的原因。两个?没错,就是两个!如果这个集合改变的话,这个类也必须改变,如果我们遍历的方式改变的话,这个类也必须跟着改变。所以,再一次地,我们的老朋友“改变”又成了我们设计原则的中心:

设计原则:一个类应该只有一个引起变化的原因

我们知道要避免类内的改变,因为修改代码很容易造成许多潜在的错误。如果有一个类具有两个改变的原因,那么这会使得将来该类的变化几率上升,而当它真的改变时,你的设计中同时有两个方面将会受到影响。

没错,这听起来很容易,但其实做起来并不简单:区分设计中的责任,是最困难的事情之一。我们的大脑很习惯看着一大群的行为,然后将它们集中在一起,尽管他们可能属于两个或者多个不同的责任。想要成功的唯一方法,就是努力不懈地检查你的设计,随着系统的成长,随时观察有没有迹象显示某个类改变的原因超出一个。

原文地址:https://www.cnblogs.com/wwlww/p/8410171.html

时间: 2024-10-12 11:38:54

《Head first设计模式》学习笔记 – 迭代器模式的相关文章

设计模式学习笔记--迭代器模式

1 using System; 2 3 namespace Iterator 4 { 5 /// <summary> 6 /// 作者:bzyzhang 7 /// 时间:2016/5/30 20:01:24 8 /// 博客地址:http://www.cnblogs.com/bzyzhang/ 9 /// Iterator说明:迭代器抽象类 10 /// </summary> 11 public abstract class Iterator 12 { 13 public abs

Java设计模式学习记录-迭代器模式

前言 这次要介绍的是迭代器模式,也是一种行为模式.我现在觉得写博客有点应付了,前阵子一天一篇,感觉这样其实有点没理解透彻就写下来了,而且写完后自己也没有多看几遍,上次在面试的时候被问到java中的I/O的各种实现用到了什么设计模式,我愣是想半天没想出来了,人家还给提示了我也没想出来,最后还是面试官给出的答案,是装饰模式,听到答案后就恍然大悟了,前两天刚看了装饰模式,还写下了I/O操作中的各种类都是用到了装饰模式,后来想想两方面原因造成的当时没回答出来,一是面试时紧张就容易想不起来,二是对设计模式

设计模式学习笔记--外观模式

好久没写设计模式的blog了,这次重新回来填坑,先找一个最简单但是却最常用的设计模式来学习,外观模式.其实说是一个设计模式,其实我们在实际的编程中无时无刻不在用外观模式,可以说这个设计模式已经渗透到编程的各个方便,可能我们自己没感觉出来罢了. 一.外观模式的定义 先来看一下外观模式的定义: 外观模式(Facade),为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层的接口,这个接口使得这一系列子系统更加容易使用. 简单解释一下,所谓外观模式,就是在我们设计系统的时候,将若干个子系统的功

javascript设计模式学习之七——迭代器模式

一.迭代器模式定义 迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,并且不需要暴露该对象的内部表示. 在当前大部分语言中,都已经内置了迭代器模式.迭代器有内部迭代器和外部迭代器之分,一般现有语言中实现的大多是内部迭代器. 二.jquery中的each实现 //类似jquery中的each迭代器 $.each=function(obj,callback){ var value, i=0, isArray=isArrayLike(obj), length=obj.length; if(isA

学习笔记——迭代器模式Iterator

迭代器模式,使用很多,但是很少实现.常用的集合都支持迭代器. 集合中的CreateIterator()可用于创建自己的迭代器,在里面通过调用迭代器的构造函数Iterator(Aggregate)来绑定自己到迭代器中,如果不使用此方法,也可以在场景中直接new Iterator(Aggregate)来得到一个迭代器.迭代器中的接口First()等其实本质都是操作引用的Aggregate对象实现的.好处在于不用集合自己来写迭代方法,一是可以复用迭代器,减少每种集合都去造轮子,二是看起减少了集合的接口

设计模式学习笔记-工厂模式

一.概述 定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法(Factory Method)使一个类的实例化延迟到其子类: 2.模式中的角色 Product(Map):定义工厂方法所创建的对象的接口. ConcreteProduct(BaiduMap.GaodeMap):具体Product,实现Product接口. Creator(IMapFactory):声明工厂方法,该方法返回一个Product类型的对象. ConcreteCreator(BaiduMapFactory.Gaod

设计模式学习笔记-桥接模式

一.概述 将抽象部分与它的实现部分分离,使它们都可以独立地变化. 二.模式中的角色 Abstraction:定义抽象类的接口:维护一个指向Implementor类型对象的指针: RefinedAbstraction:扩充由Abstraction定义的接口: Implementor:定义具体行为,具体特征的应用接口: ConcreteImplementor:实现Implementor. 三.UML类图 四.代码实现 4.1 桥接模式的实现代码 /// <summary> /// 实现 /// &

设计模式学习笔记-建造者模式

一.概述 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示: 二.模式中的角色 Builder:为创建一个Product对象的各个部件指定抽象接口: ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件:定义并明确它所创建的表示:提供一个检索产品的接口: Director:构造一个使用Builder接口的对象: Product:表示被构造的复杂对象,ConcreteBuilder创建该产品的内部表示并定义它的装配过程:包含定义组成部件的类,

设计模式学习笔记--策略模式

定义: 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. 我们在使用一些功能时,有时有很多种实现方法,或者多种结果,但是都有同样的使用方法,即调用接口,这就是策略模式. 例子: // 设计模式Demo.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> #include <string> using namespa