设计模式——模版方法模式

shanzm-2020年7月20日 01:55:50

1. 简介

1.定义git

模版方法模式(Template Method Pattern):用于定义一个操做中算法的骨架,而将一些步骤延迟到子类中。github

模版方法模式使得子类能够不改变一个算法的结构及可重定义该算法的某些特定步骤。算法

简而言之:模版方法模式功能在于固定算法骨架,而让具体算法实现可扩展。sql

其实只要是理解继承和多态便可以当即明白模版方法模式。模版方法模式是经过把不变行为搬移到父类,除去子类中的重复代码。数据库

2.主要类设计模式

  • AbstractClass:抽象类。用来定义算法骨架(抽象方法)。具体的子类经过重写抽象方法来实现一个算法的各个步骤。oracle

  • ConcreteClass:具体实现类。用来实现算法骨架中的某些步骤,完成与特定子类相关的功能。ide

3.名词解释函数

  • 模版方法:抽象类中定义算法骨架的方法。设计

  • 原语操做:即抽象方法,抽象类中定义的抽象操做。

  • 具体操做:即具体方法,抽象类定义的具体方法体的方法

  • 钩子方法:用于决定模版方法中某个方法是否执行的方法。钩子方法就是经过子类的行为去反向控制父类的行为的一种方法

4.抽象工厂模式的UML

Template Method Pattern UML

注:原图片来自《设计模式实训教程-第二版》


2. 示例

2.1 背景说明

对数据库的操做:链接-->打开-->使用-->关闭

对于不一样的数据操做步骤都是同样的,只是链接数据库稍有不一样

这里咱们定义一个抽象模版类DBOperator,其中咱们定义ConnDB()、OpenDB()、UseDB()、CloseDB()四个抽象方法

定义不一样的数据库具体实现类:SQLServerDBOperator、OracleDBOperator

2.2 代码实现

①定义抽象父类

public abstract class DBOperator
{
    //原语操做1
    public abstract void ConnDB();
    //原语操做2
    public void OpenDB()
    {
        Console.WriteLine("打开数据库");
    }
    //原语操做3
    public void UseDB()
    {
        Console.WriteLine("使用数据库");
    }
    //原语操做4
    public void CloseDB()
    {
        Console.WriteLine("关闭数据库");
    }

    //钩子方法:用于子类反向控制父类中某个方法是否执行
    //注意钩子方法这里定义为虚方法,虚方法和抽象方法不一样的地方就是虚方法有方法体,这样咱们就能够在虚方法中定义默的方法
    public virtual bool IsStart()
    {
        return true;//默认为true因此在具体子类中重写
    }

    //模版方法:定义算法骨架
    //肯定其余基本方法的执行顺序
    public void Process()
    {
        ConnDB();
        OpenDB();
        if (IsStart())//经过钩子方法控制是否执行UseDB()
        {
            UseDB();
        }
        CloseDB();
    }
}

②定义具体实现类

//具体子类1
public class OracleDBOperator : DBOperator
{
    public override void ConnDB()
    {
        Console.WriteLine("链接Oracle数据库");
    }
    //按照面向对象的思想,咱们能够在子类中使用new 关键字覆盖父类中的方法
    //注意:
    //1. 这里覆盖了UseDB(),是不够的,还要覆盖调用这个方法的方法Process()
    //2. 如果父类引用指向子类对象,则须要将父类转化为子类对象才可使用子类中覆盖的方法
    public new void UseDB()
    {
        Console.WriteLine("使用Oracle数据库");
    }

    public new void Process()
    {
        ConnDB();
        OpenDB();
        if (IsStart())
        {
            UseDB();
        }
        CloseDB();
    }
}

//具体子类2
public class SQLServerDBOperator : DBOperator
{
    ///具体子类继承抽象父类,重写抽象父类中的抽象方法
    ///抽象父类中的非抽象方法就是每一个子类都通用的方法,
    public override void ConnDB()
    {
        Console.WriteLine("链接SQL Server数据库");
    }

