策略模式(Strategy Pattern)是指定义了算法家族并分别封装恰里,让它们之间可以相互替换,此模式使得算法的变化不会影响使用算法的用户
一、策略模式的使用场景
- 系统中有很多类,而他们的区别仅仅在于行为的不同
- 一个系统需要动态地再几种算法中选择一种
二、策略模式的应用场景
网上购物现在已经是我们生活的常态,在网上购物时商家为了提高商品销量通常会举行一些促销活动,这里模拟促销优惠活动,来展示策略模式的应用
首先创建一个促销策略的接口:
/**
* @author xxbb
*/
public interface PromotionStrategy {
/**
* 优惠策略
*/
void doPromote();
}
创建具体的优惠策略,优惠券优惠策略类CouponStrategy、返现促销策略类CashbackStrategy、拼团优惠策略类GroupByStrategy和无优惠策略类EmptyStrategy。
/**
* @author xxbb
*/
public class CouponStrategy implements PromotionStrategy {
@Override
public void doPromote() {
System.out.println("领取优惠券,满100-39");
}
}
public class CashbackStrategy implements PromotionStrategy{
@Override
public void doPromote() {
System.out.println("返现促销,满100返现39,次日转回付款账号");
}
}
public class GroupBuyStrategy implements PromotionStrategy{
@Override
public void doPromote() {
System.out.println("拼团优惠,满5人成团,享受8折优惠");
}
}
public class EmptyStrategy implements PromotionStrategy {
@Override
public void doPromote() {
System.out.println("无促销策略");
}
}
编写促销活动方案类,用于执行策略
public class PromotionActivity {
private PromotionStrategy promotionStrategy;
public PromotionActivity(PromotionStrategy promotionStrategy){
this.promotionStrategy=promotionStrategy;
}
public void executor(){
promotionStrategy.doPromote();
}
}
测试代码:
public class Client {
public static void main(String[] args) {
PromotionActivity couponActivity=new PromotionActivity(new CouponStrategy());
PromotionActivity cashbackActivity=new PromotionActivity(new CashbackStrategy());
couponActivity.executor();
cashbackActivity.executor();
}
}
可以看到活动方案类根据传入的策略类的不同执行了不同的策略,这里很好的体现了面向对象的三大特征之一——多态。
然后我们再结合简单工厂模式优化下代码,使用户只需要关系选择的策略,而不需要了解策略的创建过程。
public class PromotionStrategyFactory {
interface PromotionKey{
String COUPON="COUPON";
String CASHBACK="CASHBACK";
String GROUP_BUY="GROUP_BUY";
String EMPTY="EMPTY";
}
private static Map<String,PromotionStrategy> promotionStrategyMap=new HashMap<>(10);
static{
promotionStrategyMap.put(PromotionKey.COUPON,new CouponStrategy());
promotionStrategyMap.put(PromotionKey.CASHBACK,new CashbackStrategy());
promotionStrategyMap.put(PromotionKey.GROUP_BUY,new GroupBuyStrategy());
}
private static final PromotionStrategy NON_PROMOTION=new EmptyStrategy();
private PromotionStrategyFactory(){}
public static PromotionStrategy getPromotionStrategy(String promotionKey){
PromotionStrategy promotionStrategy=promotionStrategyMap.get(promotionKey);
return promotionStrategy==null?NON_PROMOTION:promotionStrategy;
}
}
测试类:
public class Client {
public static void main(String[] args) {
String promotionKey= "COUPON";
PromotionActivity promotionActivity=new PromotionActivity(PromotionStrategyFactory.getPromotionStrategy(promotionKey));
promotionActivity.executor();
}
}
三、策略模式在源码中的体现
在JDK容器中我们有一个常用的比较器——Comparetor接口,其中的compare方法就是策略模式的抽象实现
public interface Comparator<T> {
int compare(T o1, T o2);
}
Comparetor接口下有非常多的实现类。
我们经常会把Comparetor接口作为传入参数实现排序策略,例如Arrays的parallelSort()
public static <T> void parallelSort(T[] a, Comparator<? super T> cmp) {
if (cmp == null)
cmp = NaturalOrder.INSTANCE;
int n = a.length, p, g;
if (n <= MIN_ARRAY_SORT_GRAN ||
(p = ForkJoinPool.getCommonPoolParallelism()) == 1)
TimSort.sort(a, 0, n, cmp, null, 0, 0);
else
new ArraysParallelSortHelpers.FJObject.Sorter<T>
(null, a,
(T[])Array.newInstance(a.getClass().getComponentType(), n),
0, n, 0, ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
MIN_ARRAY_SORT_GRAN : g, cmp).invoke();
}
还有TreeMap在实例化时能自定义排序策略
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
在Spring源码中的Resource接口也采用策略模式,不同的资源会采用不同的加载策略实现类。
在Spring的初始化时也才采用了策略模式,提供了一个InstantiationStrategy接口,下面提供两种策略实现类SimpleInstantiationStrategy和CglibSubclassingInstantiationStrategy.
四、策略模式的优缺点
策略模式的优点:
- 符合开闭原则
- 体现多态特性
- 可以避免使用多重条件语句如if,switch来执行具体算法
- 可以提高算法的保密性和安全性
策略模式的缺点:
- 用户必须了解所有的策略,并自行决定使用哪一种策略类
- 当代码中的策略非常多时,代码维护的难度增加
五、委派模式
委派模式(Delegate Pattern)不属于GoF23种设计模式。委派模式的基本作用就是负责人物的调用和分配,跟代理模式很香,可以看作一种特殊情况下的静态的全权代理,但代理模式注重过程,委派模式注重结果。这里通过老板下达指令,员工执行指令来展示委派模式
public interface IEmployee {
void working(String command);
}
public class EmployeeB implements IEmployee{
@Override
public void working(String command) {
System.out.println("我是员工B,正在执行"+command+"工作");
}
}
public class EmployeeA implements IEmployee{
@Override
public void working(String command) {
System.out.println("我是员工A,正在执行"+command+"工作");
}
}
public class Leader implements IEmployee{
private Map<String,IEmployee> employeeMap=new HashMap<>(5);
public Leader(){
employeeMap.put("登录",new EmployeeA());
employeeMap.put("加密",new EmployeeB());
}
/**
* 经理委派员工干活
* @param command 命令
*/
@Override
public void working(String command) {
employeeMap.get(command).working(command);
}
}
public class Boss {
public void command(String command,Leader leader){
leader.working(command);
}
}
public class TestMain {
public static void main(String[] args) {
//代理模式注重过程,委派模式注重结果
//策略模式注重可扩展性(外部可扩展性),委派模式注重内部的灵活性和可重复性
//委派模式的核心是分发,调度,派遣,委派模式是静态代理模式和策略模式的一种特殊组合
//分发:Boss将“登录”指令分发给Leader
//调度:Leader根据登录指令调度具体的EmployeeA
//派遣:EmployeeA被Leader派遣执行working方法
new Boss().command("登录",new Leader());
}
}
可以看到在Boss处我们将“登录”作为策略分发给Leader,Leader可以看作是Boss的代理,为Boss执行任务。而在Leader中根据Boss给出的策略选择对应的员工执行命令。