概述
我們知道挪捕,OOP三個基本特征是:封裝粗梭、繼承、多態(tài)级零。通過繼承断医,我們可以基于差異編程,也就是說奏纪,對于一個滿足我們大部分需求的類鉴嗤,可以創(chuàng)建它的一個子類并只改變我們不期望的那部分。但是在實際使用中序调,繼承很容易被過度使用醉锅,并且過度使用的代價是比較高的,所以我們減少了繼承的使用发绢,使用組合或委托代替
優(yōu)先使用對象組合而不是類繼承
在本文中硬耍,我們會分別介紹模板方法模式
和策略模式
,這兩個模式分別使用了繼承和委托兩種方式边酒。這兩種模式解決的問題是類似的经柴,經(jīng)常可以互換使用墩朦,它們都可以分離通用的算法和具體的上下文坯认。比如我們有一個通用的算法,算法有不同的實現(xiàn)方式,為了遵循依賴倒置原則牛哺,我們希望算法不依賴于具體實現(xiàn)陋气。
本文冒泡排序法來進行舉例說明:
/**
* @author HansChen
*/
public class Sorter {
/**
* 冒泡排序
*/
public int sort(int[] array) {
int operations = 0;
if (array.length <= 1) {
return operations;
}
for (int i = 0; i < array.length - 1; i++) {
for (int j = 0; j < array.length - i - 1; j++) {
operations++;
if (needSwap(array, j)) {
swap(array, j);
}
}
}
return operations;
}
/**
* @return 是否需要交換數(shù)組中 index 和 index+1 元素
*/
private boolean needSwap(int[] array, int index) {
return array[index] > array[index + 1];
}
/**
* 交換array數(shù)組中的 index 和 index+1 元素
*/
private void swap(int[] array, int index) {
int temp = array[index];
array[index] = array[index + 1];
array[index + 1] = temp;
}
}
這是我們實現(xiàn)的冒泡排序算法,這個sort方法可以對int數(shù)組進行排序荆隘。但我們發(fā)現(xiàn)恩伺,這種寫法的擴展性是不強的,如果我們要實現(xiàn)double數(shù)組排序呢椰拒?如果我們需要排序的是一個對象數(shù)組晶渠?難道需要各自定義一個方法嗎?如果它們都使用冒泡排序算法燃观,那么sort的算法邏輯肯定是相似的褒脯,有沒有一種方法能讓這個算法邏輯復(fù)用呢?下面用模板方法模式和策略模式對它進行改造
模板方法模式
模板方法模式:定義一個算法的骨架缆毁,將骨架中的特定步驟延遲到子類中番川。模板方法模式使得子類可以不改變算法的結(jié)構(gòu)即可重新定義該算法的某些特定步驟
下圖是用模板方法模式對冒泡排序重構(gòu)后的結(jié)構(gòu)圖:
首先,我們在BubbleSorter的sort方法中定義算法骨架脊框,再定義一些延遲到子類中的抽象方法:
/**
* @author HansChen
*/
public abstract class BubbleSorter<T> {
/**
* 冒泡排序
*/
public int sort(T array) {
setArray(array);
int length = getLength();
int operations = 0;
if (length <= 1) {
return operations;
}
for (int i = 0; i < length - 1; i++) {
for (int j = 0; j < length - i - 1; j++) {
operations++;
if (needSwap(j)) {
swap(j);
}
}
}
return operations;
}
/**
* 初始化排序數(shù)組
*/
protected abstract void setArray(T array);
/**
* @return 返回數(shù)組長度
*/
protected abstract int getLength();
/**
* @return 是否需要交換數(shù)組中 index 和 index+1 元素
*/
protected abstract boolean needSwap(int index);
/**
* 交換array數(shù)組中的 index 和 index+1 元素
*/
protected abstract void swap(int index);
}
有了BubbleSorter
類颁督,我們就可以創(chuàng)建任意不同類型的對象排序的簡單派生類,比如創(chuàng)建IntBubbleSorter
去排序整型數(shù)組:
public class IntBubbleSorter extends BubbleSorter<int[]> {
private int[] array;
@Override
protected void setArray(int[] array) {
this.array = array;
}
@Override
protected int getLength() {
return array == null ? 0 : array.length;
}
@Override
protected boolean needSwap(int index) {
return array != null && (array[index] > array[index + 1]);
}
@Override
protected void swap(int index) {
int temp = array[index];
array[index] = array[index + 1];
array[index + 1] = temp;
}
}
再比如創(chuàng)建DoubleBubbleSorter
去排序雙精度型數(shù)組:
public class DoubleBubbleSorter extends BubbleSorter<double[]> {
private double[] array;
@Override
protected void setArray(double[] array) {
this.array = array;
}
@Override
protected int getLength() {
return array == null ? 0 : array.length;
}
@Override
protected boolean needSwap(int index) {
return array != null && (array[index] > array[index + 1]);
}
@Override
protected void swap(int index) {
double temp = array[index];
array[index] = array[index + 1];
array[index + 1] = temp;
}
}
甚至我們不僅限于對數(shù)組排序浇雹,還可以對List集合排序沉御,比如創(chuàng)建IntegerListBubbleSorter
對List集合進行冒泡排序:
public class IntegerListBubbleSorter extends BubbleSorter<List<Integer>> {
private List<Integer> list;
@Override
protected void setArray(List<Integer> list) {
this.list = list;
}
@Override
protected int getLength() {
return list == null ? 0 : list.size();
}
@Override
protected boolean needSwap(int index) {
return list != null && (list.get(index) > list.get(index + 1));
}
@Override
protected void swap(int index) {
int temp = list.get(index);
list.set(index, list.get(index + 1));
list.set(index + 1, temp);
}
}
定義上述類之后,我們看下怎么使用上面的類:
public class Test {
public static void main(String[] args) {
//對整型數(shù)組排序
int[] intArray = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
int operations = new IntBubbleSorter().sort(intArray);
System.out.println("[Template Method] operations:" + operations + ", array:" + Arrays.toString(intArray));
//對double數(shù)組排序
double[] doubleArray = {9.9, 8.8, 7.7, 6.6, 5.5, 4.4, 3.3, 2.2, 1.1, 0.0};
operations = new DoubleBubbleSorter().sort(doubleArray);
System.out.println("[Template Method] operations:" + operations + ", array:" + Arrays.toString(doubleArray));
//對List集合排序
List<Integer> list = Arrays.asList(9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
operations = new IntegerListBubbleSorter().sort(list);
System.out.println("[Template Method] operations:" + operations + ", list:" + list.toString());
}
}
模板方法模式展示了經(jīng)典重用的一種形式昭灵,通用算法被放在基類中吠裆,通過繼承在不同的子類中實現(xiàn)該通用算法。我們通過定義通用類BubbleSorter烂完,把冒泡排序的算法骨架放在基類试疙,然后實現(xiàn)不同的子類分別對int數(shù)組、double數(shù)組抠蚣、List集合進行排序祝旷。但這樣是有代價的,因為繼承是非常強的關(guān)系嘶窄,派生類不可避免地與基類綁定在一起了缓屠。但如果我現(xiàn)在需要用快速排序而不是冒泡排序來進行排序,但快速排序卻沒有辦法重用setArray
护侮、getLength
、needSwap
和swap
方法了储耐。不過羊初,策略模式提供了另一種可選的方案
策略模式
策略模式屬于對象的行為模式。其用意是針對一組算法,將每一個算法封裝到具有共同接口的獨立的類中长赞,從而使得它們可以相互替換晦攒,下面用策略模式對冒泡排序進行重構(gòu)
下圖是用策略模式對冒泡排序重構(gòu)后的結(jié)構(gòu)圖:
首先定義一個BubbleSorter類,它持有一個抽象策略接口:
public class BubbleSorter<T> {
/**
* 抽象策略接口得哆,可以有不同的實現(xiàn)
*/
private SortHandler<T> sortHandler;
public BubbleSorter(SortHandler<T> sortHandler) {
this.sortHandler = sortHandler;
}
/**
* 冒泡排序
*/
public int sort(T array) {
sortHandler.setArray(array);
int length = sortHandler.getLength();
int operations = 0;
if (length <= 1) {
return operations;
}
for (int i = 0; i < length - 1; i++) {
for (int j = 0; j < length - i - 1; j++) {
operations++;
if (sortHandler.needSwap(j)) {
sortHandler.swap(j);
}
}
}
return operations;
}
}
定義抽象策略接口:
public interface SortHandler<T> {
/**
* 初始化排序數(shù)組
*/
void setArray(T array);
/**
* @return 返回數(shù)組長度
*/
int getLength();
/**
* @return 是否需要交換數(shù)組中 index 和 index+1 元素
*/
boolean needSwap(int index);
/**
* 交換array數(shù)組中的 index 和 index+1 元素
*/
void swap(int index);
}
創(chuàng)建具體的策略類IntSortHandler
對整型數(shù)組進行操作:
public class IntSortHandler implements SortHandler<int[]> {
private int[] array;
@Override
public void setArray(int[] array) {
this.array = array;
}
@Override
public int getLength() {
return array == null ? 0 : array.length;
}
@Override
public boolean needSwap(int index) {
return array != null && (array[index] > array[index + 1]);
}
@Override
public void swap(int index) {
int temp = array[index];
array[index] = array[index + 1];
array[index + 1] = temp;
}
}
創(chuàng)建具體的策略類DoubleSortHandler
對雙精度型數(shù)組進行操作:
public class DoubleSortHandler implements SortHandler<double[]> {
private double[] array;
@Override
public void setArray(double[] array) {
this.array = array;
}
@Override
public int getLength() {
return array == null ? 0 : array.length;
}
@Override
public boolean needSwap(int index) {
return array != null && (array[index] > array[index + 1]);
}
@Override
public void swap(int index) {
double temp = array[index];
array[index] = array[index + 1];
array[index + 1] = temp;
}
}
創(chuàng)建具體的策略類IntegerListSortHandler
對List集合進行操作:
public class IntegerListSortHandler implements SortHandler<List<Integer>> {
private List<Integer> list;
@Override
public void setArray(List<Integer> list) {
this.list = list;
}
@Override
public int getLength() {
return list == null ? 0 : list.size();
}
@Override
public boolean needSwap(int index) {
return list != null && (list.get(index) > list.get(index + 1));
}
@Override
public void swap(int index) {
int temp = list.get(index);
list.set(index, list.get(index + 1));
list.set(index + 1, temp);
}
}
定義上述類之后脯颜,我們看下怎么使用策略模式
public class Test {
public static void main(String[] args) {
//對整型數(shù)組排序
int[] intArray = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
BubbleSorter<int[]> intBubbleSorter = new BubbleSorter<>(new IntSortHandler());
int operations = intBubbleSorter.sort(intArray);
System.out.println("[Strategy] operations:" + operations + ", array:" + Arrays.toString(intArray));
//對double數(shù)組排序
double[] doubleArray = {9.9, 8.8, 7.7, 6.6, 5.5, 4.4, 3.3, 2.2, 1.1, 0.0};
BubbleSorter<double[]> doubleBubbleSorter = new BubbleSorter<>(new DoubleSortHandler());
operations = doubleBubbleSorter.sort(doubleArray);
System.out.println("[Strategy] operations:" + operations + ", array:" + Arrays.toString(doubleArray));
//對List集合排序
List<Integer> list = Arrays.asList(9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
BubbleSorter<List<Integer>> integerListBubbleSorter = new BubbleSorter<>(new IntegerListSortHandler());
operations = integerListBubbleSorter.sort(list);
System.out.println("[Strategy] operations:" + operations + ", list:" + list);
}
}
策略模式不是將通用方法放到基類中,而是把它放進BubbleSorter
的sort方法中贩据,把排序算法中必須調(diào)用的抽象方法定義在SortHandler
接口中栋操,從這個接口中派生出不同的子類。把派生出的子類傳給BubbleSorter后饱亮,sort方法就可以把具體工作委托給接口去完成矾芙。注意:SortHandler對BubbleSorter是一無所知的,它不依賴于冒泡排序的具體實現(xiàn)近上,這個和模板方法模式是不同的剔宪。如果其他排序算法也需要用到SortHandler,完全也可以在相關(guān)的排序算法中使用SortHandler
總結(jié)
模板方法模式和策略模式都可以用來分離高層的算法和低層的具體實現(xiàn)細節(jié)壹无,都允許高層的算法獨立于它的具體實現(xiàn)細節(jié)重用葱绒。但策略模式還有一個額外的好處就是允許具體實現(xiàn)細節(jié)獨立于高層的算法重用,但這也以一些額外的復(fù)雜性斗锭、內(nèi)存以及運行事件開銷作為代價
文中示例代碼下載:https://github.com/shensky711/awesome-demo/tree/master/Patterns