1. 什么是模板设计模式
模板设计模式(Template Method Pattern)定义:
Define the skeleton of an algorithm in an operation,deferring some steps tosubclasses.Template Method lets subclasses redefine certain steps of an algorithm withoutchanging the algorithm's structure.(定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。)
2. 角色组成
模板设计模型主要由两个个角色组成,分别是:
- 抽象类(Abstract Class)-- 定义基本方法的抽象方法,实现模板方法
- 具体子类(Conctrete Class) -- 实现抽象类定义的抽象方法
方法定义:
1、基本方法:抽象类定义,子类进行实现的方法
2、模板方法:抽象类进行实现,通过特定步骤调用基本方法
3、钩子方法:抽象类定义,子类进行实现,模板方法里面通过钩子方法对一些特定步骤进行修改
UML类图如下:
3. 代码示例
议论文大家肯定非常熟悉,毕竟在应试作文类中,他属于最容易套模板进行创作的。我们以议论文”总-分-总“模板为例。写作过程可以总结为一下几个部分:标题、中心论点、论述部分、总结部分。接下来我们开始写代码:
- 创建议论文模板类,定义模板方法 writeAritcle:和钩子方法 canAddAuthorName,其他均为基本方法
// ArticleTemplate.h
#import <Foundation/Foundation.h>
@interface ArticleTemplate : NSObject
// 标题
- (void)setTitle;
// 中心论点
- (void)theme;
// 论述论点
- (void)discourse;
// 总结
- (void)sumUp;
// 作者名字
- (void)authorName;
// 判断是否能添加作者名
- (BOOL)canAddAuthorName;
// 写论文
- (void)writeAritcle;
@end
// ArticleTemplate.m
#import "ArticleTemplate.h"
@implementation ArticleTemplate
- (void)setTitle {
NSAssert(false, @"must implement in subClass");
}
- (void)theme {
NSAssert(false, @"must implement in subClass");
}
- (void)discourse {
NSAssert(false, @"must implement in subClass");
}
- (void)sumUp {
NSAssert(false, @"must implement in subClass");
}
- (void)authorName {
NSAssert(false, @"must implement in subClass");
}
- (BOOL)canAddAuthorName {
NSAssert(false, @"must implement in subClass");
return NO;
}
- (void)writeAritcle {
[self setTitle];
[self theme];
[self discourse];
[self sumUp];
if ([self canAddAuthorName]) {
[self authorName];
}
}
@end
- 通过继承模板,进行创建个文章类:ArticleOne(可以在末尾写上作者名)、ArticleTwo(不能在末尾写上作者名)
// ArticleOne.h
#import "ArticleTemplate.h"
@interface ArticleOne : ArticleTemplate
@end
// ArticleOne.m
#import "ArticleOne.h"
@implementation ArticleOne
- (void)setTitle {
NSLog(@"标题:希望");
}
- (void)theme {
NSLog(@"中心论点:心存希望,展望未来");
}
- (void)discourse {
NSLog(@"论述:为什么……");
}
- (void)sumUp {
NSLog(@"总结:应该心存希望");
}
- (void)authorName {
NSLog(@"作者:小东");
}
- (BOOL)canAddAuthorName {
return YES;
}
@end
// ArticleTwo.h
#import "ArticleTemplate.h"
@interface ArticleTwo : ArticleTemplate
@end
// ArticleTwo.m
#import "ArticleTwo.h"
@implementation ArticleTwo
- (void)setTitle {
NSLog(@"标题:奋斗");
}
- (void)theme {
NSLog(@"中心论点:奋斗可以改变人生");
}
- (void)discourse {
NSLog(@"论述:为什么……");
}
- (void)sumUp {
NSLog(@"总结:应该坚持奋斗");
}
- (void)authorName {
NSLog(@"作者:小明");
}
- (BOOL)canAddAuthorName {
return NO;
}
@end
- 接下来就可以通过具体的文章类,进行创作了
+ (void)test {
ArticleOne *article = [ArticleOne new];
[article writeAritcle];
ArticleTwo *article2 = [ArticleTwo new];
[article2 writeAritcle];
}
@end
运行结果:
4. 分析
- 封装不变部分,拓展可变部分。把固定的算法部分在父类中进行实现,把可变部分放在子类中进行实现,拓展起来非常容易,符合开闭原则
- 将公共的部分进行封装,利于维护
- 子类的运行结果会对父类产生影响,代码阅读上会产生一定的困难
5. 适用场景
- 多个子类有共有的算法,且基本逻辑相同。可以把核心算法设计为模板方法模式
- 重构项目时这种设计模式比较常用,把共同方法抽到父类,具体细节实现放到各个子类中去,并且通过钩子函数做一些特殊步骤的管控