在我常见的需求中,比较多的就是数据导出(Excel),常见的做法是如果要导出 User 用户数据,我们写一个函数,把整个导出过程包括标题设置、数据查询、填充导出数据、导出等实现一遍。
而当我们导出 Book 书籍数据时我们会重新定义一个函数,然后将上面的步骤重新实现一遍(或者复制一遍)
上面的实现方式,可以实现导出效果,但是会写出大量重复的代码(导出操作,填充操作都是可以重复利用的)
我们考虑上面的实现方式,需要怎么优化?
第一,步骤是不变的(标题设置、数据查询、填充导出数据、导出等)
第二,不变的是填充数据和导出这个操作,这两个操作完全可复用的
第三,标题和数据源是报表变化的主要点,不同的报表标题和数据源基本不同这是可以肯定的。
那么根据上面思路,我们只要开放一个导出接口,这个接口依次执行导出的所有步骤。而对于填充数据和导出操作,是不应该变化且不对外开放且不可以修改。同时让"标题设置"和"数据查询"可扩展,让具体的报表根据具体的需求实现。
代码实现:
//Excel导出模板类
abstract class ExportTemplate {
private $_data = null;
private $_header = null;
public abstract function getHeader(); //可重定义该算法的某些特定步骤。
public abstract function getData(); //可重定义该算法的某些特定步骤。
public function export(){ //对外接口
//定义导出具体步骤(算法大纲)
$this->_data = $this->getData();
$this->_header = $this->getHeader();
$this->dealData($data);
$this->dealExcel();
}
//对数据可做额外处理,如果不需要子类不实现
protected function dealData(&$data) {}
private function dealExcel(){ //固定的导出实现
echo "根据header和data写具体Excel导出方法"
}
}
//用户Excel
class ExportUser extends ExportTemplate {
public function getHeader(){
return array('姓名','性别', '国籍', '....')
}
public function getData() {
return array(
array('小美', '女', '中国'),
array('小明', '男', '中国'),
array('小五', '男', '中国'),
)
}
}
//书籍Excel
class ExportBook extends ExportTemplate {
public function getHeader(){
return array('名称','作者', '价格', '....')
}
public function getData() {
return array(
array('PHP100', 'xxx', '中国'),
array('JAVA', 'CC', '中国'),
array('C++', 'EE', '中国'),
)
}
}
//导出用户数据
$ExportUser = new ExportUser();
$ExportUser->export();
//导出书籍书籍
$ExportBook = new ExportBook();
$ExportBook->export();
而这就是我们今天的主角模板模式
模板模式或者说模板方法主要作用是定义算法大纲。让子类去实现具体步骤(继承),对算法步骤进行控制。