定义
策略模式(Strategy Pattern) 是一种比较简单的模式, 也叫做政策模式(PolicyPattern) 。 其定义如下:
Define a family of algorithms,encapsulate each one,and make them interchangeable.
也就是:定义一组算法, 将每个算法都封装起来, 并且使它们之间可以互换。
这个模式涉及到三个角色:
环境(Context)角色:持有一个Strategy的引用。
抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
类图结构
从类图中看到,Context就是环境角色,Strategy就是抽象的策略介绍,而ConcreteStrategy就是具体的策略,实际使用中肯定不止一个实现.
场景模拟
假设你现在想去商场买东西,Shopping后就需要付款,商家提供了多种付款方式,比如说支付宝付款,信用卡付款等等,这些具体的付款方式可以理解为具体的策略,所以我们可以把付款方式抽象为策略,而支付宝付款,信用卡付款就是其子类实现,当然这个场景的”环境”就是购物,因此还要有个”环境”Shopping类.具体的代码如下:
Shopping 环境类
/**
*@DESCRIPTION 定义一个购物类, 就是用来买东西的, 买完东西肯定要调支付接口, 至于选哪种支付手段就是用户自己决定了,
*@AUTHOR SongHongWei
*@TIME 2018/8/12-21:13
*@PACKAGE_NAME test.strategy
**/
public class Shopping
{
private PayMethod payMethod;
public Shopping(PayMethod payMethod)
{
this.payMethod = payMethod;
}
//付钱,支付用什么方式付钱,客户自己选择
public void payMoney()
{
System.out.println("我在购物,现在要付钱了");
payMethod.payMoney();
}
}
抽象的付款接口
/**
*@DESCRIPTION 定义一个接口,为付款方式,底下有很多实现方式,比如信用卡支付,支付宝支付,京东支付,微信支付等等
*@AUTHOR SongHongWei
*@TIME 2018/8/12-21:11
*@PACKAGE_NAME test.strategy
**/
public interface PayMethod
{
//付款
public void payMoney();
}
支付宝付款
public class Alipay implements PayMethod
{
@Override
public void payMoney()
{
System.out.println("我在使用支付宝支付");
}
}
信用卡付款
public class CreditCard implements PayMethod
{
@Override
public void payMoney()
{
System.out.println("我在使用信用卡支付");
}
}
财付通付款
public class TenPay implements PayMethod
{
@Override
public void payMoney()
{
System.out.println("我在使用财付通付款");
}
}
消费者类
public class Client
{
public static void main(String[] args)
{
Shopping shop = new Shopping(new Alipay());//使用支付宝付款
shop.payMoney();
System.out.println("---------------------------------");
shop = new Shopping(new TenPay());//使用财付通付款
shop.payMoney();
System.out.println("---------------------------------");
shop = new Shopping(new CreditCard());//使用信用卡付款
}
}
输出结果
我在购物,现在要付钱了
我在使用支付宝支付
---------------------------------
我在购物,现在要付钱了
我在使用财付通付款
---------------------------------
我在购物,现在要付钱了
我在使用信用卡支付
Process finished with exit code 0
总结
我们可以看到策略模式和代理模式有点类似,但又有区别,区别在哪里呢.我们都知道,代理模式中,代理者与被代理者都需要实现同一个接口,代理者只是帮被代理者做一些额外的事,真正的事还是要靠被代理者自己去做,所以他们必须实现同一个接口,去做同一件事.然而策略模式的封装角色和被封装的策略类可以不是同一个接口,在我们这个场景中封装策略的Shopping类就没有实现任何接口.
策略模式的优点
算法可以自由切换
这是策略模式本身定义的, 只要实现抽象策略, 它就成为策略家族的一个成员, 通过封装角色对其进行封装, 保证对外提供“可自由切换”的策略。
避免使用多重条件判断
如果没有策略模式, 我们想想看会是什么样子? 一个策略家族有5个策略算法, 一会要使用A策略, 一会要使用B策略, 怎么设计呢? 使用多重的条件语句? 多重条件语句不易维护, 而且出错的概率大大增强。 使用策略模式后, 可以由其他模块决定采用何种策略, 策略家族对外提供的访问接口就是封装类, 简化了操作, 同时避免了条件语句判断。
扩展性良好
这甚至都不用说是它的优点, 因为它太明显了。 在现有的系统中增加一个策略太容易了, 只要实现接口就可以了, 其他都不用修改, 类似于一个可反复拆卸的插件, 这大大地符合了OCP(开闭)原则1。
策略模式的缺点
策略类数量增多
每一个策略都是一个类, 复用的可能性很小, 类数量增多。
所有的策略类都需要对外暴露
上层模块必须知道有哪些策略, 然后才能决定使用哪一个策略.实际上这与这与迪米特法则2是相违背的.
场景举例
栗子1:
比如现在有一个系统要记录日志,一般情况下是记录到数据库中的,但是如果哪一天数据库连接断开了,或者网络故障但操作请求已经执行了怎么办,总不能不记录这次操作吧,这时候就能用到策略模式了,当数据库连接断开了,我就设置另一个记录日志的策略比如说记录日志到文件里,默认策略是记录到数据库里,这样当网络环境出现问题时为我们的程序还是能够正常的执行下去的.
栗子2:
再举一个栗子,假如你有两个工程,彼此之间需要进行数据通信,也就是说跨项目调用服务,现在有两种方式实现一种是Webservice方式另一种是httpclient,这两种方式各有优点,领导说都要做,在生产环境上根据不同的网络情况切换不同的调用方式(这里可能就需要配置开关了).这时候就可以用策略模式,抽象一个跨中心调用服务的接口,用这个接口去实现款中心掉服务,该接口有两个实现类一个是Webservice方式另一个是httpclient方式.再根据开关配置去切换调用方式