日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

《Head First设计模式》第九章(2)组合模式

發布時間:2023/12/13 asp.net 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《Head First设计模式》第九章(2)组合模式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

組合模式

? 基于前一篇迭代模式的案例進行需求更新,餐廳的菜單管理系統需要有煎餅屋菜單和披薩菜單。現在希望在披薩菜單中能夠加上一份餐后甜點的子菜單。
在迭代模式中,披薩菜單是用數組維護的,我們需要讓披薩菜單持有一份子菜單,但是不能真的把他賦值給菜單項數組,因為類型不同,所以不能這么做。
所以,需要重新實現煎餅屋菜單和披薩菜單了。事實是,我們已經到達了一個復雜級別,如果現在不重新設計,就無法容納未來增加的菜單或子菜單的需求。我們需要一下改變:

  • 需要某種樹形結構,可以容納菜單、子菜單和菜單項;
  • 需要確定能夠在每個菜單的各個項之間游走,而且至少像用迭代器一樣方便;
  • 需要能夠更有彈性地在菜單項之間游走。比方說,可能只需要遍歷甜點菜單,或者可以便利整個菜單;

我們首先想到的是采用樹形結構:

? 我們要使用組合模式來解決這個問題,但并沒有放棄迭代器模式,它仍然是解決方案中的一部分,然而管理菜單的問題已經到了一個迭代器無法解決的新維度。所以,我們將倒退幾步,使用組合模式來解決。

??組合模式讓我們能用樹形方式創建對象的結構,樹里面包含了組合以及個別的對象。使用組合結構,我們能把相同的操作應用在組合的個別對象上,換句話說,在大多數情況下,我們可以忽略對象組合和個別對象之間的差別。

定義

組合模式允許將對象組合成屬性結構來表現“整體/部分”層次結構,組合能讓客戶以一致的方式處理個別對象以及對象組合。

組合模式能創建一個樹形結構

??

我們要如何將組合模式利用在菜單上呢?一開始,我們需要創建一個組件接口來作為菜單和菜單項的共同接口,讓我們能夠用同意的做法來處理菜單和菜單項。來看看設計的類圖:

? 菜單組件MenuComponent提供了一個接口,讓菜單項和菜單共同使用。因為我們希望能夠為這些方法提供默認的實現,所以我們在這里可以把MenuComponent接口換成一個抽象類。在這個類中,有顯示菜單信息的方法getName()等,還有操縱組件的方法add(),remove(),getChild()等。

? 菜單項MenuItem覆蓋了顯示菜單信息的方法,而菜單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

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

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

