设计模式第二话—策略模式
Posted 小仇哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式第二话—策略模式相关的知识,希望对你有一定的参考价值。
一、实际问题:
某电商系统逢年过节都需做线上促销活动,但每次促销活动的方式不一样,所以需要开发一个功能,只要输入原价再选择活动类型后,就能计算出折扣以后的价钱。
简单常规的做法就是:
public static void main(String[]args)
Scanner scan = new Scanner(System.in));
String input = scan.nextLine();
double price = scan.nextLine();
switch (input)
case "中秋节":
price = price * 0.95;
break;
case "国庆节":
if(price>=100)
price = price - 50;
break;
default
price = price;
println("打折后价钱:"+price);
这样写存在的问题:
若需增加新的促销活动,就要修改这段代码,需新增switch-case分支,非常麻烦且打破了OCP,即“开放-封闭”的原则。
开闭原则:
对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。
对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。
那有没有什么办法使得我们的报价管理即可扩展、可维护,又可以方便的响应变化呢?当然有解决方案啦,就是我们下面要讲的策略模式。
二、策略模式定义
策略(Strategy)模式:定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化。它也被成为政策模式,是一种行为型模式。
图1 策略模式通用类图
Strategy接口(抽象策略角色)
策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。
ConcreteStrategy具体策略角色
实现抽象策略中的操作,该类含有具体的算法。
Context封装角色
即上下文角色,起到承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
三、策略模式的源代码
//策略接口
public interface IStrategy
//定义的抽象算法方法 来约束具体的算法实现方法
public void doSomething();
// 具体的策略实现
public class ConcreteStrategy1 implements IStrategy
//具体的算法实现
@Override
public void doSomething()
System.out.println("this is ConcreteStrategy1 method...");
// 具体的策略实现
public class ConcreteStrategy2 implements IStrategy
//具体的算法实现
@Override
public void doSomething()
System.out.println("this is ConcreteStrategy2 method...");
/**
* 策略上下文
*/
public class Context
//持有一个策略实现的引用
private IStrategy strategy;
//使用构造器注入具体的策略类
public StrategyContext(IStrategy strategy)
this.strategy = strategy;
public void contextMethod()
//调用策略实现的方法
strategy.doSomething();
//外部客户端
public class Client
public static void main(String[] args)
//1.声明具体策略实现
IStrategy strategy = new ConcreteStrategy1();
//2.在创建策略上下文的同时,将具体的策略实现对象注入到策略上下文当中
Context ctx = new Context(strategy);
//3.调用上下文对象的方法来完成对具体策略实现的回调
ctx.contextMethod();
四、解决问题
针对我们上面促销活动的例子:我们可以应用策略模式对其进行改造,不同类型的活动有不同的折扣,我们可以将不同类型的活动的报价规则都封装为一个独立的算法,然后抽象出这些报价算法的公共接口
公共报价策略接口:
//报价策略接口
public interface IStrategy
//获取折后价的价格
double getPrice(double price);
中秋节活动策略实现:
//中秋节活动的报价策略实现类
public class ZqStrategy implements IStrategy
@Override
double getPrice(double price)
price = 0.95*price;
return price;
国庆活动策略实现:
//国庆活动的报价策略实现类
public class GqStrategy implements IStrategy
@Override
double getPrice(double price)
if(price>=100)
price = price - 50;
return price;
无活动策略实现:
//无活动的报价策略实现类
public class wStrategy implements IStrategy
@Override
double getPrice(double price)
return price;
策略上下文:
//报价上下文角色
public class Context
//持有一个具体的报价策略
private IStrategy strategy;
//注入报价策略
public Context(IStrategy strategy)
this.strategy = strategy;
//回调具体活动报价策略的方法
ouble getPrice(double price)
return strategy.getPrice(price);
外部客户端:
//外部客户端
public class Client
public static void main(String[] args)
//1.创建中秋活动的报价策略
IQuoteStrategy zqStrategy = new ZqStrategy();
//2.创建报价上下文对象,并设置具体的报价策略
Context context = new Context(zqStrategy);
//3.调用报价上下文的方法
double price = context.getPrice(100);
System.out.println("折扣价为:" +price);
这个时候,电商系统新推出双十一大促,可以享受折扣5折优惠,那该怎么办呢?这个很容易,只要新增一个报价策略的实现,然后外部客户端调用的时候,创建这个新增的报价策略实现,并设置到策略上下文就可以了,对原来已经实现的代码没有任何的改动。
五、策略模式优缺点
优点:提供了对开闭原则的完美支持,算法可以自由切换,避免使用多重分支条件判断;扩展性良好;复用性好,不同环境类可以方便复用这些策略类。
缺点:策略类数量多,来一种策略就要写一个类;无法在客户端同时使用多个策略类 --> 客户端每次只能使用一个策略类;所有策略类都需要对外暴露(致命缺陷,客户端需要知道所有的策略类,并自行决定使用哪一个策略–>只适用于客户端了解所有策略算法的情况)。
六、使用场景
多个类只有在算法或行为上稍有不同的场景
算法需要自由切换的场景,即如果一个系统要动态地在几种算法之间选择其中一种时
需要屏蔽算法规则的场景,即希望客户知道复杂的与算法有关的数据结构,可以将其封装到策略中
七、参考资料
设计模式之禅
以上是关于设计模式第二话—策略模式的主要内容,如果未能解决你的问题,请参考以下文章