模板方法(Template Method)模式,英文是 Template Method Design Pattern。在 GoF 的《设计模式》一书中,它是这么定义的:
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.
翻译成中文就是:模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。这里的“算法”,我们可以理解为广义上的“业务逻辑”,并不特指数据结构和算法中的“算法”。这里的算法骨架就是“模板”,包含算法骨架的方法就是“模板方法”,这也是模板方法模式名字的由来。
模板方法模式的结构与实现
结构
- 抽象类(Abstract Class):定义了一到多个的抽象方法,以供具体的子类来实现它们;而且还要实现一个模板方法,来定义一个算法的骨架。该模板方法不仅调用前面的抽象方法,也可以调用其他的操作,只要能完成自身的使命。如果你不想模板方法被子类重写,可用
final
修饰模板方法以防止子类对其进行重写。 - 具体类(Concrete Class):实现父类中的抽象方法以完成算法中与特定子类相关的步骤。
代码实现
AbstractClass.php
<?php
namespace DesignPatterns\TemplateMethod\example1;
/**
* AbstractClass是抽象类,其实也就是一抽象模板, 定义并实现了一个模板方法。这个模板方法一般
* 是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类
* 实现。顶级逻辑也有可能调用一些具体方法。
* Class AbstractClass
* @package DesignPatterns\TemplateMethod\example1
*/
abstract class AbstractClass
{
//定义抽象行为,在子类中实现
abstract public function PrimitiveOperation1();
abstract public function PrimitiveOperation2();
//模板方法给出逻辑的骨架,而逻辑的组成是一些相应的抽象操作
//它们都推迟到子类中实现
public function TemplateMethod()
{
$this->PrimitiveOperation1();
$this->PrimitiveOperation2();
}
}
ConcreteClassA.php
<?php
namespace DesignPatterns\TemplateMethod\example1;
/**
* ConcreteClass,实现父类所定义的一一个或多个抽象方法。每一一个AbstractClass都可以有任意多个
* ConcreteClass与之对应,而每一一个 ConcreteClass都可以
* 给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。
* Class ConcreteClassA
* @package DesignPatterns\TemplateMethod\example1
*/
class ConcreteClassA extends AbstractClass
{
public function PrimitiveOperation1()
{
echo '具体类A实现方法1',PHP_EOL;
}
public function PrimitiveOperation2()
{
echo '具体类A实现方法2',PHP_EOL;
}
}
ConcreteClassB.php
<?php
namespace DesignPatterns\TemplateMethod\example1;
/**
* Class ConcreteClassB
* @package DesignPatterns\TemplateMethod\example1
*/
class ConcreteClassB extends AbstractClass
{
public function PrimitiveOperation1()
{
echo '具体类B实现方法1',PHP_EOL;
}
public function PrimitiveOperation2()
{
echo '具体类B实现方法2',PHP_EOL;
}
}
运行Client.php
<?php
namespace DesignPatterns\TemplateMethod\example1;
require dirname(__DIR__).'/../vendor/autoload.php';
/**
* Class Client
* @package DesignPatterns\TemplateMethod\example1
*/
class Client
{
public function run(){
$c = new ConcreteClassA();
$c->TemplateMethod();
echo "<br>";
$c = new ConcreteClassB();
$c->TemplateMethod();
}
}
$worker = new Client();
$worker->run();
运行结果:
在客户端的调用中,实例化子类,但调用的是子类所继承的父类的模板方法。它决定了执行的顺序等行为只能由父级抽象类决定;而子类只需要完成具体的操作内容;
示例
在本例中, 模板方法模式定义了在社交网络上发布消息的算法框架。 每个子类都代表一个不同的社交网络, 它们虽以不同方式实现所有步骤, 但却会复用基本的算法。
SocialNetwork.php
<?php
namespace DesignPatterns\TemplateMethod\example2;
/**
* Class SocialNetwork
* @package DesignPatterns\TemplateMethod\example2
*/
abstract class SocialNetwork
{
protected $username;
protected $password;
public function __construct(string $username, string $password)
{
$this->username = $username;
$this->password = $password;
}
/**
* 实际的模板方法按特定的顺序调用抽象步骤。
* 子类可以实现所有的步骤,允许这个方法在社交网络上发布内容。
* @param string $message
* @return bool
*/
public function post(string $message): bool
{
if ($this->logIn($this->username, $this->password)) {
$result = $this->sendData($message);
$this->logOut();
return $result;
}
return false;
}
//登陆
abstract public function logIn(string $userName, string $password): bool;
//发布信息
abstract public function sendData(string $message): bool;
//退出
abstract public function logOut(): void;
//一个小助手方法,让等待时间感觉真实。
protected function simulateNetworkLatency()
{
$i = 0;
while ($i < 5) {
echo ".";
sleep(1);
$i++;
}
}
}
Facebook.php
<?php
namespace DesignPatterns\TemplateMethod\example2;
/**
* Class Facebook
* @package DesignPatterns\TemplateMethod\example2
*/
class Facebook extends SocialNetwork
{
public function logIn(string $userName, string $password): bool
{
echo "\n正在检查用户的凭据...\n";
echo "名称: " . $this->username . "\n";
echo "密码: " . str_repeat("*", strlen($this->password)) . "\n";
$this->simulateNetworkLatency();
echo "\n\nFacebook: '" . $this->username . "' 登录成功\n";
return true;
}
public function sendData(string $message): bool
{
echo "Facebook: '" . $this->username . "'发布了'".$message."'\n";
return true;
}
public function logOut(): void
{
echo "Facebook: '" . $this->username . "' 退出登录.\n";
}
}
ZhiHu.php
<?php
namespace DesignPatterns\TemplateMethod\example2;
/**
* Class Facebook
* @package DesignPatterns\TemplateMethod\example2
*/
class ZhiHu extends SocialNetwork
{
public function logIn(string $userName, string $password): bool
{
echo "\n正在检查用户的凭据...\n";
echo "名称: " . $this->username . "\n";
echo "密码: " . str_repeat("*", strlen($this->password)) . "\n";
$this->simulateNetworkLatency();
echo "\n\nzhihu: '" . $this->username . "' 登录成功\n";
return true;
}
public function sendData(string $message): bool
{
echo "zhihu: '" . $this->username . "'发布了'".$message."'\n";
return true;
}
public function logOut(): void
{
echo "zhihu: '" . $this->username . "' 退出登录.\n";
}
}
Client.php
<?php
namespace DesignPatterns\TemplateMethod\example2;
require dirname(__DIR__).'/../vendor/autoload.php';
/**
* Class Client
* @package DesignPatterns\TemplateMethod\example2
*/
class Client
{
public function run()
{
echo "Username: \n";
$username = readline();
echo "Password: \n";
$password = readline();
echo "Message: \n";
$message = readline();
echo "\nChoose the social network to post the message:\n" .
"1 - Facebook\n" .
"2 - zhihu\n";
$choice = readline();
// Now, let's create a proper social network object and send the message.
if ($choice == 1) {
$network = new Facebook($username, $password);
} elseif ($choice == 2) {
$network = new ZhiHu($username, $password);
} else {
die("Sorry, I'm not sure what you mean by that.\n");
}
$network->post($message);
}
}
$worker = new Client();
$worker->run();
运行 Client.php
总结
模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。这里的“算法”,我们可以理解为广义上的“业务逻辑”,并不特指数据结构和算法中的“算法”。这里的算法骨架就是“模板”,包含算法骨架的方法就是“模板方法”,这也是模板方法模式名字的由来。
在模板模式经典的实现中,模板方法定义为 final,可以避免被子类重写。需要子类重写的方法定义为 abstract,可以强迫子类去实现。不过,在实际项目开发中,模板模式的实现比较灵活,以上两点都不是必须的。
参考资料:《大话设计模式》,《深入设计模式-模板方法模式》
github示例:https://github.com/yangpanyao/design-patterns/tree/master/TemplateMethod
评论 (0)