策略模式
含义:
策略模式(Strategy)属于对象行为型设计模式,
1.主要是定义一系列的算法,
2.把这些算法一个个封装成拥有共同接口的单独的类,并且使它们之间可以互换。
- 策略模式使这些算法在客户端调用它们的时候能够互不影响地变化。这里的算法不要狭义的理解为数据结构中算法,可以理解为不同的业务处理方法。
策略模式由三个角色组成:
策略模式使用场景:
同一个系统中存在不同的逻辑算法,而彼此之间在不同场景中需要实现动态替换,
- 支付方式
可以选择:支付宝,微信,银联等,彼此之间就可以相互替换, - 打折促销活动
有双11活动,双12活动,会员活动,彼此之间可以相互替换
案例 :排序问题
1. int[ ]数组
Sorter1
public class Sorter1 {public void sort(int[] arr) {for(int i=0; i<arr.length - 1; i++) {int minPos = i;for(int j=i+1; j<arr.length; j++) {minPos = arr[minPos]> arr[j] ? j : minPos;}swap(arr, i, minPos);}}//sort(int)void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}
测试:
@Testpublic void test01() {int[] a = {9, 2, 3, 5, 7, 1, 4};Sorter1 sorter = new Sorter1();sorter.sort(a);System.out.println(Arrays.toString(a));}
2. Cat[ ]数组
Comparable接口https://blog.csdn.net/weixin_48052161/article/details/115414069
Cat
public class Cat implements Comparable<Cat> {Integer weight, height;public Cat(int weight, int height) {this.weight = weight;this.height = height;}@Overridepublic int compareTo(Cat c) {if(this.weight < c.weight) return -1;else if(this.weight > c.weight) return 1;else return 0;}@Overridepublic String toString() {return "Cat{" +"weight=" + weight +", height=" + height +'}';}
}
Sorter2
public class Sorter2 {public void sort(Cat[] arr) {for (int i = 0; i < arr.length - 1; i++) {int minPos = i;for (int j = i + 1; j < arr.length; j++) {minPos = arr[minPos].compareTo(arr[j]) == 1 ? j : minPos;}swap(arr, i, minPos);}}void swap(Cat[] arr, int i, int j) {Cat temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}
测试:
@Testpublic void test02() {Cat[] a= new Cat[]{new Cat(3,3),new Cat(5,5),new Cat(1,1)};Sorter2 sorter = new Sorter2();sorter.sort(a);System.out.println(Arrays.toString(a));}
3. 策略模式 定制规则
comparator 定制排序
Sorter3
public class Sorter3<T> {public void sort(T[] arr, Comparator<T> comparator) {for (int i = 0; i < arr.length - 1; i++) {int minPos = i;for (int j = i + 1; j < arr.length; j++) {minPos = comparator.compare(arr[minPos],arr[j])==1 ? j : minPos;}swap(arr, i, minPos);}}void swap(T[] arr, int i, int j) {T temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}
测试 ; cat比较 按体重降升序,如果体重相同身高降序排列
//按体重降升序,如果体重相同身高降序@Testpublic void test03() {Cat[] a= new Cat[]{new Cat(1,3),new Cat(3,3),new Cat(1,1),new Cat(1,2)};Sorter3<Cat> sorter = new Sorter3();sorter.sort(a,(o1,o2)->{if(o1.weight<o2.weight){return -1;}else if(o1.weight>o2.weight){return 1;}else{//Integer默认重写的compareTo方法,模式是从小到大排序return -o1.height.compareTo(o2.height);}});System.out.println(Arrays.toString(a));}
Integer 实现了Comparable接口 ,且重写了compareTo方法,模式是从小到大排序
4.JDK源码中策略模式排序
Arrays.sort()
- 在JDK中,我们调用数组工具类Arrays的一个排序方法sort时,可以使用默认的排序规则(升序),也可以自定义指定排序的规则,也就是可以自定义实现升序排序还是降序排序,方法的源码如下:
@Testpublic void test04() {Cat[] a= new Cat[]{new Cat(1,3),new Cat(3,3),new Cat(1,1),new Cat(1,2)};Arrays.sort(a,(o1,o2)->{if(o1.weight<o2.weight){return -1;}else if(o1.weight>o2.weight){return 1;}else{//Integer默认重写的compareTo方法,模式是从小到大排序return -o1.height.compareTo(o2.height);}});System.out.println(Arrays.toString(a));}
案例:支付问题
1、创建一个策略接口
PayStrategry
public interface PayStrategry {boolean pay(int money);//支付BigDecimal queryBalance(String accountNo);//查询余额
}
2、接下来定义2个实现类示例
AliPayStrategy
public class AliPayStrategy implements PayStrategry{@Overridepublic boolean pay(int money) {System.out.println("支付宝支付成功");return true;}@Overridepublic BigDecimal queryBalance(String accountNo) {System.out.println("支付宝余额10元");return new BigDecimal(10);}}
WechatPayStrategy
public class WechatPayStrategy implements PayStrategry{@Overridepublic boolean pay(int money) {System.out.println("微信支付成功");return true;}@Overridepublic BigDecimal queryBalance(String accountNo) {System.out.println("微信余额10元");return new BigDecimal(10);}}
3.一般方法测试
public class TestPayStrategy {public static void main(String[] args) {String pay = "aliPay";PayStrategy payStrategy = null;if(pay.equals("aliPay")){payStrategy = new AliPayStrategy();}else if(pay.equals("wechatPay")){payStrategy = new WechatPayStrategy();}payStrategy.pay(10);payStrategy.queryBalance("XXX");}
}
通过枚举类的实现方式来改造一下测试方法:
PayEnum
public enum PayEnum {AliPay("aliPay",new AliPayStrategy()),WechatPay("wechatPay",new WechatPayStrategy());private String key;private PayStrategry value;PayEnum(String key, PayStrategry value) {this.key = key;this.value = value;}public static PayStrategry getValue(String key){for (PayEnum payEnum : PayEnum.values()){if (payEnum.key.equals(key)){return payEnum.value;}}return new AliPayStrategy();//没有合适key则默认阿里支付}}
测试一下
public class TestPayStrategy {public static void main(String[] args) {String pay = "aliPay";PayStrategry payStrategy = PayEnum.getValue(pay);payStrategy.pay(10);payStrategy.queryBalance("XXX");}
现在增加一种支付方式,icbc支付
- 只需要对枚举类代码进行修改
PayEnum
public enum PayEnum {AliPay("aliPay",new AliPayStrategy()),WechatPay("wechatPay",new WechatPayStrategy()),ICBCPay("icbc", new PayStrategry() {@Overridepublic boolean pay(int money) {System.out.println("icbc支付成功");return true;}@Overridepublic BigDecimal queryBalance(String accountNo) {System.out.println("icbc余额10元");return new BigDecimal(10);}});private String key;private PayStrategry value;PayEnum(String key, PayStrategry value) {this.key = key;this.value = value;}public static PayStrategry getValue(String key){for (PayEnum payEnum : PayEnum.values()){if (payEnum.key.equals(key)){return payEnum.value;}}return new AliPayStrategy();//没有合适key则默认阿里支付}}
测试一下
public class TestPayStrategy {public static void main(String[] args) {String pay = "aliPay";PayStrategry payStrategy = PayEnum.getValue(pay);payStrategy.pay(10);payStrategy.queryBalance("XXX");System.out.println("----------------------------");PayStrategry payStrategy1 = PayEnum.getValue("icbc");payStrategy1.pay(10);payStrategy1.queryBalance("XXX");}}
这么改写之后,如果以后新增了其他支付方式只需要再枚举类中新怎过一种枚举类型并且实现自定义支付逻辑之后,其他代码就无需变更
策略模式总结
优点
- 结构清晰明了、使用简单直观;
- 耦合度相对而言较低,扩展方便;
- 操作封装也更为彻底,数据更为安全;
缺点
- 策略类数量会增多,每个策略都是一个类,复用的可能性很小,增加维护难度
- 所有的策略类都需要对外暴露
应用场景
- 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。多个类只有算法或行为上稍有不同的场景
- 一个系统需要动态地在几种算法中选择一种。算法需要自由切换的场景
- 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
- 需要屏蔽算法规则的场景
注意事项
- 如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题