策略模式在java集合中的TreeSet和TreeMap中获得了很好的应用,咱们能够实现Comparator接口实现Compareto()方法来定义本身的排序规则,而后经过TreeSet,TreeMap构造方法传入实现该接口的实例,map中的顺序就会是咱们自定义的顺序。咱们能够彻底定义本身的规则,用之极为方便。那么,什么是策略模式呢?java
策略模式定义:定义一组算法,将每一个算法都封装起来,而且使它们之间能够转换。策略模式使这些算法在客户端调用时可以互不影响的变化。算法
策略模式组成:编程
1.抽象的策略角色:策略类,一般由一个抽象类或者接口实现。数组
2.具体的策略角色:包装了相关的算法和行为。数据结构
3.环境角色:持有一个策略类的引用,以便最终给客户端调用。ide
策略模式的设计原则:ui
1. 封装变化this
2.使用接口编程spa
策略模式的实现:设计
1.将每一组算法封装到具备共同接口的独立类中,这样不一样的策略能够相互替换
2.算法能够在不影响客户端的状况下发生变化,把行为和环境分开。
3.环境类负责维持和查询行为类,各类算法在具体策略中提供
看文字看蒙了,举一个具体的例子吧,举什么例子呢?想到咱们数据结构中的各类排序,什么冒泡排序,快速排序,堆排序...,他们完成的功能都是同样的,只是排序算法不一样而已。咱们就能够当作是排序的不一样策略。
首先定义咱们的抽象策略:
/** * 抽象的策略类 * @author * */
public interface Strategy { public void sort(int[] array); }
定义具体的策略,这里实现了三种策略,分别是快速排序,堆排序,归并排序。
快速排序策略代码:
public class QuickSort implements Strategy{ @Override public void sort(int[] array) { qpSort(array,0,array.length - 1); } private void qpSort(int array[], int low, int high) { int pos = qkPass(array, low, high); //产生中间第一个肯定的数
if (low < high) { qpSort(array, low, pos - 1);//左边
qpSort(array, pos + 1, high);//右边
} } private int qkPass(int a[], int low, int high) { int x = a[low]; while (low < high) { while (low < high && a[high] > x) { high--; } if (low < high) { a[low] = a[high]; low++; } while (low < high && a[low] < x) { low++; } if (low < high) { a[high] = a[low]; high--; } } a[low] = x; return low; } }
堆排序策略代码:
public class HeapSort implements Strategy { @Override public void sort(int[] array) { heapSort(array,array.length); } private void heapSort(int a[], int length) { crtHeap(a, length); // 建立大根堆
for (int i = length - 1; i > 0; i--) { int b = a[0]; a[0] = a[i]; // 堆顶跟堆尾互换
a[i] = b; sift(a, 0, i - 1); // 使得r[0...i-1]变成堆
} } /** * 创建初堆 * * @param a * 为带排序的数组 * @param length * 为数组长度 */
private void crtHeap(int a[], int length) { int n = length - 1; // 建出堆,从第 n/2 个记录开始进行堆筛选
for (int i = n / 2; i >= 0; i--) { sift(a, i, n); } } /** * * @param r * 数组 * @param k * 表示以r[k]为根的彻底二叉树,调整r[k],使得r[k...m]知足大根堆的性质 * @param m * 堆尾元素下标 */
private void sift(int r[], int k, int m) { int temp = r[k]; // 暂存根记录
int i = k; // 记录初始根的下标
int j = i * 2 + 1; // 根的左孩子,由于下标从0开始
while (j <= m) { // 若是存在右子树,且右子树根的关键字大,则沿右分支筛选,不然沿左分支筛选。由于目的是找一个最大的元素
if (j < m && r[j] < r[j + 1]) { j = j + 1; } if (temp >= r[j]) { break; // 结束筛选
} else { r[i] = r[j]; // 上移
i = j; // 重新定义根下标
j = j * 2 + 1;// 继续筛选
} } r[i] = temp; // 将r[k]移动到适当的位置
} }
归并排序策略代码:
public class MergeSort implements Strategy{ @Override public void sort(int[] array) { int[] temp = new int[array.length]; mSort(array,0,array.length - 1, temp); } /** * * @param r1 等待排序数组 * @param low * @param high * @param r3 将r1排序后的结果放在r3中 r1[low...high],r3[low...high] */
private void mSort(int r1[],int low,int high, int r3[]){ if(low < high) { int mid = (low + high) /2; //归并排序中 拆,合要一块儿进行
mSort(r1,low,mid,r3); mSort(r1,mid+1,high,r3); merge(r1,low, mid, high, r3); } } private void merge(int r1[],int low, int mid, int high, int r2[]){ int i = low; int j = mid + 1; int k = 0; while(i <=mid && j <= high){ if(r1[i] <= r1[j]){ r2[k] = r1[i]; i++; } else{ r2[k] = r1[j]; j++; } k++; } while(i <= mid){ r2[k] = r1[i]; i++; k++; } while(j <= high){ r2[k] = r1[j]; j++; k++; } for(int m = 0; m < k;m++){ r1[low + m] = r2[m]; } } }
定义环境类:
public class Environment { private Strategy strategy; //策略类的引用
public Environment(Strategy strategy){ this.strategy = strategy; } //用于设置不一样的策略
public void setStrategy(Strategy strategy){ this.strategy = strategy; } //实现排序的功能
public void sort(int array[]){ strategy.sort(array); } }
最后定义客户端类:
public class Client { public static void main(String[] args) { int[] array1 = new int[]{48,62,35,77,55,14,35,98}; int[] array2 = new int[]{48,62,35,77,55,14,35,98}; int[] array3 = new int[]{48,62,35,77,55,14,35,98}; //使用堆排序策略
Environment env = new Environment(new HeapSort()); env.sort(array1); System.out.println("使用堆排序array1:"); print(array1); //使用快速排序策略
env.setStrategy(new QuickSort()); env.sort(array2); System.out.println("使用快速排序array2:"); print(array2); //使用归并排序策略
env.setStrategy(new MergeSort()); env.sort(array3); System.out.println("使用归并排序array3:"); print(array3); } static void print(int[] array){ for(int i = 0; i < array.length; i++){ System.out.print(array[i] + "\t"); } System.out.println(); System.out.println("---------------------------------"); } }
运行结果以下所示:
咱们使用不一样的排序策略分别对三个数组排序的功能已经实现了,程序很是灵活,咱们想用什么策略来实现排序能够本身决定(经过environment.serStrategy(XXX)方法)。
程序中Environment类根本不知道也不用管排序究竟是怎么实现的,它只是持有一个具体策略类的引用,而后调用具体策略类的方法。
策略模式用着很方便,可是自己也有缺点:
1.客户端必须知道全部的策略类,并自行决定使用哪一种策略类。
2.形成不少的策略类,每个不一样的策略都须要一个策略类来实现。
对于第一点很明显,咱们客户端(client类)在使用策略时必须知道具体的策略类是什么,environment.serStrategy(XXX)方法也须要知道。
对于第二点,定义了3中策略,就有3个策略类,若是策略不少的话,类也会不少。
参考资料:圣思园教学视频