前言
制作三明治时,无论制作的是哪种三明治,都有一些通用步骤。每种三明治可以在面包、肉和调味料的选用上有所不同,也可能增加了其它步骤,但是制作中仍然基本上按照通用步骤,比如无论是什么三明治,总是需要面包才能完成制作过程。制作三明治的一组通用步骤(食谱)是一个模板方法(template method)。说它是模板方法是因为它在完成工序的步骤中仍然缺少某些特定的片段。
什么是模板方法模式
模板方法定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。其基本思想是在抽象类中的一个方法中定义“标准算法”。在这个方法中调用的基本操作应由子类重载予以实现。这个方法被称为模板。
AbstractClass
不完整的定义了一些方法和算法,留出一些操作未作定义。 调用 templateMethod
时,方法中未定义的空白部分,由 ConcreteClass
重载 primitiveOperation
来填补。
什么时候使用模板方法
- 需要一次性实现算法的不变部分,并将可变的行为留给子类来实现。
- 子类的共同行为应该被提取出来放到公共类中,以避免代码重复。现有代码的差别应该被分离为新的操作。然后用一个调用这些新操作的模板方法来替换这些不同的代码
- 需要控制子类的扩展。可以定义一个在特定点调用“钩子”操作的模板方法。子类可以通过对钩子操作的实现在这些点扩展功能。
模板方法的优缺点
模板方法的优点
- 封装不变部分,扩展可变部分。
- 提取公共代码,便于维护。
- 行为由父类控制,子类实现。
模板方法的缺点
每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
模板方法的实现
制作三明治基本步骤应该像下面这样:
- 准备面包
- 把面包放在盘子上
- 往面包上加肉
- 加调味料
- 上餐
定义一个 AnySandwich
的父类 定义了 make
的模板方法,用于执行这些通用步骤方法,由于部分步骤需要子类实现,要保证模板方法正常工作,因此当其调用父类方法时,需要抛出异常。并定义一个额外步骤 extraStep
钩子方法,用于子类可选执行额外的步骤。
@interface AnySandwich : NSObject
{
}
- (void) make;
// Steps to make a sandwich
- (void) prepareBread;
- (void) putBreadOnPlate;
- (void) addMeat;
- (void) addCondiments;
- (void) extraStep;
- (void) serve;
@end
@implementation AnySandwich
- (void) make
{
[self prepareBread];
[self putBreadOnPlate];
[self addMeat];
[self addCondiments];
[self extraStep];
[self serve];
}
- (void) putBreadOnPlate
{
// We need first to put bread on a plate for any sandwich.
}
- (void) serve
{
// Any sandwich will be served eventually.
}
#pragma mark -
#pragma Details will be handled by subclasses
- (void) prepareBread
{
[NSException raise:NSInternalInconsistencyException
format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
}
- (void) addMeat
{
[NSException raise:NSInternalInconsistencyException
format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
}
- (void) addCondiments
{
[NSException raise:NSInternalInconsistencyException
format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
}
- (void) extraStep{}
@end
定义一个子类继承 AnySandwich
,完成汉堡包的制作。
@interface Hamburger : AnySandwich
{
}
- (void) prepareBread;
- (void) addMeat;
- (void) addCondiments;
//- (void) extraStep;
// Hamburger specific methods
- (void) getBurgerBun;
- (void) addKetchup;
- (void) addMustard;
- (void) addBeefPatty;
- (void) addCheese;
- (void) addPickles;
@end
Cocoa 中的模板方法
模板方法模式是一个基础 Cocoa 的设计,以及一般的面向对象框架的设计。Cocoa 中的模式允许程序的自定义组件将自己连接到算法中,但框架组件决定何时以及如何需要它们。Cocoa 类的编程接口通常包括旨在被子类覆盖的方法。在运行时,框架在其正在执行的任务的某些点调用这些所谓的通用方法。通用方法为自定义代码提供了一种结构,以将特定于程序的行为和数据贡献给正在由框架类执行和协调的任务。
总结
模板方法模式是代码复用的一项基本技术。模板方法在框架类设计中非常重要,因为它是抽出共同行为放入框架类中的手段。