zoukankan      html  css  js  c++  java
  • 设计模式系列之Template Method(模板方法模式)

         在面向对象设计中,无非就是封装,继承之类的运用,而设计出可扩展性好,代码冗余少,可重用性高的程序也是衡量面向对象开发的一个标准。面向对象使用相当灵活,虽然核心特征只有封装,继承,多态,但是通过对它们的灵活配合和使用变化出了各种设计模式。可以说,设计模式是对面向对象的一些经验总结,今天将看到其中的一个,Template Method(模板方法模式)。

    Template Method(模板方法模式) 

        定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Te m p l a t e M e t h o d使得子类

    可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 (摘自:《设计模式:可复用面向对象软件的基础》)

     

        那么如何来理解上述的定义呢?可以这样想,模板方法,说到底也是一个方法,而它的作用正如其名--模板。在这个模板里面,定义了一系列连续的动作。这样,当调用这个模板方法后,其内部的方法将被按照顺序调用。但是,仅此而已的话,只是对一系列方法进行了封装,根本没有体现面向对象,其关键在定义的后半句,将一些步骤延迟到子类中。模板方法为了减少代码冗余,将子类中相同的操作提取出来,放到抽象父类中,这些方法我们叫做具体方法(Concrete Method),而如果是不同的操作的话,就在抽象类中定义它们的抽象方法(Abstract Method),把具体的实现延迟到子类中去。除此之外,在抽象父类中还有一类方法,这类方法在需要的时候,可以在子类中实现它们,与抽象方法不同的是在子类中比较自由,没有强制性要求实现,此类方法叫做钩子方法(HookMethod),为了区别抽象方法,在定义时一般以Do开头,例如DoCloseDocument();上述三类方法都将被放到模板方法中(Template Method)。这样,在客户端中,我们调用模板方法,模板方法调用将是不变的,因为在模板方法中的算法结构是固定不变的,但是可以通过实现不同的子类方法,重定义算法的某些特定步骤。

     
         上述描述的有点多,看着晕了,还是以一个简单的例子再来理解下:

    如何把大象放进冰箱 

          记得这是赵本山很久以前的一个小品《钟点工》里的一个例子,虽然当年这个节目的时候,我年纪还很小,但是竟然还印象深刻。这里,就用这个例子来演示模板方法模式。


    一、分析此过程

     通过此例子,我们知道,把大象放进冰箱,需要三步(不考虑冰箱中是否有长颈鹿或者冰箱已经满了,我们忽略这些):

    1.打开冰箱

    2.把大象放进去

    3.关上冰箱

    二、抽象出更抽象的过程

        可以放大象,当然可以放其它东西,比如放小鸡,放长颈鹿等等,所有的这些东西我们就叫东西,这话听怎么这么奇怪,那就书面化点,所有的这些东西我们叫做对象。

    这样,抽象出的一个基类可以叫做:Abstract class PutSomethingIntoFridge{}

     然后,在基类中我们把东西放进冰箱的一系列算法步骤写入Put()的模板方法中。

    然后....此处省略100个字,具体看下文代码 

     三、继承父类,实现各种东西放进冰箱


    设计完成后,整个结构如下图所示:

     

    示例代码:

    PutSomethingIntoFridge.cs
    using System;

    namespace PatternDemo
    {
        
    abstract class PutSomethingIntoFridge
        {
            
    //模板方法
            public void Put()
            {
                
    //具体方法
                OpenFridge();
                
    //Hook方法,扩大冰箱容量
                DoEnlargeCapacity();
                
    //抽象方法
                PutInto();
                
    //具体方法
                CloseFridge();
            
            }

            
    //具体方法,无需在子类中重新实现
            protected  void OpenFridge()
            {
                Console.WriteLine(
    "打开冰箱");
            
            }

            
    //具体方法,无需在子类中重新实现
            protected void CloseFridge()
            {

                Console.WriteLine(
    "关上冰箱");
            
            }

            
    //抽象方法,在子类中强制实现
            abstract protected void PutInto();

            
    //Hook方法,扩大冰箱容量,此方法在子类中可选择实现。换句话说,只有在必要的时候才重写实现它
           virtual protected void DoEnlargeCapacity(){}

        }
    }

     PutChickenIntoFridge.cs

    using System;
    namespace PatternDemo
    {
        
    class PutChickenIntoFridge : PutSomethingIntoFridge
        {
            
    //普通冰箱能放下一只鸡,当然是在冰箱没有满的情况下,所以只要实现具体放的东西方法就行
            protected override void PutInto()
            {
                Console.WriteLine(
    "把鸡放进冰箱");
            }
        }
    }


    PutElephantIntoFridge
    using System;

    namespace PatternDemo
    {
        
    class PutElephantIntoFridge : PutSomethingIntoFridge
        {
            
    //由于大象太大,普通冰箱无法放进去,需要扩大冰箱容量,所以实现DoEnlargeCapacity方法
            protected override void DoEnlargeCapacity()
            {
                Console.WriteLine(
    "扩大冰箱容量10000倍");
            }

            
    protected override void PutInto()
            {
                Console.WriteLine(
    "把大象放进冰箱");
            }
        }

    Client
    namespace PatternDemo
    {
        
    class Client
        {
            
    static void Main(string[] args)
            {
                PutSomethingIntoFridge putchicken 
    = new PutChickenIntoFridge();
                putchicken.Put();

                PutSomethingIntoFridge putelephant 
    = new PutElephantIntoFridge();
                putelephant.Put();
                
            }
        }



    对与Template Method(模板方法)的一些总结

     


     模板方法是一种代码复用的基本技术,它们在类库中尤为重要,它们提取了类库中的公共行为。

     模板方法的适用情况:

    • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
    • 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。首先识别
    现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的
    操作的模板方法来替换这些不同的代码。
    • 控制子类扩展。模板方法只在特定点调用“ h o o k”操作(例如这里举例的当冰箱装不下大象这样的特殊情况时),这样就只允
    许在这些点进行扩展。

     需要注意的地方:

    1.为了保证模板方法中调用的各种原语操作(抽象方法,具体方法,Hook方法)只能被模板方法调用,访问权限应为protected。

    2.尽量减少原语操作。以防客户端代码冗长。

    3.避免乱用模板方法,导致子类泛滥,应根据具体情况适时的使用。

     

     参考资料

    《设计模式:可复用面向对象软件的基础》

    作者:Sirk  
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

     
  • 相关阅读:
    groovy execute
    forwarding
    C++实用技巧(一) λcalculus(惊愕到手了欧耶,GetBlogPostIds.aspx) C++博客
    lua与c若干问题 专职C++ C++博客
    [精华] kingate代理服务器指南
    Linux下的静态库和动态库 yg2362 C++博客
    069 问,lua程序设计(第四部分)笔记三,c++调用lua函数 everettjf C++博客
    partition list Groovy builtin to split an array into equal sized subarrays? Stack Overflow
    λcalculus(惊愕到手了欧耶,GetBlogPostIds.aspx) C++博客
    GDB调试使用技巧 专职C++ C++博客
  • 原文地址:https://www.cnblogs.com/vimsk/p/2072620.html
Copyright © 2011-2022 走看看