首页 > 其他分享 >【设计模式】策略模式Strategy:解决不同活动策略营销推荐场景

【设计模式】策略模式Strategy:解决不同活动策略营销推荐场景

时间:2023-09-13 19:01:38浏览次数:48  
标签:recommand 策略 模式 Strategy 算法 设计模式 public 客户端

(目录)


模板方法模式能帮助我们进行公有方法的抽取,起到快速复用和扩展的作用

另一种快速复用和扩展代码的行为型模式策略模式

策略模式在实际的开发中很常用,最常见的应用场景是利用它来替换过多的 if-else 嵌套的逻辑判断。

除此之外,还能结合工厂模式给客户端提供非常灵活的使用体验

image.png


原理

策略模式的原始定义是:定义一系列算法,封装每个算法,并使它们可以互换。策略让算法独立于使用它的客户端而变化。

在这个定义中,策略模式明确表示应当由客户端自己决定在什么样的情况下使用哪些具体的策略。

也就是说,服务端是作为一个策略的整体调控者,具体选择运行哪些策略其实是要交给客户端来决定的。

比如,压缩文件的时候,提供一系列的不同压缩策略,像 gzip、zip 等,至于客户端在什么时候使用 gzip,由客户端自行去决定。同时,gzip 还可以被替换为其他的压缩策略。

策略模式 UML 图的结构:

image-20230913183042797

策略模式包含了三个关键角色:

  • 上下文信息类(Context): 用于存放和执行需要使用的具体策略类以及客户端调用的逻辑;
  • 抽象策略类(Strategy):定义策略的共有方法;
  • 具体策略类(StrategyA 等):实现抽象策略类定义的共有方法。

UML 对应的代码实现:

public interface IStrategy {
    void operation();
}

public class Context {
    public void request(IStrategy s) {
        s.operation();
    }
}

public class StrategyA implements IStrategy{
    @Override
    public void operation() {
        System.out.println("=== 执行策略 A ......");
    }
}

public class StrategyB implements IStrategy{
    @Override
    public void operation() {
        System.out.println("=== 执行策略 B ......");
    }
}

策略模式的本质就是通过上下文信息类来作为中心控制单元,对不同的策略进行调度分配。


使用场景

策略模式常见的使用场景有以下几种:

  • 系统中需要动态切换几种算法的场景
  • 使用多重的条件选择语句来实现的业务场景
  • 只希望客户端选择已经封装好的算法场景而不用关心算法实现细节
  • 分离使用策略和创建策略的场景

使用策略模式的基本流程:

  1. 定义策略 API——PromotionStrategy,每一种促销策略的算法都要实现该接口。该接口有一个 recommand 方法,接收并返回一个 int 对象,返回的就是推荐后可以参加的促销活动。实际上,推荐返回的可能是一个活动对象,这里用简单的数字代替:
  2. ,推荐返回的可能是一个活动对象,这里用简单的数字代替:
public interface IPromotionStrategy {
    /**
     * 返回1 代表 可以参加 满减活动
     * 返回2 代表 可以参加 N折优惠活动
     * 返回3 代表 可以参加 M元秒杀活动
     */
    int recommand(String skuId);
}
  1. 定义三个类实现了 PromotionStrategy 接口,分别代表满减策略、N 折扣优惠活动策略和 M 元秒杀活动策略,类分别是 FullReduceStrategy、NPriceDiscountStrategy 和 MSpikeStrategy:
public class FullReduceStrategyImpl implements IPromotionStrategy {
    @Override
    public int recommand(String skuId) {
        System.out.println("=== 执行 满减活动");
        //推荐算法和逻辑写这里
        return 1;
    }
}

public class MSpikeStrategyImpl implements IPromotionStrategy {
    @Override
    public int recommand(String skuId) {
        System.out.println("=== 执行 M 元秒杀活动");
        //推荐算法和逻辑写这里
        return 3;
    }
}

public class NPriceDiscountStrategyImpl implements IPromotionStrategy {
    @Override
    public int recommand(String skuId) {
        System.out.println("=== 执行 N 折扣优惠活动");
        //推荐算法和逻辑写这里
        return 2;
    }
}
  1. 实现促销推荐的上下文信息类 Promotional,这里是存储和使用策略的地方。类中有一个 recommand 方法,用于执行推荐策略

