首页 > 其他分享 >设计模式:策略模式/状态模式

设计模式:策略模式/状态模式

时间:2024-01-29 12:44:06浏览次数:42  
标签:状态 策略 代码 模式 state originPrice 设计模式

设计模式是通用的、可复用的代码设计方案,也可以说是针对某类问题的解决方案,因此,掌握好设计模式,可以帮助我们编写更健壮的代码。

wiki中将设计模式分为四类,分别是:

  • 创建模式(creational patterns)
  • 结构模式(structural patterns)
  • 行为模式(behavioral patterns)
  • 并发模式(concurrency patterns)

策略模式和状态模式属于其中的行为模式,行为模式——从名称上就可以看出——与动作、操作有关。

这两种模式我接触下来,感觉存在一定的相似性。状态模式中通常会存在一个内部状态,状态改变时行为也会发生改变,而策略模式是针对不同条件下的行为进行封装。总的来说,两者都是在不同条件下有不同的行为。接下来我们分别来看一下。

策略模式

首先看策略模式,根据针对它的概述,貌似就是一系列算法的封装

Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

当然策略模式不止关于算法的定义,还有对算法的调用。关于这一点我们在策略模式对应的wiki:Strategy pattern页面也能看到相应的描述。

Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.

这句话的大概意思是:代码在运行时接收指令,决定使用一系列算法中的哪一种。在这里一种算法就是对应一个策略。

这个描述很容易让人联想到代码中常见的条件语句if-elseif-else,在条件分支根据不同的指令执行不同的操作。但是很显然,既然是一系列的算法,那就说明可能会有很多、甚至是大量的条件,那么可想而知,如果我们直接使用if-else语句来编写执行代码的话,这部分代码会非常长,并且这会破坏软件设计原则中的单一功能原则,这段代码除了判断条件,还要根据不同的条件执行不同的细节操作。

策略模式中的“策略”,其实指的就是算法。然后条件判断作为一个入口,去调用对应的“策略”。所以我们看到wiki中有下面这段描述:

Typically, the strategy pattern stores a reference to some code in a data structure and retrieves it. This can be achieved by mechanisms such as the native function pointer, the first-class function, classes or class instances in object-oriented programming languages, or accessing the language implementation's internal storage of code via reflection.

这段话的意思是:策略模式会在数据结构中存储对某些代码的引用,并对其进行检索。这可以通过本地函数指针、一级函数、面向对象编程语言中的类或类实例,或通过反射 访问 语言实现的代码内部存储等机制来实现。

简单来理解,就是把一系列相关操作封装成函数,一个函数就对应一个算法的实现。

策略模式与开闭原则

我们知道在软件设计原则中有一条是:对扩展开放,对修改封闭。策略模式与开闭原则是一致的。

According to the strategy pattern, the behaviors of a class should not be inherited. Instead, they should be encapsulated using interfaces.

根据策略模式,类的行为不应被继承,它们应使用接口进行封装。也就是说,我们最好不要对父类本身做修改,而是使用接口对子类的行为进行扩展。在wiki中使用了Java来举例子,在JavaScript中也可以做类似的处理,比如不把对应的策略函数加在对象自身,而是统一放在一个地方进行调用,也就是上面所说的在数据结构中存储对某些代码的引用。比如下面这个例子:

某商场中的商品在不同阶段的价格满足固定的逻辑,做了以下封装:

const priceProcessor = {
  pre(originPrice) {
    if (originPrice >= 100) {
      return originPrice - 20;
    }
    return originPrice * 0.9;
  },
  onSale(originPrice) {
    if(originPrice >= 100) {
      return originPrice - 30;
    }
    return originPrice * 0.8;
  },
  back(originPrice) {
    if(originPrice >= 200) {
      return originPrice - 50;
    }
    return originPrice;
  },
  fresh(originPrice) {
    return originPrice * 0.5;
  },
};

pre、onSale、back、fresh分别代表了在预热、大促、返场、尝鲜四种阶段下的价格处理。

在上述代码中,我们在priceProcessor这个数据结构中存储了针对不同阶段下对价格的处理逻辑,也就是各种封装的函数。我们可以调用这些引用,从而实现在不同条件下执行不同的算法。

