用心理解设计模式——模板方法模式 (Template Method Pattern)

前置文章: 设计模式的原则 

其他设计模式:用心理解设计模式专栏

设计模式相关代码已统一放至 我的 Github

一、定义

  行为型模式之一。

  Define the skeleton of an algorithm in an operation, deferring some steps to subclass. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.

(在操作中定义一个算法的框架,将一些步骤延迟到子类中,使得子类可以不改变算法结构即可重新定义该算法的某些特定步骤。)

二、结构解析

  模板方法模式的一般结构有两种角色: 抽象类、具体类。

  抽象类(AbstractClass):定义模板方法,模板方法将程序的执行步骤总结出来并固定, 然后直接实现其中的不变步骤,将可变的步骤声明为抽象方法,具体的实现延迟至子类。

  具体类(ConcreteClass):实现抽象父类中定义的抽象步骤。

三、评价

  模板方法模式是典型的面向抽象编程,在抽象阶段将系统的不变部分(如步骤顺序)确定下来,变化部分(如步骤具体内容)交给子类。

  模板方法模式很常见,可参考,蝇量模式 、装饰器模式、责任链模式 示例代码中的使用。

四、钩子方法

  钩子方法常出现在模板方法模式中,顾明思意,它就是一个钩子。

  钩子可以在定义时就挂着东西(父类可有实现),可以在后来看情况挂上别的东西(子类可重写),也可以总是不挂任何东西(父类中无实现,并且子类中未重写或重写无实现)。

  钩子方法应该是一个虚方法,因为虚方法才可以在定义时能够选择性实现,并且可以让子类选择性重写(注意,抽象方法无法直接给出实现,并且子类必须重写,不然会有编译错误。所以抽象方法虽然可以作为钩子方法,但不是太适合,不完美)。虚方法应该只出现在抽象类中,不然一不小心就容易违反里式替换原则(里氏替换原则要求子类可随时替换父类,而虚方法的存在,让子类与父类的行为产生了不同,又因为抽象类不会有实例,永远无法拿子类实例和抽象类实例做里氏替换,所以虚方法只出现在抽象类中是最安全的)。

  一定要注意,钩子方法应该不是关键步骤,不阻碍模板主干运行,它是一种 “可选的、附加的” 的存在。

五、实现

using UnityEngine;

namespace TemplateMethod
{
    public abstract class AbstractClass
    {
        //模板方法,将不变的行为(一般是步骤)总结出来。(子类不要重写这个方法)
        public void TemplateMethod()
        {
            DoStep1();
            DoStep2();
            DoStep3();
            Hook();
            DoStep4();
        }

        //其中,某些步骤是不变的,某些步骤是可变的。
        //不变的,直接给出实现,
        //可变的,在子类中确定具体的执行内容。
        private void DoStep1() { Debug.Log("DoStep1"); }            //不变的。
        public abstract void DoStep2();                             //可变的,实现延迟至子类。
        public abstract void DoStep3();                             //可变的,实现延迟至子类。

        //钩子方法。固定出现在某个地方,一般不是重要的关键步骤,父类可选择有实现或无实现,子类可选择重写或不重写。
        protected virtual void Hook() { Debug.Log("DoAfter3AndBefor4_XXX"); }  

        private void DoStep4() { Debug.Log("DoStep4"); }            //不变的。
    }

    //具体类A
    public class ConcreteClassA : AbstractClass
    {
        public override void DoStep2()
        {
            Debug.Log("DoStep2_A");
        }

        public override void DoStep3()
        {
            Debug.Log("DoStep3_A");
        }
    }
    
    //具体类B
    public class ConcreteClassB : AbstractClass
    {
        public override void DoStep2()
        {
            Debug.Log("DoStep2_B");
        }

        public override void DoStep3()
        {
            Debug.Log("DoStep3_B");
        }

        sealed protected override void Hook()          //可看情况,选择设置为密封方法
        {
            Debug.Log("DoAfter3AndBefor4_B");
        }
    }
    
    public class Client
    {
        static public void Main()
        {
            new ConcreteClassA().TemplateMethod();  //输出 DoStep1 DoStep2_A DoStep3_A DoAfter3AndBefor4_XXX DoStep4 
            new ConcreteClassB().TemplateMethod();  //输出 DoStep1 DoStep2_B DoStep3_B DoAfter3AndBefor4_B DoStep4 
        }
    }
}

猜你喜欢

转载自blog.csdn.net/NRatel/article/details/84328969