它的构造函数有一个 PromotionStrategy 参数,可以在运行期间使用该参数决定使用哪种促销策略

public class Promotional {
    private final IPromotionStrategy strategy;
    public Promotional(IPromotionStrategy strategy) {
        this.strategy = strategy;
    }
    public void recommand(String skuId) {
        strategy.recommand(skuId);
    }
}
  1. 简单的单元测试代码来看下具体的运行结果:
public class Client {
    public static void main(String[] args) {
        Promotional fullReducePromotional = new Promotional(new FullReduceStrategyImpl());
        fullReducePromotional.recommand("1122334455");
        Promotional nPriceDiscountPromotional = new Promotional(new NPriceDiscountStrategyImpl());
        nPriceDiscountPromotional.recommand("6677889900");
        Promotional mSpikePromotional = new Promotional(new MSpikeStrategyImpl());
        mSpikePromotional.recommand("11335577");
    }
}

// 输出结果
// === 执行 满减活动
// === 执行 N 折扣优惠活动
// === 执行 M 元秒杀活动

使用策略模式的理由

使用策略模式的原因,主要有以下三个:

  • 第一个,为了提升代码的可维护性。 在实际开发中,有许多算法可以实现某一功能,如查找、排序等,通过 if-else 等条件判断语句来进行选择非常方便。但是这就会带来一个问题:当在这个算法类中封装了大量查找算法时,该类的代码就会变得非常复杂,维护也会突然就变得非常困难。 虽然策略模式看上去比较笨重,但实际上在每一次新增策略时都通过新增类来进行隔离,短期虽然不如直接写 if-else 来得效率高,但长期来看,维护单一的简单类耗费的时间其实远远低于维护一个超大的复杂类;
  • 第二个,为了动态快速地替换更多的算法。 策略模式最大的作用在于分离使用算法的逻辑和算法自身实现的逻辑,这样就意味着当想要优化算法自身的实现逻辑时就变得非常便捷,一方面可以采用最新的算法实现逻辑,另一方面可以直接弃用旧算法而采用新算法。使用策略模式能够很方便地进行替换;
  • 第三个,为了应对需要频繁更换策略的场景。 比如,用户推荐类场景。特别是对于一些 C 端产品来说,在获取了用户的反馈数据后,会根据用户的特性制定不同的运营策略,这时如果采用 if-else 的方式编码,那么每一次的策略变化都会导致系统代码的修改,从运营的角度看是不可接受的,而采用策略模式就能很容易地解决这个问题。

优缺点

使用策略模式主要有以下几个优点:

  1. 提供良好的代码扩展性。 每一个策略都是对应生成一个新的具体策略类,满足开闭原则,同时满足里氏替换原则,可以任意替换相同的策略,这样用户可以在不修改原有系统的基础上选择算法或行为,同时也可以灵活地增加新的算法或行为;
  2. 提供了一种管理多个不同算法策略的办法。 策略模式提供了一种很好的思路,可以将算法的实现和使用算法的代码隔离开来,这样就能很好地管理不同的算法;
  3. 提供使用组合替换继承的办法。 策略模式使用组合的方式来替代继承,避免了子类出现异常而影响父类;
  4. 降低使用多重条件(if-else)嵌套语句的理解难度。 在实际的开发中,使用 if-else 是非常常见的编程方法,但是随着业务逻辑变得越来越复杂,如果一味地增加 if-else,会让代码变得非常难以理解和维护,使用策略模式则能避免这些问题的出现;
  5. 在运行时动态切换算法,提升代码灵活性。 由于策略模式将算法的选择权交给了客户端,那么客户端可以根据自身的需求灵活地切换算法。

缺点:

  1. 客户端的学习成本变高。 虽然策略模式让客户端自行决定使用哪一个策略,看上去很自由,但实际上隐含着客户必须要知道所有的策略才能做选择的事实。一旦新增或修改策略,客户端都需要知道;
  2. 具体策略类的数量会剧增,增加维护成本。 由于每一个策略都对应一个具体策略类,所以当策略比较庞大时,需要维护的类数量也会激增;
  3. 不如函数式编程简洁。 现在有很多编程语言都支持函数式——允许在一组匿名函数中实现不同版本的算法。对于一些小型的策略来说,使用函数式编程就能解决问题,但使用策略模式反而过于复杂。

总结

