策略设计模式详解C/Java/JS/Go/Python/TS不同语言实现
Posted 刀法如飞-专注算法与设计模式
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了策略设计模式详解C/Java/JS/Go/Python/TS不同语言实现相关的知识,希望对你有一定的参考价值。
简介
策略模式(Strategy Pattern)属于行为型设计模式。将每一个算法封装到具有共同接口的独立类中,根据需要来绑定策略,使得具体实现和策略解耦。
当你想使用对象中各种不同的算法变体,使用if...else 所带来的复杂和难以维护,可使用策略模式。或者当有许多相同类,它们仅在执行某些行为时略有不同,可使用策略模式。
作用
- 策略算法可以自由切换,保持策略与执行类的松耦合。
- 避免使用多重条件判断,不同环境角色可以组装多个策略。
- 扩展性良好,可以随时增删策略行为。
- 体现了多用组合,少用继承。
实现步骤
- 建立一个策略接口。
- 新建多个策略行为类,实现该策略接口。
- 建立一个抽象环境角色类,并将策略接口组合进来。是否需要抽象类可选。
- 建立多个环境角色类来继承该抽象类。
- 可以动态改变环境角色的策略行为。
不同语言设计模式源码下载:
UML
Java代码
状态基础接口
// Strategy.java 基础策略接口
public interface Strategy
public void run();
策略实现类
// StrategyA.java 策略A
public class StrategyA implements Strategy
@Override
public void run()
System.out.println("StrategyA::run().");
// StrategyB.java 策略B
public class StrategyB implements Strategy
@Override
public void run()
System.out.println("StrategyB::run().");
// StrategyC.java 策略C
public class StrategyC implements Strategy
@Override
public void run()
System.out.println("StrategyC::run().");
抽象业务类
// Context.java 抽象业务类,聚合策略对象
public abstract class Context
protected Strategy strategy;
public void setStrategy(Strategy strategy)
this.strategy = strategy;
public void action()
this.strategy.run();
具体业务类
// ContextCat.java 业务类构造器聚合了某策略
public class ContextCat extends Context
public ContextCat()
// 使用某个策略
System.out.println("ContextCat::setStrategy(StrategyC).");
this.setStrategy(new StrategyC());
// ContextDog.java 业务类构造器聚合了某策略
public class ContextDog extends Context
public ContextDog()
// 使用某个策略
System.out.println("ContextDog::setStrategy(StrategyB).");
this.setStrategy(new StrategyB());
测试调用
/**
* 策略模式就是根据需要给对象绑定具体策略,使得具体实现和策略可以灵活搭配。
* 先声明某个具体Context对象,该对象已经绑定了具体策略,同时还可以更改策略。
*/
// 实例化某个内容,策略已经绑定上
Context contextCat = new ContextCat();
contextCat.action();
// 重新设置策略
System.out.println("reset contextCat\'strategy to StrategyA.");
contextCat.setStrategy(new StrategyA());
contextCat.action();
// 实例化某个内容,策略已经绑定上
Context contextGog = new ContextDog();
contextGog.action();
Go代码
状态基础接口
// Strategy.go 基础策略接口
// 定义一个策略接口,注意go语言数据类型即接口
type Strategy interface
Run()
// 写在接口文件的其他全局方法
func Init()
fmt.Println("strategy init!")
策略实现类
// StrategyA.go 策略A
type StrategyA struct
// 实现策略接口的对应方法
func (s *StrategyA) Run()
fmt.Println("StrategyA::Run")
// StrategyB.go 策略B
type StrategyB struct
// 实现策略接口的对应方法
func (s *StrategyB) Run()
fmt.Println("StrategyB::Run")
// StrategyC.go 策略C
type StrategyC struct
// 实现策略接口的对应方法
func (s *StrategyC) Run()
fmt.Println("StrategyC::Run")
抽象业务类
// Context.go 抽象业务类,聚合策略对象
type Context struct
strategy Strategy
// 设置不同strategy,方法名首字母大写
func (c *Context) SetStrategy(s Strategy)
c.strategy = s
// 执行策略接口里面的方法
func (c *Context) Run()
c.strategy.Run()
具体业务类
// ContextCat.go 业务类构造器聚合了某策略
// 定义具体执行对象,Go没有继承,用聚合来调用Context里的函数
type ContextCat struct
context Context
// 可提前绑定具体的策略
func (c *ContextCat) Init()
c.context.SetStrategy(&StrategyC)
fmt.Println("ContextCat::init. setStrategy(StrategyC)")
// 调用策略方法
func (c *ContextCat) Run()
fmt.Println("ContextCat::run")
c.context.Run()
// ContextDog.go 业务类构造器聚合了某策略
type ContextDog struct
context Context
// 可提前绑定具体的策略
func (c *ContextDog) Init()
c.context.SetStrategy(&StrategyB)
fmt.Println("ContextDog::init. setStrategy(StrategyB)")
// 调用策略方法
func (c *ContextDog) Run()
fmt.Println("ContextDog::run")
c.context.Run()
测试调用
func main()
fmt.Println("test start:")
// 这里src.Init来自strategy.go文件
src.Init()
/**
* 策略模式就是根据需要给对象绑定具体策略,使得具体实现和策略可以灵活搭配。
* 先声明某个具体Context对象,该对象已经绑定了具体策略,同时还可以更改策略。
*/
// 声明策略执行对象
context := src.Context
// 设置策略A
context.SetStrategy(&src.StrategyA)
// 执行策略A,打印StrategyA
context.Run()
// 设置策略B
context.SetStrategy(&src.StrategyB)
// 执行策略B,打印StrategyB
context.Run()
// 执行策略C,打印StrategyC
context.SetStrategy(&src.StrategyC)
context.Run()
// /*********************** 分割线 ******************************************/
// 直接实例化具体执行对象,策略已经绑定
contextCat := src.ContextCat
contextCat.Init()
contextCat.Run()
// 直接实例化具体执行对象,策略已经绑定
contextDog := src.ContextDog
contextDog.Init()
contextDog.Run()
更多语言版本
不同语言设计模式源码:https://github.com/microwind/design-pattern
设计模式10-策略模式与责任链模式详解
1.10.策略模式与责任链模式详解
1.10.1.策略模式详解
时长:1h15min
10.1.1.策略模式的定义
定义:
策略模式【Strategy Pattern】,又叫政策模式【Policy Pattern】,它是将定义的算法家族,分别封装起来,让它们之间可以相互替换,从而
让算法的变化不会影响到使用算法的用户。
可以避免多重分支的if...else...和switch语句。
属于行为型模式。
10.1.1.1.策略模式在生活中应用场景
阶梯个税【工资收入不同,个税算法不同】
移动支付方式选择【微信,支付宝,银联】
出行交通方式选择【火车,飞机,汽车,轮船】
10.1.1.1、2.策略模式的应用场景
1.假如系统中有很多类,而他们的区别仅仅在于他们的行为 不同。
2.一个系统需要动态地在几种算法中选择一种。
3.需要屏蔽算法规则。
10.1.2.策略模式的通用实现
10.1.2.1.类图设计
10.1.2.2.代码实现
1.顶层策略接口
package com.wf.strategy.general; /** * @ClassName IStrategy * @Description 顶层策略接口 * @Author wf * @Date 2020/6/18 10:11 * @Version 1.0 */ public interface IStrategy { /** * 算法接口 */ void algorithm(); }
2.策略子类实现
package com.wf.strategy.general; /** * @ClassName ConcreteStrategyA * @Description 具体策略子类A * @Author wf * @Date 2020/6/18 10:13 * @Version 1.0 */ public class ConcreteStrategyA implements IStrategy { @Override public void algorithm() { System.out.println("这是算法A"); } } package com.wf.strategy.general; /** * @ClassName ConcreteStrategyB * @Description 具体策略子类B * @Author wf * @Date 2020/6/18 10:13 * @Version 1.0 */ public class ConcreteStrategyB implements IStrategy { @Override public void algorithm() { System.out.println("这是算法B"); } }
3.上下文对象
package com.wf.strategy.general; /** * @ClassName Context * @Description 上下文对象 * @Author wf * @Date 2020/6/18 10:14 * @Version 1.0 */ public class Context { private IStrategy strategy; public Context(IStrategy strategy) { this.strategy = strategy; } //由构造器中传参实现子类类型,来决定选择哪一种算法 public void algorithm(){ this.strategy.algorithm(); } }
4.测试类
package com.wf.strategy.general; /** * @ClassName Test * @Description 测试类 * @Author wf * @Date 2020/6/18 10:15 * @Version 1.0 */ public class Test { public static void main(String[] args) { //使用时,客户选择一种策略 IStrategy strategy = new ConcreteStrategyA(); //封装到上下文中 Context context = new Context(strategy); //调用策略方法 context.algorithm(); } }
测试结果如下:
10.1.3.策略模式的实现示例之促销案例
10.1.3.1.代码实现
1.顶层接口
package com.wf.strategy.demo.promotion; /** * @ClassName IPromotionStrategy * @Description 促销策略接口 * @Author wf * @Date 2020/6/18 10:31 * @Version 1.0 */ public interface IPromotionStrategy { /** * 促销方法 */ void doPromotion(); }
2.实现子类
package com.wf.strategy.demo.promotion; /** * @ClassName GroupPurchaseStrategy * @Description 团购促销 * @Author wf * @Date 2020/6/18 10:36 * @Version 1.0 */ public class GroupPurchaseStrategy implements IPromotionStrategy{ @Override public void doPromotion() { System.out.println("5人成团,可以优惠"); } } package com.wf.strategy.demo.promotion; /** * @ClassName CashRollbackStrategy * @Description 返现促销 * @Author wf * @Date 2020/6/18 10:34 * @Version 1.0 */ public class CashRollbackStrategy implements IPromotionStrategy { @Override public void doPromotion() { System.out.println("返现,直接打款到支付宝帐号"); } } package com.wf.strategy.demo.promotion; /** * @ClassName CouponStrategy * @Description 优惠券促销方式 * @Author wf * @Date 2020/6/18 10:33 * @Version 1.0 */ public class CouponStrategy implements IPromotionStrategy { @Override public void doPromotion() { System.out.println("使用优惠券抵扣"); } } package com.wf.strategy.demo.promotion; /** * @ClassName EmptyStrategy * @Description 无优惠购买 * @Author wf * @Date 2020/6/18 10:37 * @Version 1.0 */ public class EmptyStrategy implements IPromotionStrategy { @Override public void doPromotion() { System.out.println("原价购买,无优惠"); } }
3.测试类
public static void main(String[] args) { String promotion = ""; IPromotionStrategy strategy = null; if("团购".equals(promotion)){ strategy = new GroupPurchaseStrategy(); }else if("".equals(promotion)){ //... strategy = new EmptyStrategy(); } strategy.doPromotion(); }
测试结果:
说明:
这里并没有使用策略模式,而是通过分支判断,多态的方式来实现。
4.策略模式运用---增加上下文对象
package com.wf.strategy.demo.promotion; /** * @ClassName PromotionActivity * @Description 上下文对象,促销活动 * @Author wf * @Date 2020/6/18 10:45 * @Version 1.0 */ public class PromotionActivity { private IPromotionStrategy strategy; public PromotionActivity(IPromotionStrategy strategy) { this.strategy = strategy; } public void execute(){ strategy.doPromotion(); } }
修改测试代码如下:
public static void main(String[] args) { String promotion = ""; PromotionActivity activity = null; if("团购".equals(promotion)){ activity = new PromotionActivity(new GroupPurchaseStrategy()); }else if("".equals(promotion)){ //... activity = new PromotionActivity(new EmptyStrategy()); } activity.execute(); //说明: //这里仍然需要进行if...else分支判断,代码仍然不够优雅 //解决这个问题,通常使用策略模式 + 简单工厂模式 }
说明:
//这里仍然需要进行if...else分支判断,代码仍然不够优雅 //解决这个问题,通常使用策略模式 + 简单工厂模式
5.引入简单工厂优化
package com.wf.strategy.demo.promotion; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * @ClassName PromotionStrategyFactory * @Description 上下文对象创建工厂 * @Author wf * @Date 2020/6/18 10:52 * @Version 1.0 */ public class PromotionStrategyFactory { private static Map<String,IPromotionStrategy> promotionStrategyMap = new HashMap<String,IPromotionStrategy>(); private static final IPromotionStrategy EMPTY = new EmptyStrategy(); static{ promotionStrategyMap.put(PromotionKey.COUPON,new CouponStrategy()); promotionStrategyMap.put(PromotionKey.CASH_ROLLBACK,new CashRollbackStrategy()); promotionStrategyMap.put(PromotionKey.GROUP_PURCHASE,new GroupPurchaseStrategy()); } private PromotionStrategyFactory(){} public static IPromotionStrategy getPromotionStrategy(String promotionKey){ IPromotionStrategy strategy = promotionStrategyMap.get(promotionKey); return strategy == null ? EMPTY : strategy; } //定义内部接口,定义key private interface PromotionKey{ String COUPON = "COUPON"; String CASH_ROLLBACK = "CASH_ROLLBACK"; String GROUP_PURCHASE = "GROUP_PURCHASE"; } public Set<String> getPromotionKeys(){ return promotionStrategyMap.keySet(); } }
修改测试类:
public static void main(String[] args) { String promotionKey = "COUPON"; IPromotionStrategy strategy = PromotionStrategyFactory.getPromotionStrategy(promotionKey); strategy.doPromotion(); }
测试结果如下:
说明:
分支判断,移到工厂类中去处理。
10.1.3.2.系统类图
10.1.4.策略模式的实现示例之支付方式选择案例
10.1.4.1.代码实现
1.顶层接口
package com.wf.strategy.demo.pay.payport; import com.wf.strategy.demo.pay.MsgResult; /** * @ClassName Payment * @Description 支付抽象接口 * @Author wf * @Date 2020/6/18 11:31 * @Version 1.0 */ public abstract class Payment { public abstract String getName(); public MsgResult pay(String uid, double amount){ //查询余额是否足够 if(queryBalance(uid) < amount){ return new MsgResult(500,"支付失败","余额不足"); } return new MsgResult(200,"支付成功","支付金额:"+amount); } protected abstract double queryBalance(String uid); }
2.子类实现
package com.wf.strategy.demo.pay.payport; /** * @ClassName WechatPay * @Description 微信支付 * @Author wf * @Date 2020/6/18 14:27 * @Version 1.0 */ public class WechatPay extends Payment { @Override public String getName() { return "微信支付"; } @Override protected double queryBalance(String uid) { return 500; } } package com.wf.strategy.demo.pay.payport; /** * @ClassName JDPay * @Description 京东白条支付 * @Author wf * @Date 2020/6/18 14:25 * @Version 1.0 */ public class JDPay extends Payment { @Override public String getName() { return "京东白条"; } @Override protected double queryBalance(String uid) { return 500; } } package com.wf.strategy.demo.pay.payport; /** * @ClassName BankUnionPay * @Description 银联支付 * @Author wf * @Date 2020/6/18 14:28 * @Version 1.0 */ public class BankUnionPay extends Payment { @Override public String getName() { return "银联支付"; } @Override protected double queryBalance(String uid) { return 120; } } package com.wf.strategy.demo.pay.payport; /** * @ClassName AliPay * @Description 支付宝支付 * @Author wf * @Date 2020/6/18 14:23 * @Version 1.0 */ public class AliPay extends Payment { @Override public String getName() { return "支付宝"; } @Override protected double queryBalance(String uid) { return 900; } }
3.订单类
package com.wf.strategy.demo.pay; import com.wf.strategy.demo.pay.payport.PayStrategy; import com.wf.strategy.demo.pay.payport.Payment; /** * @ClassName Order * @Description 订单类 * @Author wf * @Date 2020/6/18 14:29 * @Version 1.0 */ public class Order { private String uid; private String orderId; private double amount; public Order(String uid, String orderId, double amount) { this.uid = uid; this.orderId = orderId; this.amount = amount; } public MsgResult pay(){ return pay(PayStrategy.DEFAULT_PAY); } public MsgResult pay(String payKey){ Payment payment = PayStrategy.get(payKey); System.out.println("欢迎使用:"+payment.getName()); System.out.println("本次交易金额为:"+amount+",开始扣款"); return payment.pay(uid,amount); } }
4.上下文对象
package com.wf.strategy.demo.pay.payport; import java.util.HashMap; import java.util.Map; /** * @ClassName PayStrategy * @Description 支付策略,上下文对象 * @Author wf * @Date 2020/6/18 14:32 * @Version 1.0 */ public class PayStrategy { public static final String ALI_PAY = "AliPay"; public static final String JD_PAY = "JdPay"; public static final String WECHAT_PAY = "WechatPay"; public static final String BANKUINION_PAY = "BankUnionPay"; public static final String DEFAULT_PAY = "AliPay"; private static Map<String,Payment> strategyMap = new HashMap<String, Payment>(); static { strategyMap.put(ALI_PAY,new AliPay()); strategyMap.put(JD_PAY,new JDPay()); strategyMap.put(WECHAT_PAY,new WechatPay()); strategyMap.put(BANKUINION_PAY,new BankUnionPay()); } public static Payment get(String payKey){ if(!strategyMap.containsKey(payKey)){ return strategyMap.get(DEFAULT_PAY); } return strategyMap.get(payKey); } }
5.结果集po
package com.wf.strategy.demo.pay; /** * @ClassName MsgResult * @Description 结果集 * @Author wf * @Date 2020/6/18 11:36 * @Version 1.0 */ public class MsgResult { private int code; private Object data; private String msg; public MsgResult(int code, String msg, Object data) { this.code = code; this.data = data; this.msg = msg; } @Override public String toString() { return "MsgResult{" + "code=‘" + code + ‘‘‘ + ", data=" + data + ", msg=‘" + msg + ‘‘‘ + ‘}‘; } }
6.测试类
package com.wf.strategy.demo.pay; import com.sun.org.apache.xpath.internal.operations.Or; import com.wf.strategy.demo.pay.payport.PayStrategy; /** * @ClassName Test * @Description 测试类 * @Author wf * @Date 2020/6/18 14:43 * @Version 1.0 */ public class Test { public static void main(String[] args) { Order order = new Order("1","20200618012222",324.5); System.out.println(order.pay(PayStrategy.BANKUINION_PAY)); } }
测试结果如下:
10.1.4.2.类图设计
10.1.5.策略模式在源码中应用
10.1.5.1.jdk中Comparator接口
int compare(T o1, T o2);
表示列表中前后两个元素进行比较,如果返回值大于0,表示升序。
它的实现类,在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(); }
默认策略中调用:
static final class NaturalOrder implements Comparator<Object> { @SuppressWarnings("unchecked") public int compare(Object first, Object second) { return ((Comparable<Object>)first).compareTo(second); } static final NaturalOrder INSTANCE = new NaturalOrder(); }
10.1.5.2.spring中Resource接口
10.1.5.3.spring中InstantiationStrategy接口
10.1.6.策略模式使用总结
10.1.6.1.优缺点总结
优点:
符合开闭原则
避免使用多重条件判断,如:if...else if...,switch
使用策略模式可以提高算法的保密性和安全性。
缺点:
客户端必须要知道所有的策略,并且自行选择使用哪一个策略。
可以通过字典表配置成枚举参数,当用户点击时,弹出所有策略。任选一个。
代码中会增加非常多的策略类,增加维护难度 。
1.10.2.责任链模式详解
以上是关于策略设计模式详解C/Java/JS/Go/Python/TS不同语言实现的主要内容,如果未能解决你的问题,请参考以下文章