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