策略模式最大的用处是能在运行时改变代码的算法行为,同时给使用者提供一种可以根据情况来选择算法的途径。

虽然策略模式是一个比较容易理解和使用的设计模式,但是却增加了使用者的难度,因为可能需要在了解了所有的策略后才能做出决策。即便是类似排序这样简单的算法,不同使用者的选择也可能完全不同,如果交给使用者来选择,就意味着使用者需要了解不同排序算法的优劣,才能更好地做出选择。

不过,策略模式对算法起到了很好的封装作用,通过使用算法和创建算法的分离,将算法实现的复杂性放到了子类去解决。同时,策略模式还可以随时进行替换,对于一些老旧的算法,可以很方便地进行替换和升级。


标签:recommand,策略,模式,Strategy,算法,设计模式,public,客户端
From: https://blog.51cto.com/panyujie/7463287

相关文章

  • 七牛云存储____容灾备份策略
    ......
  • 设计模式-观察者模式
    设计模式提供了软件开发过程中的一些最佳实践,可以帮助我们解决常见的编程问题,提高软件的可维护性和可复用性,并使我们的代码更加健壮和灵活。设计模式可以带来以下好处:提高代码的可读性和可维护性、提高软件的可复用性、提高开发效率、提高系统的灵活性和可扩展性。今天我们讲一下观......
  • 《精通Python设计模式》 PDF电子书 +源码
    第1章工厂模式第2章建造者模式第3章其他创建型模式第4章适配器模式第5章装饰器模式第6章桥接模式第7章外观模式第8章其他结构型模式第9章职责链模式第10章命令模式第11章观察者模式第12章状态模式第13章其他行为型模式第14......
  • 设计模式-门面模式
    门面模式(文章目录)1、什么是门面模式  门面模式为子系统提供一组统一的接口,定义一组高层接口让子系统更易用。注意这里针对的是接口。  这个定义很简洁,我再进一步解释一下。  假设有一个系统A,提供了a、b、c、d四个接口。系统B完成某个业务功能,需要调用A系统的......
  • 6. 前端设计模式之观察者模式
    使用观察者模式,我们可以将某些对象(观察者)订阅到另一个对象(可观察对象)。当一个事件发生时,可观察对象会通知它的所有观察者!这个模式出境的概率就比较高了,无论是在前端还是后端,都能见到它的身影,特别跟事件有关的场景。从定义看这个模式涉及到两种对象,一种可观察对象也就是观察的......
  • 如何在 Linux 中使用 Chage 命令,修改Linux系统用户密码更改策略
    Chage是一个用于修改Linux系统用户密码更改策略的命令行工具。在本文中,我们将介绍如何在Linux系统中使用Chage命令。检查用户密码过期信息使用Chage命令可以检查用户密码更改策略和过期信息。要检查特定用户的密码过期信息,可以使用以下命令:sudochage-l用户名例如,要检查名为......
  • 如何设计一个缓存策略,可以动态缓存热点数据呢?
    如何设计一个缓存策略,可以动态缓存热点数据呢?热点数据动态缓存的策略总体思路:通过数据最新访问时间来做排名,并过滤掉不常访问的数据,只留下经常访问的数据。以电商平台场景中的例子,现在要求只缓存用户经常访问的Top1000的商品。具体细节如下:先通过缓存系统做一个排序队列(比......
  • 说说常见的缓存更新策略?
    说说常见的缓存更新策略?有cacheaside,read/writethrough,writeback三种cacheaside对于写操作,先更新数据库,后删除缓存,对于读操作,先访问缓存,有返回,没有查询数据库再保存到缓存中。适用于读多写少的场景,不适合写多的场景,因为写多会频繁删除缓存如果对缓存命中率有很高要求,有......
  • lucene内部的合并策略
    原文链接:http://java.dzone.com/news/merge-policy-internals-solr?mz=33057-solr_lucenesolr(orlucene)内部的合并策略是怎样的呢?选择哪些段(segment)需要被合并,是基于名为MergePolicy的抽象类决定的。这个类创建了一个合并规则类MergeSpecification:由OneMerge对象组成的一个......
  • 设计模式-装饰器模式
    装饰器模式(文章目录)什么是装饰器模式  装饰器(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。为什么使用装饰器模式使用装饰者模式有如下好处:  1.避免了类爆炸问题,简化了设计  2.易于扩展,可以灵活组......