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

简介

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

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

作用

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

实现步骤

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/15861.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python入门教程+项目实战-11.5节: 程序实战-选择排序算法

目录 11.5.1 排序算法简介 11.5.2 选择排序算法 11.5.3 系统学习python 11.5.1 排序算法简介 所谓排序,是指将数据集合中的元素按从小到大的顺序进行排列,或按从大到小的顺序进行排列。前者称为升序排序,后者称为降序排序。在数据结构与算…

【Java笔试强训 7】

🎉🎉🎉点进来你就是我的人了博主主页:🙈🙈🙈戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔🤺🤺🤺 目录 一、选择题 二、编程题 🔥Fibona…

( 哈希表) 594. 最长和谐子序列 ——【Leetcode每日一题】

❓594. 最长和谐子序列 难度:简单 和谐数组是指一个数组里元素的最大值和最小值之间的差别 正好是 1 。 现在,给你一个整数数组 nums ,请你在所有可能的子序列中找到最长的和谐子序列的长度。 数组的子序列是一个由数组派生出来的序列&am…

AWSFireLens轻松实现容器日志处理

applog应用程序和fluent-bit共享磁盘,日志内容是json格式数据,输出到S3也是JSON格式 applog应用部分在applog目录: Dockerfile文件内容 FROM alpine RUN mkdir -p /data/logs/ COPY testlog.sh /bin/ RUN chmod 777 /bin/testlog.sh ENTRYP…

MySQL知识学习01

1、什么是关系型数据库? 顾名思义,关系型数据库(RDBMS,Relational Database Management System)就是一种建立在关系模型的基础上的数据库。关系模型表明了数据库中所存储的数据之间的联系(一对一、一对多、多对多&am…

宏基因组组装 | 就现在!做出改变!!

微生态研究的核心难点是什么! 基因组组装! 从宏基因组数据中组装获得细菌的完整基因组(complete MAGs)是微生物组研究的长期目标,但基于NGS的宏基因组测序和组装方法是无法实现完整的细菌基因组组装的。即便是红极一…

【五一创作】Apollo(入门)

Apollo(入门) Quick Start 配置中心是一种统一管理各种应用配置的基础服务组件 Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且…

使用pands.rolling方法实现移动窗口的聚合计算

一个问题举例 假设有一个5天的收益数据,需要每3天求出一次平均值来达成某个需求: daterevenue2023-05-01102023-05-02202023-05-03302023-05-04402023-05-0550 1号、2号和3号的数据求一次平均值,2号、3号和4号的数据求一次平均值&#xff…

5.4.1树的存储结构 5.4.2树和森林的遍历

回忆一下树的逻辑结构: 双亲表示法(顺序存储) 如果增加一个结点M,L。毋须按照逻辑上的次序存储。 如果是删除元素: 方案一:比如说删除元素为G,设置其双亲结点为-1。 方案二: 把尾部的结点提上…

Sybase使用sp_helptext查看系统存储过程的源码

sp_helptext存储过程用于显示已编译对象的源代码。 sp_helptext是Sybase ASE内置的存储过程,可从任何位置调用。 但实际上,如果直接使用,常常会得到(令人头大的)错误提示: Msg 17461 Object does not exi…

基于JavaSpringboot+vue国风汉服文化交流宣传系统

基于JavaSpringbootvue国风汉服文化交流宣传系统 博主介绍:5年java开发经验,专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文章目…

【可解释AI】图神经网络的可解释性方法及GNNexplainer代码示例

图神经网络的可解释性方法及GNNexplainer代码示例 GNNExplainerIntroductionModelSingle-instance explanations(Explanation via Structural Information)Joint learning of graph structural and node feature information(Explanation via…

SuperMap iClient3D for Cesium 构建隧道

作者:kele 背景 前段时间看到一篇构建隧道的文章(https://blog.csdn.net/supermapsupport/article/details/128453116),突然想到一个使用场景:隧道通常是建在山体下面,是否可以通过这种方式构建出一条贯穿…

使用Python和机器学习进行文本情感分类

使用Python和机器学习进行文本情感分类 1. 效果图2. 原理3. 源码参考这篇博客将介绍如何使用Python进行机器学习的文本情感分类(Text Emotions Classification)。 1. 效果图 训练文本及情感分类前5条数据如下: 训练过程及测试文本情感分类效果图如下: 可以看到 对文本“S…

【量化交易笔记】5.SMA,EMA 和WMA区别

股票中的SMA,EMA和WMA是常用的技术分析指标。这些指标基于历史股价计算得出,可以帮助投资者了解股票的趋势,为决策提供依据。虽然它们都是平均值算法,但它们之间还是有一些区别的。 SMA 简单移动平均线(Simple Moving…

分布式的流处理平台Kafka

目录: 一、简介二、基本概念三、生产者使用详解四、发送消息五、消费者代码示例 一、简介 ApacheKafka 是一个分布式的流处理平台。它具有以下特点: 支持消息的发布和订阅,类似于 RabbtMQ、ActiveMQ 等消息队列;支持数据实时处理…

计算机组成原理第五章(2)---中断

5.1概述 产生和应用 在IO设备和主机交换数据时,由于设备本身的机电特性的影响,其工作速度比较低,与CPU无法匹配,如果采用程序查询的方式需要CPU进行等待,但是如果在等待的过程中CPU可以执行其他的程序,可…

【@Param注解】| 台面使用——>底层原理分析

🐇 🐇 😄 🐇 🐇 🐇 🐇 😄 🐇 🐇 🐇 🐇 😄 🐇 🐇 🐇 🐇 😄 🐇 🐇 🐇 🐇 😄 目录 🦁 定义🦁 台面使用🦁 底层原理分析🦁 尾声🐇 🐇 😄 🐇 🐇 🐇 🐇 😄 🐇 🐇 🐇 🐇 😄 🐇 🐇 🐇 🐇 😄…

java实现乘法的方法

我们都知道,乘法运算的核心思想就是两个数相乘,如果能将乘法运算转化成一个加数的运算,那么这个问题就很容易解决。比如我们要实现23的乘法,首先需要定义两个变量:2和3。我们将这两个变量定义为一个变量:2x…

PySide6/PyQT多线程之 信号与槽 / (Signal Slot)的高效利用

前言 PySide6/PyQT信号槽是一种事件处理方式,允许程序中的对象发送和接收信号。 在 PySide6/PyQT 精进的过程中,一定躲不开 信号和槽 这座大山,这是一个比较有意思的知识点: 初接触的看不懂,觉得复杂;看得…