什么是策略模式(Strategy)
概念
策略(Strategy)模式属于行为型模式,定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。简单来说,就是把本来写在类内部的算法而且可以相互替换的算法全部都提取到算法类中,这些算法类就像一个个锦囊一样,然后通过使用不同的算法对象来实现算法的替换,至于为什么是算法,我觉得其实业务也行,但是业务的替换性不是很强,很多情况下不太适用。
策略模式是想使用组合对象的方式来实现继承实现不了的代码复用,并且能够在横向竖向都能够复用代码,如果是使用继承的话,仅仅只能够在竖向复用代码。我看到很多博客文章都写策略模式是用来减少if-else的,我在学习的过程中也没有体会到是怎么减少if-else的,所以我不打算朝这方面是思考,以后有改观了再补充。
这里举个例子,排序算法有很多种,冒泡、选择、归并、插入、二叉树等等,我们每次排序的时候都可以选取其中一个算法来使用,这如果在一个类创建N多个算法,那这个类都要炸掉了;还有超市的会员制度,比如一级会员没有任何特权只有积分,二级可以有会员价和积分,三级不仅有会员价而且有双倍积分,如果不采用策略模式,那么就会出现非常多重复的代码,使用继承也不能很好的解决这个问题。
优点
- 符合开放封闭原则。想要增加新的算法直接写新的算法类即可,不需要对已有的类进行修改,提高了系统的可扩展性。
- 符合单一职责原则。把算法封装到单个类中,这个类只做这个算法有关的事情。
- 提高了代码的重用性。
缺点
- 会存在大量的策略类,增大维护难度。
- 使用者需要理解每个策略类的区别,根据自己的需求选择策略类。
原则
“+”代表遵守,“-”代表不遵守或者不相关
原则 | 开放封闭 | 单一职责 | 迪米特 | 里氏替换 | 依赖倒置 | 接口隔离 | 合成复用 |
---|---|---|---|---|---|---|---|
+ | + | - | - | + | - | - | |
适用场景
- 继承不能满足当前代码的复用。继承只能竖向复用代码,适用策略类可以横向以及竖向复用代码。
- 算法存在需要经常切换的情况。
- 多种算法实现过于复杂。
如何实现
想要实现策略模式,需要以下三样东西:
- 策略抽象类/接口:定义策略类的公共接口,规范策略类的职责,在声明引用时使用该抽象类或接口。
- 策略实现类:实现策略抽象类/接口,实现具体的算法。
- 环境类:管理和调用策略实现类对象。
类图
例子
这里以上面举的超市会员的例子为例,一级没有任何特权只有积分,二级可以有会员价和积分,三级不仅有会员价而且有双倍积分,如果不用策略模式来实现,那么就得实现三个类,其中会员价和积分的处理逻辑会重复并且代码也不能得到复用。
那么这里就有会员、价格处理方式、积分处理方式这三种对象,会员就对应着环境类,而价格、积分处理方式就对应着策略类,价格处理方式:无处理、会员价,积分处理:积分、双倍积分
类图
代码
/**
*
* @author xuxiaobai
*/
public class StrategyTest {
public static void main(String[] args) {
System.out.println("----一级会员----");
Member member1 = new Member("111");
member1.doProcess(100);
System.out.println("----二级会员----");
Member member2 = new Member("222");
member2.doProcess(100);
System.out.println("----三级会员----");
Member member3 = new Member("333");
member3.doProcess(100);
/**
* 结果:
* ----一级会员----
* 需支付:100.0元
* 支付成功!
* 用户:111,增加积分:100
* ----二级会员----
* 需支付:90.0元
* 支付成功!
* 用户:222,增加积分:100
* ----三级会员----
* 需支付:90.0元
* 支付成功!
* 用户:333,增加积分:200
*/
}
}
/**
* 策略接口
* 价格处理接口
*/
interface Price{
/**
* 处理价格
* @param price
* @return
*/
double processPrice(double price);
}
/**
* 策略类
* 无处理价格类
*/
class CommonPrice implements Price{
@Override
public double processPrice(double price) {
return price;
}
}
/**
* 策略类
* 会员价类
*/
class MemberPrice implements Price{
@Override
public double processPrice(double price) {
//我要做黑心商家
return price>100?price-5:price*0.9;
}
}
/**
* 策略接口
* 积分处理接口
*/
interface Integral{
/**
* 增加积分
* @param integral
*/
long processIntegral(long integral);
}
/**
* 策略类
* 积分类
*/
class CommonIntegral implements Integral{
@Override
public long processIntegral(long price) {
return price;
}
}
class DoubleIntegral implements Integral{
@Override
public long processIntegral(long price) {
return price*2;
}
}
/**
* 环境类
* 会员
*/
class Member{
private String phone;
private Price priceStrategy;
private Integral integralStrategy;
/**
*
* @param phone
*/
public Member(String phone){
this.phone=phone;
/**模拟去数据库查询会员等级,然后赋值价格和积分处理策略
* 这里类似一个工厂方法,通过不同的结果去使用不同的策略
*/
if ("111".equals(phone)){
//一级会员
this.priceStrategy=new CommonPrice();
this.integralStrategy=new CommonIntegral();
}
if ("222".equals(phone)){
//二级会员
this.priceStrategy=new MemberPrice();
this.integralStrategy=new CommonIntegral();
}
if ("333".equals(phone)){
//三级会员
this.priceStrategy=new MemberPrice();
this.integralStrategy=new DoubleIntegral();
}
}
public double doProcess(double price) {
double processPrice = priceStrategy.processPrice(price);
System.out.println("需支付:"+processPrice+"元");
System.out.println("支付成功!");
long integral = integralStrategy.processIntegral((long) price);
System.out.println("用户:"+phone+",增加积分:"+integral);
return 0;
}
}
这里的会员类是可以再加一个方法去升级的,比如一级会员第一次付满一百元,就升级成二级会员,可以做一个策略对象的替换。
总结
在我上面的例子中可以看到,我在构造方法使用了一些判断来创建会员类,策略模式搭配着工厂模式来使用会比较方便,把创建环境类的过程封装到工厂类中,这样能分明类的职责。如果不适用策略模式,那么我们就得创建一级会员、二级会员和三级会员,会存在方法的代码重复的情况,而且这还是类少的情况,如果以后再来个四级会员、五级。。。那类就会十分地膨胀,而且代码会有很多重复。
这里来对比一下策略模式和命令模式的区别,策略模式,重在重用代码,可以弥补继承的不足;命令模式,重在分离方法的使用者和实现者,可以延迟、撤销命令等。
写着写着,我就想起来又跟桥接模式特别的像,都是对象模式,都把请求委托给了一个接口的具体实现,但桥接模式是为了把本来使用继承实现的N*M个对象改成了使用组合对象实现;策略模式是把算法封装起来,做到可以随时替换。
——————————————————————————————
你知道的越多,不知道的就越多。
如果本文章内容有问题,请直接评论或者私信我。如果觉得我写得还不错的话,点个赞也是对我的支持哦
未经允许,不得转载!