在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板.它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行.这种类型的设计模式属于行为型模式
使用模板方法要思考一下几点:
- 在父类中一次性实现算法中不可变部分,并将可变的行为留给子类来实现
- 子类公用的行为应该被提出来放到公共类中,以避免代码重发
- 要考虑一些特殊情况特殊处理.这里采用子类的扩展来实现。可以定义一些在特定点调用“钩子”操作方法。子类可以通过对钩子操作的实现从而在这些点上扩展功能。钩子操作默认情况是不对整个模板造成影响的。子类重载后,才为模板算法提供附加的操作。比如项目中的某些数据请求方法要加请求头token之类,但其它大部分网络请求却不需要,就可以采用"钩子"操作来实现。
场景
比如,小时候老师布置作业都是说多少页第几题,抄写题目并答题,这样我们就需要先抄题目再做答案,有时候没看清或没听清把题目抄错了,这就意味着,即使我们做的再好,也不会正确
后来老师每次布置作业都是发试卷,这样的好处就是大家的题目是一样的,大家做答案就行了,这就是模板模式,题目一样,不同的是答案不一样
模式结构和说明
AbstractClass:抽象类.用来定义算法骨架和原语操作,具体的子类通过重定义这些原语操作来实现一个算法的各个步骤.在这个类里面,还可以提供算法中通用的实现
ConcreteClass:具体实现类.用来实现算法骨架中的某些步骤,完成跟特定子类相关的功能
示例代码
- 定义抽象类(模板)
@interface GTPaper : NSObject
/**/
@property (nonatomic,strong) NSString *answer;
//题目1
- (void)subject1;
//题目2
- (void)subject2;
@end
- (void)subject1
{
NSLog(@"中国是世界%@ 人口大国\n A 第一 B 第二 C 第三 D 第四 ",self.answer);
}
- (void)subject2
{
NSLog(@"刀削面是%@省的特色\n A 山东 B 山西 C 陕西 D 兰州",self.answer);
}
- 定义子类,完成特有功能
@implementation GTPagerA
- (void)subject1
{
self.answer = @"A 第一";
[super subject1];
}
- (void)subject2
{
self.answer = @"A 山东";
[super subject2];
}
@end
@implementation GTPagerB
- (void)subject1
{
self.answer = @"A 第一";
[super subject1];
}
- (void)subject2
{
self.answer = @"B 山西";
[super subject2];
}
@end
可以看出上面的子类只有答案是不同的
3. 客户端实践
GTPaper *pageA = [GTPagerA new];
[pageA subject1];
[pageA subject2];
GTPaper *pageB = [GTPagerB new];
[pageB subject1];
[pageB subject2];
模式讲解
1. 模式的功能
模板方法的功能在于固定算法骨架,而让具体算法实现可扩展。
模板方法还额外提供了一个好处,就是可以控制子类的扩展.因为在父类里面定义好了算法的步骤,只是在某几个固定的点才会调用到被子类实现的方法,因此也就只允许在这几个点来扩展功能,这些个可以被子类覆盖以扩展功能的方法通常被称为"钩子"方法
2. 变与不变
程序设计的一个很重要的思考点就是“变与不变”,也就是分析程序中哪些功能是可变的,哪些功能是不变的,然后把不变的部分抽象出来,进行公共的实现,把变化的部分分离出去,用接口来封装隔离,或者是用抽象类来约束子类行为。
模板方法模式很好的体现了这一点。模板类实现的就是不变的方法和算法的骨架,而需要变化的地方,都通过抽象方法,把具体实现延迟到子类去了,而且还通过父类的定义来约束了子类的行为,从而使系统能有更好的复用性和扩展性。
讲到这大家再想想除了继承外,还有别的方式可以实现模式的思想吗?
答案肯定的有,那就是回调机制,先实现公共部分,然后在回调里实现不同的处理方式,从某一个方面而言,回调机制要比继承好,因为oc的继承是单继承的,对子类而言,使用继承方式,就不能继承其他对象了,并且回调机制是通过委托方式来实现功能的,耦合成都要比继承低,大家可以试试,这里不做过多代码实现