设计模式之模板方法浅析

/**
 * 
 * 设计原则:
 *   好莱坞原则:别调用我,我会调用你
 *   好莱坞法则的目的在于:防止依赖腐败  让高层组件调用底层组件
 *   
 * 模板方法模式: 定义了一个算法的框架,允许子类为其提供一个或多个步骤的实现
 *   模板方法和策略方法都封装算法,模板使用继承,策略使用组合
 *   工厂方法为模板方法的特例
 * 
 * 钩子:
 *   被声明在抽象类中的方法,但是有默认的或者空的实现,给子类决定要不要重写
 *   让子类决定算法中的某些部分是否需要
 * 
 * 示例: 咖啡和茶的冲泡过程 
 * 咖啡:把水煮开、用沸水冲泡咖啡、把咖啡倒进杯子、加糖和牛奶 
 * 茶叶:把水煮开、用沸水浸泡茶叶、把茶倒进杯子、加柠檬
 * 
 * @author Administrator
 * 
 */


模板方法示例采用咖啡和茶叶的冲泡流程

  咖啡和茶叶都含有咖啡因 都称为咖啡因饮料

package com.undergrowth.template;

/**
 * 
 * 设计原则:
 *   好莱坞原则:别调用我,我会调用你
 *   好莱坞法则的目的在于:防止依赖腐败  让高层组件调用底层组件
 *   
 * 模板方法模式: 定义了一个算法的框架,允许子类为其提供一个或多个步骤的实现
 *   模板方法和策略方法都封装算法,模板使用继承,策略使用组合
 *   工厂方法为模板方法的特例
 * 
 * 钩子:
 *   被声明在抽象类中的方法,但是有默认的或者空的实现,给子类决定要不要重写
 *   让子类决定算法中的某些部分是否需要
 * 
 * 示例: 咖啡和茶的冲泡过程 
 * 咖啡:把水煮开、用沸水冲泡咖啡、把咖啡倒进杯子、加糖和牛奶 
 * 茶叶:把水煮开、用沸水浸泡茶叶、把茶倒进杯子、加柠檬
 * 
 * @author Administrator
 * 
 */
public abstract class CaffeineBeverage {

	/**
	 * 茶和咖啡的冲泡算法 模板方法声明为final 不允许子类重写
	 */
	public final void prepareRecipe() {
		boilWater();
		brew();
		pourInCup();
		//加上钩子 控制步骤
		if(isAddCondiment()){
			addCondiments();
		}
		
	}

	/**
	 * 钩子方法
	 * @return
	 */
	 boolean isAddCondiment() {
		// TODO Auto-generated method stub
		return true;
	}

	/**
	 * 相同步骤
	 */
	void boilWater() {
		System.out.println("把水煮开");
	}

	/**
	 * 冲泡方式不一样 留给子类实现
	 */
	abstract void brew();

	void pourInCup() {
		// TODO Auto-generated method stub
		System.out.println("把饮料倒进杯子");
	}

	/**
	 * 加调料不一样 留给子类实现
	 */
	abstract void addCondiments();

}

咖啡

package com.undergrowth.template;

public class Coffee extends CaffeineBeverage {

	@Override
	void brew() {
		// TODO Auto-generated method stub
		System.out.println("用沸水冲泡咖啡");
	}

	@Override
	void addCondiments() {
		// TODO Auto-generated method stub
		System.out.println("加糖和牛奶");
	}

}


