什么是策略模式?
策略模式(Strategy)顾名思义,就是应对一个事情要有不同的策略。
不同的策略中对应不同的方法去应对。例如诸葛亮给赵云的锦囊妙计。根据诸葛亮推测它老大哥的处境,给出不同的策略去执行。
这就是策略模式。
策略模式(Strategy Pattern)是一种比较简单的模式,也叫做政策模式(Policy Pattern)。其定义如下: Define a family of algorithms,encapsulate each one,and make them interchangeable.(定义一组 算法,将每个算法都封装起来,并且使它们之间可以互换。)
— 《设计模式之禅》
设计模式框架图:
策略模式使用的就是面向对象的继承和多态机制,非常容易理解和掌握,我们再来看看 策略模式的三个角色
● Context封装角色
它也叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问, 封装可能存在的变化。
● Strategy抽象策略角色 策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。各位看官可能要问了,类图中的AlgorithmInterface是什么意思,嘿嘿,algorithm是“运算法则”的 意思,结合起来意思就明白了吧。
● ConcreteStrategy具体策略角色 实现抽象策略中的操作,该类含有具体的算法。 我们再来看策略模式的通用源码,非常简单。先看抽象策略角色,它是一个非常普通的 接口,在我们的项目中就是一个普通得不能再普通的接口了,定义一个或多个具体的算法。
下面给大家举两个例子,相信大家就会很容易理解。
诸葛亮的锦囊妙计
这个例子摘自《设计模式之禅》
在诸葛亮气周瑜的过程中,有一件事情:那就是周瑜赔了夫人又折兵这件事情。事情经过是这样的:孙权看刘备有雄起之意,杀是不能杀了,那会惹天下人唾弃,就想个招儿挫他一下,那有什么办法呢?孙权有个妹妹——孙尚香,准备招刘备做女婿,然后孙权想办法把刘备软禁起来,孙权的想法还是很单纯的嘛,就是不让你刘备回西川,然后我东吴想干啥就干啥,夺荆州,吞西川也不是不可能的。东吴的想法是好的,无奈中间多了智谋无敌的诸葛亮,他早就预测了东吴有此招数,于是在刘备去东吴招亲之前,特授以伴郎赵云三个锦囊,说是按天机拆开解决棘手问题。
这三个妙计分别是:找乔国老帮忙(也就是走后门了),求吴国太放行(诉苦)以及孙夫人断后,对这三个妙计不熟悉的读者可以去温习一下《三国演义》,这里就不多说了。想想看,这三个计谋有什么相似之处,他们都是告诉赵云要怎么执行,也就是说这三个计谋都有一个方法是执行,具体执行什么内容,每个计谋当然不同了,分析到这里,我们是不是就有这样一个设计思路:三个妙计应该实现的是同一个接口?聪明!是的,我们来看类图,如图18-1所示。
那么在代码中我们该如何去设计呢?
首先我们把妙计设计为接口
public interface IStrategy {
//每个锦囊妙计都是一个可执行的算法
public void operate();
}
接口很简单,定义了一个方法operate,每个妙计都是可执行的,否则那叫什么妙计,我 们先看第一个妙计——找乔国老开后门
public class BackDoor implements IStrategy {
public void operate() {
System.out.println("找乔国老帮忙,让吴国太给孙权施加压力"); }
}
第二个妙计是找吴国太哭诉,企图给自己开绿灯
public class GivenGreenLight implements IStrategy{
public void operate(){
System.out.println("求吴国太开绿灯,放行!")
}
}
第三个妙计是在逃跑的时候,让新娘子孙夫人断后,谁来砍谁。
public class BlockEnemy implements IStrategy {
public void operate() {
System.out.println("孙夫人断后,挡住追兵");
}
}
在这个场景中,三个妙计都有了,那还缺少两个配角:
第一,妙计肯定要放到一个地方 吧,这么重要的东西要保管呀,也就是承装妙计的锦囊,所以俗称锦囊妙计嘛;
第二,这些 妙计都要有一个执行人吧,是谁?当然是赵云了,妙计是小亮给的,执行者是赵云。赵云就 是一个干活的人,从锦囊中取出妙计,执行,然后获胜。过程非常清晰,我们把完整的过程 设计出来
public class Context {
//妙计
private IStrategy istrategy ;
//构造函数来给妙计具体的参数
Context(IStrategy istrategy){
this.istrategy = istrategy;
}
//使用该妙计
public void operate(){
this.istrategy.operate();
}
}
通过构造函数把策略传递进来,然后用operate()方法来执行相关的策略方法。三个妙计 有了,锦囊也有了,然后就是赵云雄赳赳地揣着三个锦囊,拉着已步入老年行列的、还想着娶纯情少女的刘老爷子去入赘了。嗨,还别说,小亮同志的三个妙计还真是不错,
public class ZhaoYun {
//赵云出场了,他根据诸葛亮给他的交代,依次拆开妙计
public static void main(String[] args) {
Context context;
//刚刚到吴国的时候拆第一个
System.out.println("---刚刚到吴国的时候拆第一个---");
//拿到妙计
context = new Context(new BackDoor());
//拆开执行
context.operate();
System.out.println("\n\n\n\n\n\n\n\n");
//刘备乐不思蜀了,拆第二个了
System.out.println("---刘备乐不思蜀了,拆第二个了---");
context = new Context(new GivenGreenLight());
//执行了第二个锦囊
context.operate();
System.out.println("\n\n\n\n\n\n\n\n");
//孙权的小兵追来了,咋办?拆第三个
System.out.println("---孙权的小兵追来了,咋办?拆第三个---");
context = new Context(new BlockEnemy());
//孙夫人退兵
context.operate();
System.out.println("\n\n\n\n\n\n\n\n");
}
}
这整个过程就是一个策略模式。
ComparaTor
在之前的博客中我写过ComparaTor,实际应用也很简单,对类的比较而言有不同的比较方法,只需要在具体实现时实现ComparaTor接口,这样就可以实现多维度的比较了。
我们来定义 猫 类。
在这里们对猫进行排序,根据猫的不同属性进行排序,我们就要实现Comparator接口。
UML图:
class Cat{
private int heigh ;
private int weight;
public Cat(int heigh, int weight) {
this.heigh = heigh;
this.weight = weight;
}
public void setHeigh(int heigh) {
this.heigh = heigh;
}
public void setWeight(int weight) {
this.weight = weight;
}
public int getHeigh() {
return heigh;
}
public int getWeight() {
return weight;
}
@Override
public String toString() {
return "Cat{" +
"heigh=" + heigh +
", weight=" + weight +
'}';
}
}
排序工具类
class SortUtil<E>{
public void Sort(E[] arr, Comparator<E> comparator){
//写个简单的快排
quicksort(arr,0,arr.length-1,comparator);
}
private void quicksort(E[] arr,int left,int right,Comparator<E> comparator){
if(left > right) return;
int parartion = getParation(arr,left,right,comparator);
if(parartion - left > 1){
quicksort(arr,left,parartion-1,comparator);
}
if(right - parartion > 1){
quicksort(arr,parartion+1,right,comparator);
}
}
private int getParation(E[] arr,int left,int right,Comparator<E> comparator){
E porvit = arr[left];
while(left <= right ){
//从后向前找 右边为大于等于
while( left < right && comparator.compare(arr[right],porvit) >= 0 ){
right--;
}
//直接放置
arr[left] = arr[right];
//从前向后找 左边为小于
while(left < right && comparator.compare(arr[left],porvit) < 0 ){
left++;
}
arr[right] = arr[left];
if(left == right){
break;
}
}
arr[left] = porvit;
return left;
}
}
两个排序准则,实现的两个比较器
class CatHighComparator implements Comparator<Cat>{
@Override
public int compare(Cat o1, Cat o2) {
if(o1.getHeigh() > o2.getHeigh()) return 1;
else if( o1.getHeigh() < o2.getHeigh()) return -1;
return 0;
}
}
class CatWeightComparator implements Comparator<Cat>{
@Override
public int compare(Cat o1, Cat o2) {
if(o1.getWeight() > o2.getWeight()) return 1;
else if(o1.getWeight() < o2.getWeight()) return -1;
return 0;
}
}
测试
public static void main(String[] args) {
Cat[] arr = {new Cat(1,3),new Cat(2,4),new Cat(4,1)};
SortUtil sortUtil = new SortUtil();
sortUtil.Sort(arr,new CatHighComparator());
System.out.println(Arrays.toString(arr));
sortUtil.Sort(arr,new CatWeightComparator());
System.out.println(Arrays.toString(arr));
}
结果:
第一行,按照heigh 排序, 第二行按照weight 排序。