策略设计模式详解C/Java/JS/Go/Python/TS不同语言实现

Posted 刀法如飞-专注算法与设计模式

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了策略设计模式详解C/Java/JS/Go/Python/TS不同语言实现相关的知识,希望对你有一定的参考价值。

简介

策略模式(Strategy Pattern)属于行为型设计模式。将每一个算法封装到具有共同接口的独立类中,根据需要来绑定策略,使得具体实现和策略解耦。

当你想使用对象中各种不同的算法变体,使用if...else 所带来的复杂和难以维护,可使用策略模式。或者当有许多相同类,它们仅在执行某些行为时略有不同,可使用策略模式。

作用

  1. 策略算法可以自由切换,保持策略与执行类的松耦合。
  2. 避免使用多重条件判断,不同环境角色可以组装多个策略。
  3. 扩展性良好,可以随时增删策略行为。
  4. 体现了多用组合,少用继承。

实现步骤

  1. 建立一个策略接口。
  2. 新建多个策略行为类,实现该策略接口。
  3. 建立一个抽象环境角色类,并将策略接口组合进来。是否需要抽象类可选。
  4. 建立多个环境角色类来继承该抽象类。
  5. 可以动态改变环境角色的策略行为。

 

不同语言设计模式源码下载:

https://github.com/microwind/design-pattern

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不同语言实现的主要内容,如果未能解决你的问题,请参考以下文章

行为型设计模式 - 策略模式详解

行为型设计模式 - 策略模式详解

设计模式之策略模式(Strategy)详解及代码示例

Android设计模式之策略模式详解

设计模式之策略模式与责任链模式详解和应用

设计模式-策略模式详解