您现在的位置是:首页 >技术杂谈 >设计模式(行为型模式)之:Template Method(模板方法)网站首页技术杂谈

设计模式(行为型模式)之:Template Method(模板方法)

whcz 2024-08-26 00:01:02
简介设计模式(行为型模式)之:Template Method(模板方法)

简介

该篇文章内容及思想出自 张建忠《设计模式》、GOF: 《设计模式》

定义一个操作中算法的骨架,而将一些操作延迟到子类中。TemplateMethod使得子类可以不改变一个算法的结构,即可重定义该算法的某些特定步骤。(GOF: 《设计模式》)

Template Method可以说是所有设计模式中最常用的一种了。常用到什么地步呢?就算你从来没接触过任何设计模式,你也一定使用过这种模式,只是自己没有意识到罢了。

动机

在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现
如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求?

定义

定义一个操作中的算法的骨架(稳定),而将一些步骤延迟(变化)到子类中。Template Method 使得子类可以不改变(复用)一个算法的结构即可重定义(override 重写)该算法的某些特定步骤

早绑定代码

代码案例
先上代码:

class APP
{
    bool step2();
    int step4();
};

class Framework
{
    void step1();
    void step3();
    void step5(int u);
};

int main()
{
    Framework pl;
    APP app;
    
    //Process
    pl.step1();
    if(app.step2())
        pl.step3();
    
    int s = app.step4();
    for(int i=0; i<s; ++i)
    	pl.step5(i);
}
//我们假装所有函数都已经实现

这里模拟了一个平台的启动流程:在main函数中,我们创建了Framework和APP两个对象,并按照特定的顺序调用了这两个对象的某些函数执行了一个特定的流程。现在,我们假设你是用户,即你是该main函数和APP这个类的编写者,你需要做什么?

很显然,你必须严格地按照指定的流程(因为只有这样才能使得框架代码正确运行)在你的main中原封不动地写下上面的代码,并且补全你的Class APP。

这听起来也是一种合理的做法。如果你对WINDOWS提供的图形化编程库MFC有所了解的话,你就知道,比如,在WINDOWS下申请一个窗口,你必须怎样怎样注册,怎样怎样给它指定参数,这套流程是框架代码编写方制定的,是稳定的。

这就引出了我们接下来的问题:真的有必要让用户手敲这么一长串功能单一且完全不需要更改的代码吗?我们对这种写法的复用性表示质疑。

晚绑定 模板设计模式

现在,我们来看看改进后的代码:

class Framework
{
private:
    void step1();
    void step3();
    void step5(int u);
protected:
    virtual bool step2() = 0;
    virtual int step4() = 0;
public:
    void run()
    {
        step1();
        if(step2())
            step3();
        
        int s = step4();
    	for(int i=0; i<s; ++i)
    		step5(i);
    }
};

class APP: public Framework
{
    //override
protected:
    bool step2(){}
    int step4(){}
};

应用设计模式的出发点,就是要找到应用场景中的稳定点和变化点,在这一案例中,稳定点就是上述代码的run函数。既然程序主流程是定死的,那么就直接在Framewoek中将它实现。进而,我们只需要用我们自己的APP类来继承框架类,补全两个必要的函数,这样,在main中只需要写下以下代码:

int main()
{
    APP app;
    app.run();
}

程序就可以正确运行了, 这就是Template Method模式。run函数就是所谓的Template(模板),它封装了使得框架正确运行的算法,用户只需要重写相应的函数即可。

对比分析

在第一种设计方案中,Framework和APP互相不干扰。而在应用Template Method的设计中,我们将稳定的算法封装到了Framework中,使得用户不需要了解这些代码,只需要重写相应的功能。

我们讲过,考虑应用设计模式是,应当明确稳定点和变化点。在上面的案例中,稳定点是Framework中的算法,变化点就是APP应当重写的子过程。

因此TemplateMethod适用于这种情况:框架代码稳定,而子过程会发生变化 。

所以为什么说Template Method是最常见的设计方法呢?当你理解上面的这一过程后,你想必已经恍然大悟了。在C++编程者最爱的STL算法库中,就总是会使用到这种技术,比如下面的代码:

class A
{
private:
    int a, b ,c;
public:
    bool operator<(A a)
    {
        return a<a.a && b<a.b && c>a.c;
    }
};

int main()
{
    vector<A> va;
    sort(va.begin(), va.end());
}

用vector容器装自己编写的A类对象,并调用库函数sort对其进行排序。sort函数就相当于上文例子中被封装的run方法,而在类A中对‘ < ’进行重写,相当于类APP继承Framework并重写两个必要的虚函数。

总结

设计模式Template Method(模板方法)
稳定点:程序的整体运行框架
变化点:某些子过程
效果:使得子类可以不改变整体算法而置重定义某些特定步骤。
特点:封装算法,重写子过程
适用:框架代码稳定,而子过程会发生变化

(下图来自GOF 《设计模式》)
在这里插入图片描述

参考:https://blog.csdn.net/natrick/article/details/112846765

Template Method 模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(抽象方法的多态性)为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构
处理可以灵活应对子步骤的变化外,“不要调用我,让我来调用你”的反向控制结构是Template Method的典型应用
在具体实现方面,被Template Method 调用的抽象方法可以有实现 ,也可以没有任何实现,但一般推荐将它们设置为protected 方法

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。