这样,当我们策划新的促销活动时,只需要在priceProcessor这个结构中增加新的处理逻辑,而不需要影响商品对象和其他的处理逻辑,并且这样子处理后,测试流程中就只需要测试新的处理逻辑,而不需要回归测试整体功能。

每个处理逻辑有单独的函数实现,这也方便不同条件下的算法替换,比如在某次商场大促,想要使用返场的价格,就可以直接调用priceProcessor.back方法,而不需要编写重复冗余的代码。

状态模式

策略模式的核心很简单,就是单一功能的函数封装。接下来我们继续看状态模式。

The state pattern is a behavioral software design pattern that allows an object to alter its behavior when its internal state changes.

状态模式也很简单,就是允许对象在内部状态发生变化时改变其行为。

状态模式的“状态”,就是指对象内部的状态,也就是说,这个模式针对的是存在内部状态的对象。

The state pattern can be interpreted as a strategy pattern, which is able to switch a strategy through invocations of methods defined in the pattern's interface.

我们可以看到wiki这部分也有说,状态模式可以解释为一种策略模式。其实上也就是说,根据不同的状态切换策略;在策略模式下,是根据不同的条件切换不同策略,这个是广泛意义下的条件,而状态模式中,不同条件就特定为不同的内部状态。

这样处理后,就不需要使用条件语句了,可以直接通过不同状态映射不同的行为。

在状态模式的wiki页面中,也列举了它所解决的主要问题:

The state pattern is set to solve two main problems:[4]

  • An object should change its behavior when its internal state changes.
  • State-specific behavior should be defined independently. That is, adding new states should not affect the behavior of existing states.

在某类场景中,第一,对象应根据其内部状态的改变来改变其行为。

第二,特定于状态的行为应独立定义。也就是说,添加新状态不应影响现有状态的行为。

这里看第二点,其实和策略模式的场景很类似。

对应这两个问题,状态模式描述了以下解决方案:

In this, the pattern describes two solutions:

  • Define separate (state) objects that encapsulate state-specific behavior for each state. That is, define an interface (state) for performing state-specific behavior, and define classes that implement the interface for each state.
  • A class delegates state-specific behavior to its current state object instead of implementing state-specific behavior directly.

第一,是定义独立的状态对象,为每个状态封装特定于状态的行为。

第二,类将特定于状态的行为委托给其当前的状态对象,而不是直接实现特定于状态的行为。

因此,状态模式中的关键就在于对状态对象的实现。比如下面这个例子:

一个养生壶有不同的功能,当切换不同的功能时我们可以认为它处于不同的工作状态。

class HealthPot {
  constructor() {
    this.state = new State();
  }

  changeState(status) {
    this.state.status = status;
    // 若状态不存在,则返回
    if(!this.state.statusToProcessor[status]) {
      return;
    }
    this.state.statusToProcessor[status]();
   }
}

class State {
  constructor() {
    this.status = '';
  }
  statusToProcessor = {
    water() {
        console.log('煮开水');
    },
    flowersTea() {
        console.log('煮花草茶');
    },
    fruitsTea() {
        console.log('煮水果茶');
    },
    keepWarm() {
        console.log('保温');
    }
  }
}

const hp = new HealthPot();
hp.changeState('flowersTea');

在上述代码中,如何实现特定状态的行为与养生壶本身无关,只与状态对象有关。养生壶的行为只是改变状态,并调用对应方法,这样如果后续有新的状态增加,也不用去修改养生壶这个具体的对象,相当于一种拆分行为。

这其实就有点类似于vue中的状态管理工具vuex。

总结

策略模式和状态模式两者存在一定的相似性,但是策略模式封装的函数其独立性会更高,而状态模式中封装的函数依赖于主体的状态,具体操作代码也可能依赖主体的其他属性,比如养生壶例子中,执行各种功能时,需要保证壶中有水,并且要判断是否通电中等等。

简单来说就是一种拆分、封装的行为,满足软件设计原则中的单一职责和开闭原则。

一般在应用开发初期,由于功能简单,开发者可能不会特别在意拆分,并且通常而言不太提倡提前优化,所以会在之后的维护和迭代中,应用这些模式来优化和重构代码;但是有时设计良好的代码,会更便于代码的维护。

标签:状态,策略,代码,模式,state,originPrice,设计模式
From: https://www.cnblogs.com/beckyyyy/p/17994265