    public override bool IsStart()//覆盖了钩子函数,修改了抽象父类中模版方法,实现子类反向控制父类
    {
        return false;
    }
}

③客户端

static void Main(string[] args)
{
     static void Main(string[] args)
        {
            DBOperator sqlServerDBOperator = new SQLServerDBOperator();
            DBOperator oracleDBOperator = new OracleDBOperator();

            sqlServerDBOperator.Process(); //注意这里,咱们定义的钩子方法是虚方法,经过重写,实现子类控制父类,这里是不须要将强转为子类对象
            Console.WriteLine("---------------------------");

            oracleDBOperator.Process();
            Console.WriteLine("---------------------------");

            ((OracleDBOperator)oracleDBOperator).Process(); //这里是经过覆盖父类中的方法,因此须要将父类强转为相应的子类对象
            Console.WriteLine("---------------------------");

            Console.ReadKey();
        }
}

④运行结果

链接SQL Server数据库
打开数据库
关闭数据库
---------------------------
链接Oracle数据库
打开数据库
使用数据库
关闭数据库
---------------------------
链接Oracle数据库
打开数据库
使用Oracle数据库
关闭数据库


3. 总结分析

3.1 补充

1.模版方法模式中为什么使用的是抽象类而不是接口?

这里先说明一下接口和抽象类的区别:

  • 接口只是定义一组行为,某个类(非抽象类)实现这个接口就须要使用该接口中的全部方法。简而言之:接口约束实现该接口的类的行为。因此接口也称之为契约。

  • 抽象类中的抽象方法须要子类重写,而抽象类能够有非抽象方法,非抽象方法是须要在抽象类中写方法体的。简而言之:抽象类不只约束子类的行为并且须要为子类中非抽象方法提供方法体

这里咱们使用抽象类而不是使用接口,为何呢?如果定义为抽象方法则咱们须要在实现类中须要对抽象方法都重写,而可能多个子类中的某个方法实现是同样的。

这里也体现了接口和抽象类的不一样之处:接口约束实现接口的类行为(因此接口也称之为契约)而抽象类不只要约束子类的行为,而要为子类提供公共方法体

这里示例中的数据库链接函数ConnDB定义为抽象方法,由于每一个数据库的链接方法是不同的,因此在每一个具体的实现类中都须要对其重写 。而其余的数据库操做方法相似,因此咱们在父类中对其实现,避免子类中代码冗余。

3.2 优势

模板方法模式的优势是实现代码复用。
模板方法模式是一种实现代码复用的很好的手段。经过把子类的公共功能提炼和抽取,把公共部分放到模板中去实现。

模版方法模式体现类开闭原则。首先模版方法模式从设计上分离变与不变,而后把不变的部分抽取出来,定义到父类中,好比算法骨架,一些公共的、固定的实现等。这些不变的部分被封闭起来,尽可能不去修改它们。要想扩展新的功能,那就使用子类来扩展,经过子类来实现可变化的步骤,对于这种新增功能的作法是开放的。

3.3 缺点

模板方法模式的缺点是算法骨架不容易升级。
模板方法模式最基本的功能就是经过模板的制定,把算法骨架彻底固定下来。事实上模板和子类是很是耦合的,若是要对模板中的算法骨架进行变动,可能就会要求全部相关的子类进行相应的变化。好比模版方法中添加一个抽象方法,则全部的子类都须要去实现这方法。
因此抽取算法骨架的时候要特别当心,尽可能确保是不会变化的部分才放到模板中。

3.4 适应场合

建议在如下状况中选用模板方法模式

  • 须要固定定义算法骨架。实现一个算法的不变的部分,并把可变的行为留给子类来实现的状况。
  • 各个子类中具备公共行为,应该抽取出来,集中在一个公共类中去实现,从而避免代码重复。
  • 须要控制子类扩展的状况。模板方法模式会在特定的点来调用子类的方法,这样只容许在这些点进行扩展。

3.5 对比与合用

【TODO】(待策略模式研究完在完成此部分!)

  • 模版方法模式与工厂方法模式

  • 模版方法模式和策略模式



4. 参考及源码

相关文章
相关标签/搜索