public abstract class MenuComponent {

?

????// add,remove,getchild

????// 把組合方法組織在一起,即新增、刪除和取得菜單組件

?

????public void add(MenuComponent component) {

????????throw new UnsupportedOperationException();

????}

?

????public void remove(MenuComponent component) {

????????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 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 void print() {

????????System.out.println(" " + getName());

????????if (isVegetarian()) {

????????????System.out.println("(V)");

????????}

????????System.out.println(", " + getPrice());

????????System.out.println(" -- " + getDescription());

????}

}

?

public class Menu?extends MenuComponent {

????ArrayList<MenuComponent> menuComponents =?new ArrayList<MenuComponent>();

????String name;

????String description;

?

????public Menu(String name, String description) {

????????this.name = name;

????????this.description = description;

????}

?

????public void add(MenuComponent menuComponent) {

????????menuComponents.add(menuComponent);

????}

?

????public void remove(MenuComponent menuComponent) {

????????menuComponents.remove(menuComponent);

????}

?

????public MenuComponent getChild(int i) {

????????return menuComponents.get(i);

????}

?

????public String getName() {

????????return name;

????}

?

????public String getDescription() {

????????return description;

????}

?

????public void print() {

????????System.out.println("\n" + getName());

????????System.out.println(", " + getDescription());

????????System.out.println("----------------------");

?

????????Iterator<MenuComponent> iterator = menuComponents.iterator();

????????while(iterator.hasNext()) {

????????????MenuComponent menuComponent = iterator.next();

????????????menuComponent.print();

????????}

????}

}

?

?

public class Waitress {

????MenuComponent allMenus;

?

????public Waitress(MenuComponent allMenus) {

????????this.allMenus = allMenus;

????}

?

????public void printMenu() {

????????allMenus.print();

????}

}

?

?

public class Client {

?

????public static void main(String[] args) {

????????// 創建菜單對象

????????MenuComponent pancakeHouseMenu =?new Menu("煎餅屋菜單",?"提供各種煎餅。");

????????MenuComponent pizzaHouseMenu =?new Menu("披薩屋菜單",?"提供各種披薩。");

????????MenuComponent cafeMenu =?new Menu("咖啡屋菜單",?"提供各種咖啡");

????????// 創建一個頂層的菜單

????????MenuComponent allMenus =?new Menu("All Menus",?"All menus combined");

????????// 把所有菜單都添加到頂層菜單

????????allMenus.add(pancakeHouseMenu);

????????allMenus.add(pizzaHouseMenu);

????????allMenus.add(cafeMenu);

????????// 在這里加入菜單項

????????pancakeHouseMenu.add(new MenuItem("蘋果煎餅",?"香甜蘋果煎餅",?true,?5.99));

????????pizzaHouseMenu.add(new MenuItem("至尊披薩",?"意大利至尊咖啡",?false,?12.89));

????????cafeMenu.add(new MenuItem("美式咖啡",?"香濃美式咖啡",?true,?3.89));

?

????????Waitress waitress =?new Waitress(allMenus);

????????waitress.printMenu();

????}

?

}

? 組合模式以單一責任設計原則換取透明性。通過讓組件的接口同時包含一些管理子節點和葉節點的操作,客戶就可以將組合和葉節點一視同仁。也就是說,一個元素究竟是組合還是葉節點,對客戶是透明的。

? 現在,我們在MenuComponent類中同時具有兩種類型的操作。因為客戶有機會對一個元素做一些不恰當或是沒有意義的操作,所以我們失去了一些安全性。

擴展:組合迭代器

我們現在再擴展一下,這種組合菜單如何設計迭代器呢?細心的朋友應該觀察到,我們剛才使用的迭代都是遞歸調用的菜單項和菜單內部迭代的方式?,F在我們想設計一個外部迭代的方式怎么辦?譬如出現一個新需求:服務員需要打印出蔬菜性質的所有食品菜單。首先,我們給MenuComponent加上判斷蔬菜類食品的方法,然后在菜單項中進行重寫:

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 abstract class MenuComponent {

?

????…………

????/**

?????* 判斷是否為蔬菜類食品

?????*/

????public boolean isVegetarian() {

????????throw new UnsupportedOperationException();

????}

}

/**

?* 菜單項

?*/

public class MenuItem?extends MenuComponent{

????String name;

????double price;

????/**蔬菜類食品標志*/

????boolean vegetarian;

?

????…………

?

????public boolean isVegetarian() {

????????return vegetarian;

????}

?

????public void setVegetarian(boolean vegetarian) {

????????this.vegetarian = vegetarian;

????}

?

}

這個CmpositeIterator是一個不可小覷的迭代器,它的工作是遍歷組件內的菜單項,而且確保所有的子菜單(以及子子菜單……)都被包括進來。

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

//跟所有的迭代器一樣,我們實現Iterator接口。

class CompositeIterator?implements Iterator {

????Stack stack =?new Stack();

????/**

?????*將我們要遍歷的頂層組合的迭代器傳入,我們把它拋進一個堆棧數據結構中

?????*/

????public CompositeIterator(Iterator iterator) {

????????stack.push(iterator);

????}

?

????@Override

????public boolean hasNext() {

????????//想要知道是否還有下一個元素,我們檢查堆棧是否被清空,如果已經空了,就表示沒有下一個元素了

????????if (stack.empty()) {

????????????return false;

????????}?else {

????????????/**

?????????????*否則我們就從堆棧的頂層中取出迭代器,看看是否還有下一個元素,

?????????????*如果它沒有元素,我們將它彈出堆棧,然后遞歸調用hasNext()。

?????????????*/

????????????Iterator iterator = (Iterator) stack.peek();

????????????if (!iterator.hasNext()) {

????????????????stack.pop();

????????????????return hasNext();

????????????}?else {

????????????????//否則,便是還有下一個元素

????????????????return true;

????????????}

????????}

????}

?

????@Override

????public Object next() {

????????//好了,當客戶想要取得下一個元素時候,我們先調用hasNext()來確定時候還有下一個。

????????if (hasNext()) {

????????????//如果還有下一個元素,我們就從堆棧中取出目前的迭代器,然后取得它的下一個元素

????????????Iterator iterator = (Iterator) stack.peek();

????????????MenuComponent component = (MenuComponent) iterator.next();

????????????/**

?????????????*如果元素是一個菜單,我們有了另一個需要被包含進遍歷中的組合,

?????????????*所以我們將它丟進對戰中,不管是不是菜單,我們都返回該組件。

?????????????*/

????????????if (component?instanceof Menu) {

????????????????stack.push(component.createIterator());

????????????}

????????????return component;

????????}?else {

????????????return null;

????????}

????}

?

????@Override

????public void remove() {

????????throw? new UnsupportedOperationException();

????}

}

在我們寫MenuComponent類的print方法的時候,我們利用了一個迭代器遍歷組件內的每個項,如果遇到的是菜單,我們就會遞歸地電泳print方法處理它,換句話說,MenuComponent是在“內部”自行處理遍歷。
但是在上頁的代碼中,我們實現的是一個“外部”的迭代器,所以有許多需要追蹤的事情。外部迭代器必須維護它在遍歷中的位置,以便外部可和可以通過hasNext和next來驅動遍歷。在這個例子中,我們的代碼也必須維護組合遞歸結構的位置,這也就是為什么當我們在組合層次結構中上上下下時,使用堆棧來維護我們的位置。

空迭代器

菜單項沒什么可以遍歷的,那么我們要如何實現菜單項的createIterator()方法呢。
1:返回null。我們可以讓createIterator()方法返回null,但是如果這么做,我們的客戶代碼就需要條件語句來判斷返回值是否為null;
2:返回一個迭代器,而這個迭代器的hasNext()永遠返回false。這個是更好的方案,客戶不用再擔心返回值是否為null。我們等于創建了一個迭代器,其作用是“沒作用”。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

class NullIterator?implements Iterator{

?

????@Override

????public boolean hasNext() {

????????return false;

????}

?

????@Override

????public Object next() {

????????return null;

????}

?

????@Override

????public void remove() {

????????throw? new UnsupportedOperationException();

????}

}

? 以上便是組合模式的一些內容。

總結

以上是生活随笔為你收集整理的《Head First设计模式》第九章(2)组合模式的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。