1.什么是策略模式?
策略模式是将可变的部分从程序中抽象成算法接口,在该结构下分别封装一系列算法实现.
比较典型的,可以拿大家都用过的美团来说,当大家在美团上开房进入到支付页面时,可以选择各种方式支付,比如美团支付,银联支付,支付宝支付,微信等…这里其实美团就把支付页面抽象成一个接口,具体的银联支付,支付宝支付,微信支付…交给对应的产商去实现其支付算法,美团在这里仅提供一个抽象的接口,不提供实现,这样做可以避免以后每接入一种新的支付方式就去改一遍代码…
2.为什么使用策略模式?
提到策略模式,我们先讲一下继承,在传统的做法中,当遇到一个类中的方法被多个类调用时,我们可能会考虑通过继承来实现,在父类中实现该方法,然后在子类直接继承父类即可拥有该方法,但这么做有何弊端?
①不够灵活,当某个子类需要修改父类中方法时还需要去覆盖父类的方法,万一有个子类忘了修改呢?编译器这时候又不报错或提示;
②一旦父类中的方法发生改变,所有继承它的子类都会受到牵连,有种株连九族的感觉.
有人会说,那我可以抽象父类中的方法,让子类去实现啊,的确这样做确实解决了上面提到的2个弊端,但又引入了新的弊端:
①每个子类都要强制重写父类中的方法,那么多子类,每个都要去写实现,多累啊.
②如果其中有几个子类的实现功能都相同,那又重新引入了代码重复问题.
有没有什么办法可以避免上面这两种方式啊?答案就是策略模式,策略模式可以很灵活的解决这些弊端,同时符合开闭原则,使代码具有很高的可扩展性复用性,可以消除代码中大量的条件语句,提高代码的可读性和质量所以推荐使用策略模式.
3.适用场景
①在代码中出现很多类具有相同的共性,可以考虑将其抽出来.
②运行时需要选取不同的算法变体.
③代码中有条件语句去选取其中一个分支时.
当出现以上三种情况下,可以考虑使用策略模式,让你的代码更优雅.
4.实现
由于我是一个车迷,所以这里拿车子来写代码,虽然我在现实中没车,但代码里可以有啊…
我现在有3辆车,一辆陆地跑的,一辆可以水上漂的,一辆更牛逼,可以上天飞的劳斯莱斯…
这些车子无论是天上飞的还是地上跑的都具有一个共性就是都可以Run,于是我决定把这个Run的算法抽出来作为接口,然后用不同的类分别去实现它们背后的算法,比如给水上漂的装上气垫,给天上飞的装上螺旋桨…对我抽象出来的Run接口而言,我再无需关心背后的实现.即便是以后再入手一辆可以在太空中翱翔的车,我也不用改来改去的,只需要再创个类去实现它即可.
①创建抽象接口和抽象方法
/**
* 策略模式-抽象接口
*/
public interface RunStrategy {
void run();//所有车子都能跑
}
②创建抽象的父类-车,将抽象接口私有化并修改默认构造方法.
/**
* 父类-车
*/
public abstract class Car {
private RunStrategy runStrategy;
public Car(RunStrategy runStrategy) {
this.runStrategy = runStrategy;
}
public void init(){
runStrategy.run();
}
}
③创建抽象接口的各种实现类:
public class RunOnRoald implements RunStrategy {
@Override
public void run() {
System.out.println("在陆地上疾驰...");
}
}
public class RunOnWater implements RunStrategy {
@Override
public void run() {
System.out.println("在水面上疾驰...");
}
}
public class RunOnAir implements RunStrategy {
@Override
public void run() {
System.out.println("在空气上疾驰...");
}
}
④创建对应的各种类型的车子,并继承它们的爸爸-Car:
/**
* 陆地suv汽车
*/
public class Suv extends Car{
public Suv(RunStrategy runStrategy) {
super(runStrategy);
}
}
/**
* 超牛的多功能汽车
*/
public class Mpv extends Car {
public Mpv(RunStrategy runStrategy) {
super(runStrategy);
}
}
/**
* 会飞的汽车
*/
public class Skr extends Car{
public Skr(RunStrategy runStrategy) {
super(runStrategy);
}
}
⑤测试一下:
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
//创建各种车子并调用其父类的初始化方法
Car suv = new Suv(new RunOnRoald());
suv.init();
Car mpv = new Mpv(new RunOnWater());
mpv.init();
Car skr = new Skr(new RunOnAir());
skr.init();
}
}
测试结果:
测试结果完全符合预期效果,如果你不喜欢车子,喜欢接近实际点的业务场景,那么我这里还有个现成的,你可以模拟去体验一把:
有个电商商城,类似于淘宝这种,然后商城有会员制度,不同等级的会员享受不同力度的折扣,非会员无折扣,具体的打折情况如下表:
你可以抽象一个计算价格的父类,父类中拥有一个计算折扣的抽象接口,不同的用户身份应不同的折扣算法,这些折扣算法分别去实现计算折扣的抽象接口,最后让所有用到计算价格的类去继承这个父类. 这个场景就比较贴合实际业务,加入某一天老板突然说要加入个什么白金会员,黑卡会员之类的,你要改动的代码会很少,这个我就不写了,感兴趣的自己去实现.