package com.undergrowth.template;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Tea extends CaffeineBeverage {

	@Override
	void brew() {
		// TODO Auto-generated method stub
		System.out.println("用沸水浸泡茶叶");
	}

	@Override
	void addCondiments() {
		// TODO Auto-generated method stub
		System.out.println("加柠檬");
	}

	/**
	 * 重写钩子方法
	 */
	@Override
	boolean isAddCondiment() {
		// TODO Auto-generated method stub
		boolean isAdd=true;
		String input=getInput();
		if(!"y".equalsIgnoreCase(input)){
			isAdd=false;
		}
		return isAdd;
	}

	private String getInput() {
		// TODO Auto-generated method stub
		String input=null;
		System.out.println("是否想要在茶中添加饮料");
		BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
		try {
			input=reader.readLine();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return input;
	}
    
	
}

测试

package com.undergrowth.template.test;

import static org.junit.Assert.*;

import org.junit.Test;

import com.undergrowth.template.CaffeineBeverage;
import com.undergrowth.template.Coffee;
import com.undergrowth.template.Tea;

public class CaffeineBeverageTest {

	@Test
	public void test() {
		CaffeineBeverage baBeverage=new Tea();
		System.out.println("开始泡茶");
		baBeverage.prepareRecipe();
		baBeverage=new Coffee();
		System.out.println("==================开始冲泡咖啡==================");
		baBeverage.prepareRecipe();
	}

}

控制台输出

开始泡茶
把水煮开
用沸水浸泡茶叶
把饮料倒进杯子
是否想要在茶中添加饮料
Y
加柠檬
==================开始冲泡咖啡==================
把水煮开
用沸水冲泡咖啡
把饮料倒进杯子
加糖和牛奶


再来看看模板方法的一个变体的应用   

  Arrays.sort()方法  比较鸭子的体重 然后进行排序

package com.undergrowth.template;

/**
 * 鸭子比较器
 * @author Administrator
 *
 */
public class DuckComparable implements Comparable<Object> {

	int weight;
	String name;
	
	public DuckComparable(int weight, String name) {
		super();
		this.weight = weight;
		this.name = name;
	}
    
	/**
	 * 比较两个鸭子的体重 是否相等
	 */
	@Override
	public int compareTo(Object o) {
		// TODO Auto-generated method stub
		DuckComparable otherDuckComparable=(DuckComparable) o;
		if(this.weight>otherDuckComparable.weight) return 1;
		else if(this.weight==otherDuckComparable.weight) return 0;
		else {
		  return -1;	
		}
	}



	@Override
	public String toString() {
		return "DuckComparable [weight=" + weight + ", name=" + name + "]";
	}

}


测试

package com.undergrowth.template.test;

import static org.junit.Assert.*;

import java.util.Arrays;

import org.junit.Test;

import com.undergrowth.template.DuckComparable;

public class DuckComparableTest {

	@Test
	public void test() {
		DuckComparable[] ducks = { new DuckComparable(100, "绿头鸭"),
				new DuckComparable(85, "红头鸭"), new DuckComparable(110, "黑头鸭") 
		, new DuckComparable(57, "蓝头鸭")};
		System.out.println("===================未排序的鸭子===================");
		display(ducks);
		System.out.println("===================已排序的鸭子===================");
		Arrays.sort(ducks);
		display(ducks);
	}

	private void display(DuckComparable[] ducks) {
		// TODO Auto-generated method stub
		for (int i = 0; i < ducks.length; i++) {
			System.out.println(ducks[i]);
		}
	}
	
	

}

控制台

===================未排序的鸭子===================
DuckComparable [weight=100, name=绿头鸭]
DuckComparable [weight=85, name=红头鸭]
DuckComparable [weight=110, name=黑头鸭]
DuckComparable [weight=57, name=蓝头鸭]
===================已排序的鸭子===================
DuckComparable [weight=57, name=蓝头鸭]
DuckComparable [weight=85, name=红头鸭]
DuckComparable [weight=100, name=绿头鸭]
DuckComparable [weight=110, name=黑头鸭]


现在再来追踪看下 Arrays.sort方法 是如何实现的

/**
     * Sorts the specified array of objects into ascending order, according
     * to the {@linkplain Comparable natural ordering} of its elements.
     * All elements in the array must implement the {@link Comparable}
     * interface.  Furthermore, all elements in the array must be
     * <i>mutually comparable</i> (that is, {@code e1.compareTo(e2)} must
     * not throw a {@code ClassCastException} for any elements {@code e1}
     * and {@code e2} in the array).
     *

  public static void sort(Object[] a) {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a);
        else
            ComparableTimSort.sort(a);
    }
上面是说以升序的方式进行排序 并且数组中的每个元素必须要实现Comparable接口

往下看  ComparableTimSort类的

/*
     * The next two methods (which are package private and static) constitute
     * the entire API of this class.  Each of these methods obeys the contract
     * of the public method with the same signature in java.util.Arrays.
     */

    static void sort(Object[] a) {
          sort(a, 0, a.length);
    }

static void sort(Object[] a, int lo, int hi) {
        rangeCheck(a.length, lo, hi);
        int nRemaining  = hi - lo;
        if (nRemaining < 2)
            return;  // Arrays of size 0 and 1 are always sorted

        // If array is small, do a "mini-TimSort" with no merges
        if (nRemaining < MIN_MERGE) {
            int initRunLen = countRunAndMakeAscending(a, lo, hi);
            binarySort(a, lo, hi, lo + initRunLen);
            return;
        }

        /**
         * March over the array once, left to right, finding natural runs,
         * extending short natural runs to minRun elements, and merging runs
         * to maintain stack invariant.
         */
        ComparableTimSort ts = new ComparableTimSort(a);
        int minRun = minRunLength(nRemaining);
        do {
            // Identify next run
            int runLen = countRunAndMakeAscending(a, lo, hi);

            // If run is short, extend to min(minRun, nRemaining)
            if (runLen < minRun) {
                int force = nRemaining <= minRun ? nRemaining : minRun;
                binarySort(a, lo, lo + force, lo + runLen);
                runLen = force;
            }

            // Push run onto pending-run stack, and maybe merge
            ts.pushRun(lo, runLen);
            ts.mergeCollapse();

            // Advance to find next run
            lo += runLen;
            nRemaining -= runLen;
        } while (nRemaining != 0);

        // Merge all remaining runs to complete sort
        assert lo == hi;
        ts.mergeForceCollapse();
        assert ts.stackSize == 1;
    }


看到binarySort方法

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
            int left = lo;
            int right = start;
            assert left <= right;
            /*
             * Invariants:
             *   pivot >= all in [lo, left).
             *   pivot <  all in [right, start).
             */
            while (left < right) {
                int mid = (left + right) >>> 1;
                if (pivot.compareTo(a[mid]) < 0)
                    right = mid;
                else
                    left = mid + 1;
            }
            assert left == right;

            /*
             * The invariants still hold: pivot >= all in [lo, left) and
             * pivot < all in [left, start), so pivot belongs at left.  Note
             * that if there are elements equal to pivot, left points to the
             * first slot after them -- that's why this sort is stable.
             * Slide elements over to make room for pivot.
             */
            int n = start - left;  // The number of elements to move
            // Switch is just an optimization for arraycopy in default case
            switch (n) {
                case 2:  a[left + 2] = a[left + 1];
                case 1:  a[left + 1] = a[left];
                         break;
                default: System.arraycopy(a, left, a, left + 1, n);
            }
            a[left] = pivot;
        }
    }

这里的

 if (pivot.compareTo(a[mid]) < 0)
即使调用实现Comparable接口的compareTo方法 进行比较

posted on 2014-12-15 22:37  liangxinzhi  阅读(134)  评论(0编辑  收藏  举报