zoukankan      html  css  js  c++  java
  • [设计模式总结] 7. 模板方法模式 封装算法

    引子

    例如有两个处理逻辑:泡茶、冲咖啡;

    他们的基本流程(算法)是相同的:煮开水、冲泡、倒进杯子、加入调料。只不过具体到个别步骤可能有差异。

    如果分成两个类来实现,就会存在重复代码

    ——可以将公共的部分(算法)提到父类中;由各个子类实现每个具体步骤。


    定义

    Define the skeleton of an algorithm in an operation, deferring some steps to subclasses.

    Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.

    模板方法模式在一个方法中定义算法的框架,而将一些算法步骤延迟到子类中定义。使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。——即,封装算法


    类图


    public abstract class AbstractClass {
        //template method ----final
        final void templateMethod() {
            primitiveOperation1();
            primitiveOperation2();
            concreteOperation();
            hook();
            if( hook2() ){ //钩子可影响算法行为
    
            }
        }
    
        protected abstract void primitiveOperation1();
        protected abstract void primitiveOperation2();
        final void concreteOperation() { ...... }
        //父类中可以有“默认不做事的方法”;子类可以视情况决定要不要覆盖它
        void hook() { }//empty method
        boolean hook2() { return true; }
     }
    1. 为防止子类改变模板方法中的算法,模板方法一般都为final
    2. 算法中的必需步骤,用abstract;算法中的可选步骤,用hook
    3. 好莱坞原则:在设计模板方法模式时,我们(高层组件)告诉子类(低层组件)“不要调用我们,我们会调用你”


    优点

    1. 封装不变部分,扩展可变部分
    2. 提取公共部分代码,方便维护
    3. 行为由父类控制,子类实现


    缺点

    子类执行的结果会影响父类的结果,子类影响父类


    使用场景

    1. 多个子类有公用方法,并且逻辑基本相同时
    2. 重要的、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能由子类实现
    3. 可用钩子约束算法行为——使用钩子可作为条件控制,影响抽象类中的算法流程

    JDK中的模板方法

    Array.sort()

    // Arrays
       public static void sort(Object[] a) {
                ...
                ComparableTimSort.sort(a);
        }
    
    // ComparableTimSort
       static void sort(Object[] a, int lo, int hi) {
            ...
                binarySort(a, lo, hi, lo + initRunLen);
               
       }
    
        //算法框架
        private static void binarySort(Object[] a, int lo, int hi, int start) {
            assert lo <= start && start <= hi;
            if (start == lo)
                start++;
            for ( ; start < hi; start++) {
                @SuppressWarnings("unchecked")
                Comparable<Object> pivot = (Comparable) a[start];
    
                // Set left (and right) to the index where a[start] (pivot) belongs
                ....
                while (left < right) {
                    int mid = (left + right) >>> 1;
                    if (pivot.compareTo(a[mid]) < 0) //compareTo这个算法步骤,是由各个Comparable的子类定义的
                        right = mid;
                    else
                        left = mid + 1;
                }
                ....
            }
        }
    
    
    


    Q:这个算法框架并不是设计在父类中,而是在一个工具类中

    A:是的,与教科书上模板方法的定义有差异;因为sort要适用于所有数组,所以提供了一个Arrays工具类。但仍然是模板方法模式


    InputStream.read()

     //算法框架
     public int read(byte b[], int off, int len) throws IOException {
            ...
    
            int c = read();
            
            ...
    }
    
    //算法步骤由子类实现
    public abstract int read() throws IOException;
    
    
    

    JFrame.paint()

    // JFrame
        public void update(Graphics g) {
            paint(g);
        }
    public class MyFrame extends JFrame {
    	public MyFrame(){
    		super();
    		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		this.setSize(300,300);
    		this.setVisible(true);
    	}
    	
    	@Override
    	public void paint(Graphics g){ //重定义算法步骤
    		super.paint(g);
    		g.drawString("I rule !", 100, 100);
    	}
    	
    	public static void main(String[] args){
    		MyFrame frame = new MyFrame();
    	}
    
    }


    Applet.init()/start()/stop()/destroy()/paint()

    // Applet
        public void init() { //什么也不做的hook
        }
    // Beans
       public static Object instantiate(ClassLoader cls, String beanName, 
                        BeanContext beanContext, AppletInitializer initializer)
                            throws IOException, ClassNotFoundException {
    
            
    
                    // If it was deserialized then it was already init-ed.
                    // Otherwise we need to initialize it.
    
                    if (!serialized) {
                        // We need to set a reasonable initial size, as many
                        // applets are unhappy if they are started without
                        // having been explicitly sized.
                        applet.setSize(100,100);
                        applet.init(); //调用hook
                    }
    
                    
            }
    
            return result;
        }	


    Applet中的init()/start()/stop()/destroy()/paint()这些方法,都是hook。










  • 相关阅读:
    子数组的最大乘积
    重建二叉树
    只考加法的面试题
    找出发帖的水王问题
    寻找最近点对
    寻找最大的k个数问题
    寻找数组中 的最大值最小值
    数组中的最长递增子序列
    常用的百度API地图操作
    div 背景自适应
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3000348.html
Copyright © 2011-2022 走看看