相关文章

  • Quant-Ch05 量化择时策略
    Ch5量化择时策略 量化择时策略,就是采用数量化分析方法,利用单个或多个技术指标的组合,来对交易标的股票或股票指数进行低买高卖的操作,期望获得超越简单买入持有策略的收益风险表现。 量化择时策略的核心是技术分析,更准确地来说,是客观型技术分析。客观型技术分析,是指其分析过程中所......
  • 用Java语言实现一个观察者模式
    观察者模式(也被称为发布/订阅模式),提供了避免组件之间紧密耦合的另一种方法,它将观察者和被观察的对象分开。在该模式中,一个对象通过添加一个方法(该方法允许另一个对象,即观察者注册自己)使本身变得可观察。当可观察的对象更改时,它会将消息发送到已注册的观察者。这些观察者收到消......
  • 命令行模式
    定义:将“请求”封装成对象,以便使用不同的请求补充解释:解决了应用程序中对象的职责以及他们之间的通信方式,使发送者和接收者完全解耦,发送者和接收者之间没有没有直接关系,下命令的对象只知道如何发送请求,不知道如何完成请求类型:行为型适用场景:请求调用者和请求接收者需要解耦,......
  • 《设计模式之禅》读书笔记
    参考  https://zhuanlan.zhihu.com/p/357889775 一、六大设计原则单一职责原则定义:应该有且仅有一个原因引起类的变更。举例:属性和行为拆分,例如setPassword(Stringpassword)和changePassword(Stringpassword)。单一职责原则提出了一个编写程序的标准,用“职责”或“......
  • 设计模式
    1、设计模式七大原则:1)单一职责原则,一个类或者一个方法只负责一项原则2)接口隔离原则,客户端不应该依赖他不需要的接口3)依赖倒转原则,细节依赖抽象(面向接口编程)4)里氏替换原则,子类中尽量不重写父类的方法,可通过聚合、组合解决5)开闭原则,对扩展开放,对修改关闭6)迪米特法则,最少知道原......
  • jax框架为例:求hession矩阵时前后向模式的自动求导的性能差别
    注意:本文相关基础知识不介绍。给出代码:fromjaximportjacfwd,jacrevimportjax.numpyasjnpdefhessian_1(f):returnjacfwd(jacrev(f))defhessian_2(f):returnjacfwd(jacfwd(f))defhessian_3(f):returnjacrev(jacfwd(f))defhessian_4(f):ret......
  • Part 4:NetOps 策略如何影响企业网络团队?
    转载网络运营(NetOps)是一种采用DevOps中应用程序开发人员流行的敏捷协作软件开发框架并将其与网络管理联系起来的方法。现代网络正在从静态配置、以硬件为中心的架构转向灵活、以软件为中心的系统。这一举措凸显了DevOps和网络管理之间配对的必要性。网络团队正在有机地转向NetO......
  • 通达信【小楷超级竞价秘籍】竞价低吸+激进模式二合一玩转竞价 源码文件分享
     本套竞价指标,原价200金钻哦(1金钻=10元,就是2000元哦)小楷超级竞价秘籍,经过半年时间的研发测试,现在将这套系统推出!一、此指标包括选股预警+使用说明,预警时间为上午9.25-9.30分二、此指标包括两种竞价模式竞价激进模式+竞价低吸模式三、竞价激进模式适用于激进选手,出票一般都是......
  • 设计模式:创建型模式
    文章目录 1.简单工厂模式(SimpleFactory)1.1.定义1.2.结构1.3.时序图1.4.代码实现1.5.优缺点1.6.使用场景1.7.总结2.工厂方法模式(FactoryMethod)2.1.定义2.2.结构2.3.时序图2.4.代码实现2.5.优缺点2.6.使用场景2.7.总结3.抽象工厂模式(AbstractFactory)3.1.......
  • 单例设计模式
    单例设计模式概念单例模式是java中常见的设计模式之一,主要有:懒汉单例模式,饿汉单例模式,还有登记式单例.这边主要讲懒汉和饿汉.单例模式有以下特点.单例类只能有一个实例单例类必须自己创建自己的唯一实例单例;类必须给所有其他对象提供这一实例懒汉设计模式代码public......