设计模式之模板方法模式 浏览次数:298 日期:2020-08-14 13:07:21 ### 定义 模板方法定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。 通俗的说,模板方法定义算法的大逻辑,具体细节由子类完成。举例来说,比如盖房子,模板方法定义可以定义盖房子的关键步骤,先打地基、然后盖墙壁,然后嵌入门窗,最后搭房顶等,而具体的实现可以交给子类,子类怎么打地基,怎么盖墙壁,父类并不关心。 ### 结构 - 抽象类:负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。这些方法的定义如下: - 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。为防止恶意操作,一般模板方法都加上 final 关键词 - 基本方法:是整个算法中的一个步骤,包含以下几种类型: - 抽象方法:在抽象类中申明,由具体子类实现。 - 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。 - 钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种 - 具体子类:实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。 ### 代码实现 ```php getSheetName(); $this->getTitle(); $this->getBody(); if($this->haveFooter()) { $this->getFooter(); } } } class OrderExcel extends Excel { public function getTitle() { echo "OrderExcel的title"; } public function getBody() { echo "OrderExcel的body"; } public function getFooter() { } } class UserExcel extends Excel { public function getTitle() { echo "UserExcel的title"; } public function getBody() { echo "UserExcel的body"; } public function getFooter() { echo "UserExcel的footer"; } public function haveFooter() { return true; } } ``` 使用方法 ```php $orderExcel = new OrderExcel; $orderExcel->handle(); $orderExcel = new UserExcel; $orderExcel->handle(); ``` 我们可以看到 - 模板方法handle()规定了大的行为,具体的实现由子类完成; - 抽象类的getTitle()、getFooter()、getBody()抽象方法由子类实现,实现具体的细节; - 钩子方法haveFooter()用于判断是否需要getFooter()方法,每一个子类可以根据自己的业务是否需要 getFooter()去重写钩子方法; - getSheetName()作为逻辑中的一个小环节,由抽象类实现,具体子类可以进行重写,也可以不重写。 模板方法提供了一个骨架的基本算法,子类不需要关注骨架的前后逻辑,只需要实现每一个细节抽象类即可。有时候模板方法会要求子类按照一定的规范,去在每一步返回固定格式的数据,便于模板方法拿到数据,进行业务处理。大体如下: ```php getSheetName(); echo "我获取的title内容:".$this->getTitle(); $this->getBody(); echo "我拿到了body数据:".$this->body; if($this->haveFooter()) { //我不关心getFooter的返回结果 $this->getFooter(); } } } class UserExcel extends Excel { public function getTitle() { return "UserExcel的title"; } public function getBody() { $this->body = 'this is body'; } public function getFooter() { return "UserExcel的footer"; } public function haveFooter() { return true; } } ``` ### 优缺点 优点: - 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。 - 它在父类中提取了公共的部分代码,便于代码复用。 - 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。 缺点: - 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。 - 按照设计习惯,抽象类负责声明最抽象、最一般的事务属性和方法,实现类负责完成具体的事务属性和方法,但是模板方法正好相反,子类执行的结果影响了父类的结果,会增加代码阅读的难度 最后更新时间为: 5个月前 (2020-08-14 13:07:21)