设计模式【12】-- 搞定最近大火的策略模式

发布时间:2022-06-21 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了设计模式【12】-- 搞定最近大火的策略模式脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

开局还是那种图,最近策略模式貌似很火,各位客官往下看...

设计模式【12】-- 搞定最近大火的策略模式

策略模式到底是什么

前面我们其实已经将结构型模式讲解完了,剩下的全都是行为型模式,三种模式的区分:

  • 创建型模式:如何创建一个对象
  • 结构型模式:对象内部的构造是如何构造的
  • 行为型模式:对象是如何运行(可以做什么)

而提到策略模式,我们该如何理解呢?

  • 从北京到上海,可以坐飞机,也可以坐动车,也可以坐绿皮,甚至可以骑自行车,这些不同的方式,就是策略。

  • 一件商品,可以直接打 5 折,也可以加 100 再打 6 折,也可以打 5.5 折之后返回现金券,这些也是策略。

  • 带了 200 去超市买东西,可以买零食,也可以买生活用品,也可以啥也不买,这些也是策略。

上面的例子,其实我们可以发现不同的策略其实是可以互换的,甚至策略细节之间可以混搭,那么很多时候我们为了方便拓展,方便替换策略,对调用方屏蔽不同的策略细节,就可以使用策略模式。倘若所有的策略全部写在一个类里面,可以是可以,这样维护代码会越来越复杂,维护只会越来越困难,里面会充斥着各种if-else,算法如果日益复杂,动一发而牵全身,那就只剩下提桶跑路了。

策略模式是指有一定行动内容的相对稳定的策略名称。策略模式在古代中又称“计策”,简称“计”,如《汉书·高帝纪上》:“汉王从其计”。这里的“计”指的就是计谋、策略。策略模式具有相对稳定的形式,如“避实就虚”、“出奇制胜”等。一定的策略模式,既可应用于战略决策,也可应用于战决策;既可实施于大系统的全局性行动,也可实施于大系统的局部性行动。

@H_126_44@

再说一个打工人都知道的例子,比如每个人都要交个人所得税,我们知道个人所得税的计算方式是很复杂的,税法计算就是一个行为,而可能存在不同实现算法,那每一种计算算法都是一种策略。

这些策略可能随时被替换掉,那么我们为了好替换,相互隔离,就定义一系列算法,封装起来,这就叫策略模式。

策略模式的角色

策略模式一般有三种角色:

  • 抽象的策略类(Strategy):将策略的行为抽象出公共接口
  • 具体的策略类(concreteStrategy):以Strategy接口实现某些具体的算法,具体的策略可能多个,也可以相互替换
  • 上下文(Context): 一般是封装了策略,以及策略的使用方法,对上层调用屏蔽了细节,直接调用即可。

设计模式【12】-- 搞定最近大火的策略模式

策略模式的demo

创建一个抽象的策略接口:

public interface Strategy {

    int DOStrategy();
}

创建 2 个具体的策略类,分别执行策略:

public class Concretestrategy1 implements Strategy{
    @override
    public int doStrategy() {
        System.out.PRintln("执行策略1...");
        return 1;
    }
}

public class ConcreteStrategy2 implements Strategy{
    @Override
    public int doStrategy() {
        System.out.println("执行策略2...");
        return 2;
    }
}

创建上下文类,封装策略类:

public class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void executeStrategy(){
        strategy.doStrategy();
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        Context context1 = new Context(new ConcreteStrategy1());
        context1.executeStrategy();

        context1 = new Context(new ConcreteStrategy2());
        context1.executeStrategy();
    }
}

测试结果:

执行策略1
执行策略2

可以看到只要使用不同的策略,就可以执行不同的结果。

红包策略实战

比如,工作中有涉及到分配的策略,场景就是有红包,分配给若干个人,但是会有不同分配策略。这里不同的分配策略其实就是用策略模式来实现。可以随机分配,也可以平均分配,还可以根据各种权重来分配等等。这里只演示两种分配:

