zoukankan      html  css  js  c++  java
  • 模板方法模式

    模板方法模式

    模板方法模式(Template Method Pattern)是很重要的一种设计模式,它具体属于行为模式中的一种。

    模板方法模式在一个操作中定义了一个算法流程的大致骨架,而这些算法流程骨架中包含的一些步骤被推迟到子类去实现。这样就可以在保证算法既定流程步骤不变的情况下,给与了子类重定义算法表现的机会。

    模板方法模式符合面向对象设计的对扩展开放,对修改关闭的原则。

    模板方法实际就是利用抽象、继承等概念的一种常见的编程模式,体现了面向对象编程的主要特性。

    该模式通常会涉及到一个抽象类,抽象类里面包含一个或多个Template Method,以及几个必须被子类重写抽象Primitive Method,其中Template Method调用这些Primitive Method来实现算法的基本流程。另外,抽象类可能还包含一些非抽象的Hook Method,子类可以选择性的重写这些Hook方法以达到扩展功能的目的,但是对于Primitive Method,子类是必须重写的。

    实现说明

    1. 基于上述说明,在Java等面向对象的语言中,可以将其Template Method声明为final的方法防止被子类重写,Primitive Method声明为abstract方法强制子类重写,而Hook Method则声明为普通的非抽象方法,默认实现是空。
    2. Primitive Method的方法(需要被重写)数量尽可能少,否则会给子类带来很多繁琐的实现。
    3. 命令规范,为了和其他的方法区分开来,Prmitive Methond通常命名为doXXX(),这样就暗示这个方法是子类必须要重写的,且会影响整个Template Method执行效果。

    UML类图

    UML类图如下:

    这里写图片描述

    实例

    
    public abstract class AbstractApplication {
        //primitive method
        protected abstract Document doCreateDocument();
        protected abstract boolean canOpenDocument(String name);
    
        //hook method
        public void aboutToOpenDocument(Document doc) {
            //do nothing
        }
    
        //template method
        public final void openDocument(String name) {
            //step1
            if (!canOpenDocument(name)) {//调用Primitive Method
                System.out.println("can not open document!");
                return;
            }
    
            //step2
            Document doc = doCreateDocument();//调用Primitive Method
            if (doc != null) {
                aboutToOpenDocument(doc);//调用hook方法
                doc.open();
                doc.doRead();
            }
        }
    }
    
    public abstract class Document {
    
        //primitive method
        public abstract void doRead();
        protected abstract String getType();
        protected abstract String getName();
    
        //template method
        public void open() {
            System.out.println("is openning document, please waiting ....");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(String.format("open document[name=%s, type=%s]", getName(), getType()));
        }
    }
    
    public class ImageApplication extends AbstractApplication {
    
        private String name = "";
    
        @Override
        protected Document doCreateDocument() {
            // TODO Auto-generated method stub
            return new ImageDocument(name);
        }
    
        @Override
        protected boolean canOpenDocument(String name) {
            // TODO Auto-generated method stub
            if (name != null && (name.contains(".jpg") || name.contains(".png") || name.contains(".gif"))) {
                this.name = name;
                return true;
            }
            return false;
        }
    
        //optional hook method
        @Override
        public void aboutToOpenDocument(Document doc) {
            // TODO Auto-generated method stub
            System.out.println("I am hooking aboutToOpenDocument()");
            super.aboutToOpenDocument(doc);
        }
    }
    
    public class ImageDocument extends Document {
        private String name = "";
        public static final String TYPE = "IMAGE"; 
    
        public ImageDocument(String name) {
            this.name = name;
        }
    
        @Override
        public void doRead() {
            // TODO Auto-generated method stub
            System.out.println("I am reading " + this.name + ", type : " + TYPE);
        }
    
        @Override
        protected String getType() {
            // TODO Auto-generated method stub
            return TYPE;
        }
    
        @Override
        protected String getName() {
            // TODO Auto-generated method stub
            return name;
        }
    
    }
    
    public class Main {
        public static void main(String[] args) {
            AbstractApplication app = new ImageApplication();
            app.openDocument("spground.jpg");
        }
    }

    总结

    模板方法很重要,其将不变(invariants)的部分封装在模板方法的调用流程中,而将变化(variants)的不拆分为一些基本步骤Primitive Method以供子类去实现,既对外提供了统一的功能接口,又保证了功能的可扩展性。

    模板方法的使用关键在于如何将可变和不可变的部分区分开来,个人认为模板方法模式的使用场景通常是在框架编写方面,编码人员的代码主要是用来供给用户使用或者扩展。

    References

    1. 《设计模式:可复用面向对象软件的基础》
  • 相关阅读:
    Containers vs Serverless:本质区别是什么?
    博云 x 某农商行 | 银行信息化运维系统升级的最佳实践
    兼容传统应用,蓄力边缘云——博云胖容器解决方案
    使用Bookinfo应用测试Kuma服务网格
    干货 | 博云基于OVS自研容器网络插件在金融企业的落地实践
    开源分布式事务中间件Seata使用指南
    调研 | 5种分布式事务解决方案优缺点对比
    Ambassador,云原生应用的“门神”
    容器网络插件那么多,博云为什么基于OVS深度自研?
    观察 | 边缘云计算的概念理解
  • 原文地址:https://www.cnblogs.com/Spground/p/9567889.html
Copyright © 2011-2022 走看看