首页 > 技术文章 > 设计模式 - 模板方法设计模式

kika 2018-07-01 14:52 原文

模板方法及各种语言的实现

简介

模式定义

模板方法(Template Method)设计模式中,定义了一个操作中算法的骨架,而将一些步骤延迟到子类中。子类可以复用算法的结构,同时可用重写算法的某些特定步骤。

模式特点

模板方法将稳定的东西(流程,不需要重写的方法)放在类库中,只将变化的东西开放给应用开发人员。

这里写图片描述

示例

例如,程序框架定义了稳定的算法流程 run(),且在其依赖的三个方法中,有虚方法 f2()

class Lib
{
public:
    void run()
    {
        f1();
        f2();
        f3();
    }

    void f1()
    {
        //...
    }

    virtual void f2()
    {
        //...
    }

    void f3()
    {
        //...
    }
}

应用开发人员实现自己的 f2(),然后启动流程:

class App
{
public:
    virtual void f2()
    {
        //...
    }

    run();
}

早绑定和晚绑定

将函数体和函数调用关联起来,就叫绑定(Connecting a function call to a function body is called binding)。

  • 早绑定(Early binding)
    When binding is performed before the program is run (by the compiler and linker), it’ s called early binding
    在程序运行之前(也就是编译和链接时)就发生的绑定是早绑定。
  • 晚绑定(late binding)
    late binding, which means the binding occurs at runtime, based on the type of the object. When a language implements late binding, there must be some mechanism to determine the type of the object at runtime and call the appropriate member function.
    晚绑定发生在运行时,基于不同类型的对象。如果一种语言实现了晚绑定,就必须有某种机制判断对象的具体类型然后调用合适的成员函数。

晚绑定可以实现多态。父类中定义 virtual 虚方法(相当于抽象方法),在子类中重载时,只有在运行时才会知道用哪个子类。

简单的 C++ 虚方法示例,其中虚方法 sleep 发生晚绑定:

#include <iostream>
using namespace std;

class Animal
{
public:
    virtual void sleep()
    {
        cout << "Animal sleep!" << endl;
    }

    void breath()
    {
        cout << "Animal breath!" << endl;
    }
};

class Fish :public Animal
{
public:
    virtual void sleep()
    {
        cout << "Fish sleep!" << endl;
    }
    void breath()
    {
        cout << "Fish breath!" << endl;
    }
};
int main()
{
    Fish f;
    Animal* a = &f; //基类指针指向派生类
    a->breath();
    a->sleep();
    return 0;
}

输出:

Animal breath!
Fish sleep!

PHP 代码示例

模板方法必须是 final 类型,只能使用而不能修改。

abstract class TemplateAbstract {
    //模板方法,设置算法的流程
    public final function showBookTitleInfo($book_in) {
        $title = $book_in->getTitle();
        $author = $book_in->getAuthor();
        $processedTitle = $this->processTitle($title);
        $processedAuthor = $this->processAuthor($author);
        if (NULL == $processedAuthor) {
            $processed_info = $processedTitle;
        } else {
            $processed_info = $processedTitle.' by '.$processedAuthor;
        }
        return $processed_info;
    }
    // 抽象方法,必须重写
    abstract function processTitle($title);
    // 这个可以不重写,这样会返回 NULL
    function processAuthor($author) {
        return NULL;
    } 
}

class TemplateExclaim extends TemplateAbstract {
    function processTitle($title) {
      return Str_replace(' ','!!!',$title); 
    }
    function processAuthor($author) {
      return Str_replace(' ','!!!',$author);
    }
}

class TemplateStars extends TemplateAbstract {
    function processTitle($title) {
        return Str_replace(' ','*',$title); 
    }
}

class Book {
    private $author;
    private $title;
    function __construct($title_in, $author_in) {
        $this->author = $author_in;
        $this->title  = $title_in;
    }
    function getAuthor() {return $this->author;}
    function getTitle() {return $this->title;}
    function getAuthorAndTitle() {
        return $this->getTitle() . ' by ' . $this->getAuthor();
    }
}

  writeln('BEGIN TESTING TEMPLATE PATTERN');
  writeln('');

  $book = new Book('PHP for Cats','Larry Truett');

  $exclaimTemplate = new TemplateExclaim();  
  $starsTemplate = new TemplateStars();

  writeln('test 1 - show exclaim template');
  writeln($exclaimTemplate->showBookTitleInfo($book));
  writeln('');

  writeln('test 2 - show stars template');
  writeln($starsTemplate->showBookTitleInfo($book));
  writeln('');

  writeln('END TESTING TEMPLATE PATTERN');

  function writeln($line_in) {
    echo $line_in."<br/>";
  }

推荐阅读