首先定义一个红包类,包含红包金额(我们以分为单位,保存整数,避免金额小数不准确问题

public class redPackage {
    public int remainSize;

    public int remainMoney;

    public RedPackage(int remainSize, int remainMoney) {
        this.remainSize = remainSize;
        this.remainMoney = remainMoney;
    }
}

定义一个分配策略的抽象类:

import java.util.List;

public interface AllocateStrategy {

    List<Integer> allocate(RedPackage redPackage);

}

平均分配的时候,如果不能平均,那么第一位会有所增减:

import java.util.ArrayList;
import java.util.List;

public class AverageAllocateStrategy implements AllocateStrategy {
    @Override
    public List<Integer> allocate(RedPackage redPackage) {
        List<Integer> results = new ArrayList<>();
        Integer sum = redPackage.remainMoney;
        Integer average = sum / redPackage.remainSize;
        for (int i = 0; i < redPackage.remainSize; i++) {
            sum = sum - average;
            results.add(average);
        }
        if (sum != 0) {
            results.set(0, results.get(0) + sum);
        }
        return results;
    }
}

随机分配的时候,我们将每一份随机添加到红包里面去,但是这样不能保证每一份都有金额(注意这个不要在生产使用,仅限测试


import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class RandomAllocateStrategy implements AllocateStrategy {
    @Override
    public List<Integer> allocate(RedPackage redPackage) {
        return ranRedPackage(redPackage.remainSize, redPackage.remainMoney);
    }

    public List<Integer> ranRedPackage(Integer count, Integer money) {
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            result.add(0);
        }
        for (int i = 1; i <= money; i++) {
            int n = new Random().nextInt(count);
            result.set(n, result.get(n) + 1);
        }
        return result;
    }

}

定义上下文类,封装不同的策略:

import java.util.List;

public class Context {
    private AllocateStrategy strategy;

    public Context(AllocateStrategy strategy) {
        this.strategy = strategy;
    }

    public List<Integer> executeStrategy(RedPackage redPackage){
        return strategy.allocate(redPackage);
    }
}

测试类:

import java.util.List;

public class Test {
    public static void main(String[] args) {
        RedPackage redPackage = new RedPackage(10,102);
        Context context = new Context(new RandomAllocateStrategy());
        List<Integer> list =context.executeStrategy(redPackage);
        list.foreach(l-> System.out.print(l+" "));

        System.out.println("");
        context = new Context(new AverageAllocateStrategy());
        list =context.executeStrategy(redPackage);
        list.forEach(l-> System.out.print(l+" "));
    }
}

可以看到分配的金额确实会随着策略类的不同发生不同的变化:

9 10 16 8 14 8 7 15 9 6 
12 10 10 10 10 10 10 10 10 10 

注意这个不能在生产使用!!!

优缺点

策略模式的优点:

  • 消除if-else语句,不同策略相互隔离,方便维护
  • 切换自由
  • 拓展性好,实现接口即可

策略模式的缺点:

  • 策略一旦数量过多,维护也会相对比较难,复用代码比较难
  • 所有的策略类都对外暴露了,虽然一般通过Context上下文调用

策略模式比较常用,可能有些同学会混淆策略模式和状态模式:

相对于状态模式:策略模式只会执行一次方法,而状态模式会随着状态变化不停的执行状态变更方法。举个例子,我们从A地方去B地方,策略就是飞机或者火车,状态模式则是可能到某一个地方,换了一种交通方式,到另外一个地方又换了一种交通方式。

小结

策略模式比较常用,核心就是隔离不同的策略,将具体的算法封装在策略里面,抽象出一个策略抽象类,把不同具体策略类注入到上下文类中,达到选择不同策略的目的。

但是如果策略很多,需要考虑复用,策略类对外暴露的时候需要考虑滥用风险,会破坏封装性。

【作者简介】: 秦怀,公众号秦怀杂货店】作者,个人网站:http://aphysia.cn,技术之路不在一时,山高水长,纵使缓慢,驰而不息。

设计模式系列:

  • 设计模式【1】-- 单例模式到底几种写法?
  • 设计模式【1.1】-- 你想如何破坏单例模式?
  • 设计模式【1.2】-- 枚举式单例有那么好用么?
  • 设计模式【1.3】-- 为什么饿汉式单例是线程安全的?
  • 设计模式【2】-- 简单工厂模式了解一下?
  • 设计模式【2.1】-- 简单工厂模式怎么演变成工厂方法模式?
  • 设计模式【2.2】-- 工厂模式怎么演变成抽象工厂模式?
  • 设计模式【3.1】-- 浅谈代理模式之静态、动态、cglib代理
  • 设计模式【3.2】-- JDK动态代理源码分析有多香?
  • 设计模式【3.3】-- CGLIB动态代理源码解读
  • 设计模式【4】-- 建造者模式详解
  • 设计模式【5】-- 原型模式
  • 设计模式【6.1】-- 初探适配器模式
  • 设计模式【6.2】-- 再聊聊适配器模式
  • 设计模式【7】-- 探索一下桥接模式
  • 设计模式【8】-- 手工耿教我写装饰器模式
  • 设计模式【9】-- 外观模式?没那么高大上
  • 设计模式【10】-- 顺便看看享元模式
  • 设计模式【11】-- 搞定组合模式

脚本宝典总结

以上是脚本宝典为你收集整理的设计模式【12】-- 搞定最近大火的策略模式全部内容,希望文章能够帮你解决设计模式【12】-- 搞定最近大火的策略模式所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。