《HeadFirst设计模式》第九章-2组合模式

本文探讨如何使用设计模式中的组合模式优化餐厅菜单结构,解决菜单和子菜单的管理问题,实现统一处理菜单和菜单项,提高代码的灵活性和可扩展性。通过创建菜单组件接口、菜单项和组合菜单的实现,以及女招待员的角色,展示了组合模式在菜单遍历和迭代器模式中的应用,强调了透明性和单一责任原则的权衡。
摘要由CSDN通过智能技术生成

1.声明

设计模式中的设计思想、图片和部分代码参考自《Head First设计模式》作者Eric Freeman & Elisabeth Freeman & Kathy Siezza & Bert Bates。

在这里我只是对这本书进行学习阅读,并向大家分享一些心得体会。

2.优化

首先回顾一下上篇"迭代器模式"中女招待员的代码:

//女招待员
public class Waitress {
	Menu pancakeHouseMenu;
	Menu dinerMenu;
	Menu cafeMenu; //咖啡菜单
 
	public Waitress(Menu pancakeHouseMenu, Menu dinerMenu, Menu cafeMenu) {
		this.pancakeHouseMenu = pancakeHouseMenu;
		this.dinerMenu = dinerMenu;
		this.cafeMenu = cafeMenu;
	}
 
	public void printMenu() {
		Iterator<MenuItem> pancakeIterator = pancakeHouseMenu.createIterator();
		Iterator<MenuItem> dinerIterator = dinerMenu.createIterator();
		Iterator<MenuItem> cafeIterator = cafeMenu.createIterator();

		System.out.println("MENU\n----\nBREAKFAST");
		printMenu(pancakeIterator);
		System.out.println("\nLUNCH");
		printMenu(dinerIterator);
		System.out.println("\nDINNER");
		printMenu(cafeIterator);
	}
 
	//打印菜单
	private void printMenu(Iterator<MenuItem> iterator) {
		while (iterator.hasNext()) {
			MenuItem menuItem = iterator.next();
			System.out.print(menuItem.getName() + ", ");
			System.out.print(menuItem.getPrice() + " -- ");
			System.out.println(menuItem.getDescription());
		}
	}
}

通过观察我们发现,printMenu中调用了三次createIterator方法,并且还调用了三次打印的方法,那么如果我们又增加一些餐厅,就得再修改这两处代码,这违反了开放-关闭原则

于是我们有了改进后的女招待员代码:

改进后的女招待员代码:

//女招待员
public class Waitress {
	ArrayList<Menu> menus;
     
  
	public Waitress(ArrayList<Menu> menus) {
		this.menus = menus;
	}
   
	public void printMenu() {
		Iterator<?> menuIterator = menus.iterator();
		while(menuIterator.hasNext()) {
			Menu menu = (Menu)menuIterator.next();
			printMenu(menu.createIterator());
		}
	}
   
	void printMenu(Iterator<?> iterator) {
		while (iterator.hasNext()) {
			MenuItem menuItem = (MenuItem)iterator.next();
			System.out.print(menuItem.getName() + ", ");
			System.out.print(menuItem.getPrice() + " -- ");
			System.out.println(menuItem.getDescription());
		}
	}
}

正当我们觉得这很“安全“的时候,餐厅有提出了新的需求,他们希望创建一份甜点菜单,在午餐后(餐厅)提供。也就是说还需要菜单中的菜单。

新的需求:

显然对于这个新的需求,现有的代码肯定是行不通的,那么我们只能不幸的告诉厨师们,重构代码已经必不可免。、

项目达到一定级别,就必须重构代码,使他能够成长。如果不这么做,就会导致僵化并且没有弹性的代码,完全看不到萌发新生命的希望。

对于这份需求,需要我们做什么?

  • 我们需要某种树状结构,可以容纳菜单,子菜单和菜单项。
  • 我们需要确定能够在每个菜单的各个项之间游走,而且至少要像现在用迭代器一样方便。
  • 我们也需要能够更有弹性地在菜单项之间游走。比方说,可能只需要遍历甜点菜单,或者可以遍历整个菜单(包括甜点菜单)。

3.组合模式

3.1定义组合模式

组合模式

允许你将对象组合成树状结构来表现"整体/部分"层次结构。组合能让客户以一致地方式处理个别对象以及对象组合。

菜单视角运用组合模式:

我们已菜单为例思考这一切:这个模式能够创建一个树状结构,在同一个结构中处理潜逃菜单和菜单项组。通过将菜单和项放在相同地的结构中,我们创建了一个"整体/部分"层次结构,即由菜单和菜单项组成的对象树。但是可以将它视为一个整体,像是一个丰富的大菜单。

一旦有了丰富的大菜单,我们就可以使用这个模式来"统一处理个别对象和组合对象"。这意味着,如果我们有了一个树形结构的菜单、子菜单和可能还带有菜单项的子菜单,那么任何一个菜单都是一种"组合”。因为它既可以包含其他菜单,也可以包含菜单项。个别对象只是菜单项——并未持有其他对象。使用一个遵循组合模式的设计,让我们可以写出简单的代码,就能够对整个菜单结构应用相同的操作(例如打印)。

组合模式让我们能用树形方式创建对象的结构,树里面包含了组合以及个别对象。

使用组合结构,我们能把相同的操作应用在组合和个别对象上。换句话说,在大多数情况下,我们可以忽略对象组合和个别对象之间的差异。

3.2组合模式类图

问答:

1.组合的思想在树状结构中是如何体现的?

答:组合包含组件。组件中又有两种表现形式:组合和叶子节点。这用到了递归思想。组合持有一群孩子,这些孩子可以是别的组合或者叶节点元素。当我们用这种方式组织数据的时候,最终会得到树状结构(准确的说是自上而下的树状结构),根部是一个组合,而组合的分支逐渐向下延伸,直到叶子节点为止。

3.3利用组合模式设计菜单

一开始我们需要创建一个组件接口来作为菜单和菜单项的共同接口,让我们能够用统一的做法来处理菜单和菜单项。换句话说,我们可以针对菜单和菜单项调用相同的方法。

以对象村餐厅的角度设计类图:

3.4代码设计

3.4.1实现菜单组件

菜单组件的角色时为叶节点和组合节点提供一个共同的接口,并且菜单组件中所有的方法都有默认实现,这样,如果菜单项(叶子节点)或者菜单(组合)不想实现某个方法的时候(例如叶子节点不想实现getChild()方法),就可以不是先这些方法。

所有的组件都必须实现MenuComponent接口,然而,叶子节点和组合节点的角色不同,所以有些方法可能并不适合某种节点,面对这种情况,我们需要将不需要的方法抛出异常。因为有些方法只对菜单项有意义,有的则支队菜单有意义,默认实现时抛出UnsupportedOperationException异常。这样,如果菜单项或菜单不支持某个操作,他们就不做任何事,直接继承默认实现就可以了。

菜单组件:

//菜单组件,方法默认抛出异常
public abstract class MenuComponent {
   
	public void a
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

琴瘦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值