[设计模式总结] 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.

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


类图

[设计模式总结] 7. 模板方法模式 -封装算法

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。

 










 

你可能感兴趣的:(设计模式)