定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method 使得子类可以在不改变一个算法的结构的情况下重定义该算法的某些特定的步骤【GOF95】
【模板方法模式结构图】
Template Method
Template Method
【模板方法模式中主要角色】
抽象模板(AbstractClass)角色: 定义一个或多个抽象方法让子类实现。这些抽象方法叫做基本操作,它们是顶级逻辑的组成部分。
定义一个模板方法。这个模板方法一般是一个具体方法,它给出顶级逻辑的骨架,而逻辑的组成步骤在对应的抽象操作中,这些操作将会推迟到子类中实现。同时,顶层逻辑也可以调用具体的实现方法 具体模板(ConcrteClass)角色:实现父类的一个或多个抽象方法,作为顶层逻辑的组成而存在。
每个抽象模板可以有多个具体模板与之对应,而每个具体模板有其自己对抽象方法(也就是顶层逻辑的组成部分)的实现,从而使得顶层逻辑的实现各不相同。
【模板方法模式适用场景】
1、一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
2、各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
3、控制子类扩展。
【模板方法模式与其它模式】
1、策略模式(strategy模式):模板方法使用继承来改变算法的部分,策略模式使用委托来改变整个算法。区别在于封闭的变化不同,一个变化的部分,一个变化的是整体。
2、工厂方法模式(factory method模式):Factory Method模式常被模板方法调用。
【模板方法模式】
模板方法是一种代码复用的基本技术,模板方法导致一种反射的控制结构,这指的是一个父类调用子类的操作。
其实现过程:准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。
【重构的原则】
重构时应当遵守的原则是:将行为以是移到结构的高端,而将状态尽量移动到结构的低端。
Auer曾在文献【AUER95】中指出:
1、应当要所行为而不是状态定义一个类。
2、在实现行为是,是用抽象状态而不是用具体状态。
3、给操作划分层次。
4、将状态的确认推迟到子类中。在父类中,如果需要状态属性的话,可以调用抽象的取值方法,而将抽象的取值方法的实现放到具体子类中。
如果可以遵守以上的而,那么就可以在等级结构中将接口与实现分离,将抽象与具体分离,从而保证代码可以最大限度的被复用。
以下内容来自:http://my.oschina.net/cniiliuqi/blog/64672
Template_模板模式解决问题的思路:
设想这样一个问题,即用户登录问题。在一个系统中,可能会有多种角色进行登录操作。可能有普通用户,VIP用户,后台管理员等等。这些角色的用户信息可能被分别储存在不不同的用户数据表中。但仔细问题,我们会发现,无论什么角色,在进行登录时,基本上的操作流程都是:输入用户名、密码 -> 提交输入的信息 -> 服务器端进行数据验证 -> 返回验证信息(TRUE:进入相应的后台,FLASE:进入相应的错误处理流程.)
在普通的实现中,这些登陆可能是完全分开的,相互独立的模块。我们并没有从整体上进行控制。如果把这些登陆操作当做一个整体来看,我们就会发现这些登陆操作存在着大量的,重复或者相似的代码。根据模板模式的定义,我们应该把这些重复或者相似的代码从各自独立的模块中抽取出来,做成公共的功能,而不同角色的登陆控制就可以去扩展这些公共功能了。
001 <?php
002 /**
003 * Template_模板方法模式_PHP语言描述
004 * <ahref="http://my.oschina.net/arthor"target="_blank">@author</a> ToniLiu
005 */
006
007 /**
008 * 封装进行登录控制所需的数据
009 */
010 class loginModel{
011 //登录人员ID,通用的,可能是普通用户编号,也可能是后台管理员编号
012 private $loginId;
013 //登录的密码
014 private $pwd;
015
016 public function getLoginId(){
017 return $this->loginId;
018 }
019
020 public function setLoginId($loginId){
021 $this->loginId = $loginId;
022 }
023
024 public function getPwd(){
025 return $this->pwd;
026 }
027
028 public function setPwd($pwd){
029 $this->pwd = $pwd;
030 }
031 }
032
033 /**
034 * 定义公共的登陆控制算法模板骨架,即抽象的模板类
035 */
036 abstract class LoginTemplate{
037 /**
038 * 判断登陆数据是否正确,也就是是否能登陆成功
039 *@param lm 封装登陆数据的Model
040 *<a href="http://my.oschina.net/u/556800"target="_blank">@return</a> true 表示登陆成功,false表示登陆失败
041 */
042 public final function Login($lm){
043 $dbLm = $this->findLoginUser($lm->getLoginId());
044
045 if($dbLm!=null){
046 $encryptPwd = $this->encryptPwd($lm->getPwd());
047 $lm->setPwd($encryptPwd);
048 return $this->match($lm,$dbLm);
049 }
050 }
051
052 /**
053 * 根据登陆编号来查找和获取存储中相应的数据
054 *@param loginId 登陆编号
055 *<a href="http://my.oschina.net/u/556800"target="_blank">@return</a> 登陆编号中存储中相对应的数据
056 */
057 public abstract function findLoginUser($loginId);
058
059 public function encryptPwd($pwd){
060 return $pwd;
061 }
062
063 /**
064 * 判断用户填写的登陆数据和数据空中对应的数据是否匹配的上
065 *@param lm 用户填写的登陆数据
066 *@param dbLm 数据库中对应的数据
067 *<a href="http://my.oschina.net/u/556800"target="_blank">@return</a> 表示匹配成功,false表示匹配失败
068 */
069 public function match($lm,$dbLm){
070 echo "lm:".$lm->getPwd()."<br>";
071 echo "dblm:".$dbLm->getPwd()."<br>";
072 if($lm->getLoginId() == $dbLm->getLoginId() &&$lm->getPwd() == $dbLm->getPwd()){
073 return true;
074 }
075 return false;
076 }
077 }
078
079 /**
080 * 实现不同的登陆特例,普通用户登陆逻辑
081 */
082 class NormalLogin extends LoginTemplate{
083 //此处实现的是特例的个体逻辑
084 public function findLoginUser($loginId){
085 //这里是一个示例,就直接返回一个创建好的用户信息了,应该是根据ID去数据库查询是否有这个用户,如果有这个ID用户,初始化并拿出来,普通用户,密码为明码
086 $lm = new loginModel();
087 $lm->setLoginId($loginId);
088 $lm->setPwd("4444");
089 return $lm;
090 }
091 }
092
093 /**
094 * 实现后台工作人员登陆特例
095 */
096 class WorkerLogin extends LoginTemplate{
097 public function findLoginUser($loginId){
098 //这里是一个示例,就直接返回一个创建好的用户信息了,模拟从数据拿出来的管理员数据,密码为MD5加密
099 $lm = new loginModel();
100 $lm->setLoginId($loginId);
101 $lm->setPwd(md5("123456"));
102 return $lm;
103 }
104
105 public function encryptPwd($pwd){
106 //覆盖父类的方法,提供真正的加密实现
107 //这里对密码进行加密,比如使用MD5
108 echo "使用了MD5进行了加密";
109 return md5($pwd);
110 }
111 }
112
113 $lm = new loginModel();
114 $lm->setLoginId("admin");
115 $lm->setPwd("123456");
116
117 //准备用来进行判断的对象
118 $lt = new WorkerLogin();
119 $lt2 = new NormalLogin();
120
121 //进行登陆测试
122 $flag = $lt->login($lm);
123 echo "可以登陆工作平台=".$flag;
124 $flag2 = $lt2->login($lm);
125 echo "可以进行普通人员登陆=".(int)$flag2;
126 ?>