策略 Strategy 定义
定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
这个定义比较抽象,是我抄来的,说了半天,好像很强,但又不知所云。下面我们用具体的例子来做具象化的了解。
如何理解
说到策略,一定离不开接口,其核心就是要对我们的策略进行抽象,抽象成一个策略接口,然后基于策略接口进行不同类型的策略实现,最后需要一个承载运行策略的一个环境。
那么其实核心可以分成 3 部分:
1. 策略抽象接口 2. 策略抽象接口实现类 3. 策略运行承载类
如此设计的好处就是,抽象接口不用修改,运行承载类不用修改,我们只需要拓展策略接口的实现类即可。满足开闭原则。
举个例子
给出一个攻击的抽象策略接口
@FunctionalInterface
public interface Attack<T> {
void doAttack(T t1,T t2);
}
定义一个英雄实体,包含一些基本属性
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Hero {
private String name;
/**
* 攻击力
*/
private Integer AP;
/**
* 生命值
*/
private Integer HP;
/**
* 法力值
*/
private Integer MP;
}
定义一个战场环境,作为承载执行攻击策略的载体。
public class Battlefield<T> {
public void fight(Attack<T> attack, T t1, T t2) {
System.out.println();
System.out.println("初始 战斗平台");
System.out.println(t1+"进入战场");
System.out.println(t2+"进入战场");
attack.doAttack(t1, t2);
System.out.println("初始 攻击结束");
System.out.println();
}
}
抽象策略的实现 其实就有很多种方式了:
- 定义不同的类继承实现
- 在调用的时候直接内部实现
- 函数式编程
public class Strategy {
public static void main(String[] args) {
Battlefield battlefield =new Battlefield();
Hero hero1=new Hero("盖伦",4,10,30);
Hero hero2=new Hero("诺手",2,50,90);
battlefield.fight((Attack<Hero>) (t1, t2) -> System.out.println(t1.getName()+" 对 "+t2.getName()+" 使用 旋风斩 造成了 "+t1.getAP()+" 点伤害"),hero1,hero2);
battlefield.fight((Attack<Hero>) (t1, t2) -> System.out.println(t1.getName()+" 对 "+t2.getName()+" 使用 正义审判 造成了 "+t1.getAP()*10+" 点伤害"),hero1,hero2);
battlefield.fight((Attack<Hero>) (t2, t1) -> System.out.println(t2.getName()+" 对 "+t1.getName()+" 使用 无情铁手 造成了 "+t2.getAP()*3+" 点伤害"),hero2,hero1);
}
}
博主这里使用函数式编程的方式,实现了 3 种不同的策略,最终的运行效果如下:
如何运用
优点:
- 使程序更易维护。避免了多 case 情况的大量 if else 语句,
- 符合开闭原则。可以在不修改原有代码的情况下灵活添加算法。
- 高内聚低耦合。将算法的使用放到环境类中,算法的实现放到策略接口实现中。
缺点:
- 可能会造成大量的策略接口实现类
- 使用者必须清楚何时使用恰当的策略类
运用:
在运用上,我们需要确定当前场景是否符合使用策略模式的条件:
- 是否有策略(某些算法或者方法)可以进行抽象
- 是否有必要抽象,抽象后是否经常需要拓展
在必要的时候使用策略模式是一种设计的美,但并不是任何场景都需要套用策略模式,不加思考的套用可能反而增加程序的维护成本。
JDK 对策略模式的应用
那么 JDK 在设计上也使用了策略模式。
比如 Comparator 接口,比较器接口就是典型的策略接口,这使得我们可以通过该接口定义任意的比较规则。
Arrays.sort() 就是具体比较策略算法的使用承载环境类,对你提供的集合和比较算法进行排序。
这里比较策略就是我们可以灵活拓展定制的部分了。
总结
对于 策略模式 你只需要牢牢记住 3 部分就好了。这里我再重复一遍:
1. 策略抽象接口 2. 策略抽象接口实现类 3. 策略运行承载类
在实际开发中灵活思考进行运用。但我们仍需记住一点,只在必要的时候使用,而不是生搬硬套的炫技。
希望本文对你有所帮助,同时 欢迎添加个人微信 dyinggq 一起交流学习~~
我是 dying 搁浅 ,我始终期待与你的相遇。无论你是否期待,潮涨潮落,我仅且就在这里…
我们下期再见~