让我们一起写一篇无限长的文章吧!但这是一项不可能完成的任务。然而,我们可以创建一个过程,如果永远运行,就会生成一篇无限长的文章。足够接近。
现在,您显然可以用一行 Python 代码生成一篇冗长且重复的文章:
>>> "This is water. " * 20 'This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. '
哈欠……嘘!相反,我们将使用本文中的状态设计模式生成一篇更有趣的文章。
首先,我们将了解什么是状态机以及它们与状态设计模式的关系。接下来,我们将创建一个状态机,它可以生成一篇(相当)有趣且无限的文章。然后,我们将简要回顾一下状态设计模式。最后,我们将使用状态设计模式将该状态机转换为面向对象的代码。
软件设计模式是解决常见问题的有效方法。如果应用得当,软件设计模式(例如状态模式)可以帮助您编写更好的可扩展、可维护和可测试的软件。
本质上,状态设计模式将状态机转换为面向对象的代码。
如果您不熟悉状态机,这是一个非常简单的概念。状态机具有状态和转换。状态是我们感兴趣的系统的某些属性,状态转换是改变这些属性并从而导致状态变化的操作。
由于我有机器人技术背景(除其他外),并且状态机在机器人技术中广泛使用,因此我将使用机器人真空吸尘器的一个简单示例来说明状态机的工作原理。
即使您从未遇到过状态机,状态机图也直观地描绘了机器人如何运行。让我们一步步回顾一下这个操作。
因此,我们的机器人真空吸尘器具有三种状态:对接、清洁和充电,并根据地板及其电池的感官检测进行转换。
现在我们已经基本了解了状态机,让我们创建一个能够编写无限论文的状态机。
上面是一个状态机,它使用英语语法生成由简短句子组成的文章。我保证我们很快就会得到一个更有趣的版本,但这应该作为理解的一个很好的起点。让我们看一下它是如何工作的。
这个状态机可能会生成一篇如下所示的(无意义的)文章。
世界变成红色!哈里表弟下雨了?老虎闪闪发光,很有趣。 ……
尽管“非确定性”听起来很复杂,但就我们的目的而言,它只是意味着添加一些随机性。本质上,我们在过渡到某些状态之前添加了一种抛硬币的方法。你就会明白我的意思了。
上面的非确定性状态机与之前的非常相似。唯一的区别是:
通过引入随机性、否定性和连词,我们现在可以生成更有趣且长度可变的句子。
现在,让我们了解状态设计模式是如何工作的。再次记住,我们正在尝试将状态机转换为面向对象的代码。
在论文生成状态机中,观察到每个状态都需要做两件事。
从特定国家的角度来看,它不需要了解或做任何其他事情。我们可以一次只关注一种状态,而不是被整个系统的复杂性(所有状态和转换)所困扰。在我看来,这种隔离和解耦是State模式的最大卖点。
下面是状态设计模式的UML图。每个状态都在一定的上下文中运行,如Context
类所示。上下文对象有一个私有状态属性,它用它来调用当前状态来执行其操作。每个状态都实现一个State
接口,其中包含执行其动作或操作并返回下一个状态的方法。
如果我们将其映射到论文生成示例上,UML 图如下所示。
WordState
现在是一个抽象类(用斜体表示)而不是一个接口。抽象类可以有一些抽象的(未实现的)方法和属性,而其他的可以定义。接口是完全抽象的:它们的所有方法都是抽象的。我进行此更改是因为所有状态下的generateWord
实现都是相同的,并且避免重复代码是件好事。
让我们分解一下上面的每个属性和方法。在EssayContext
类中,我们有:
state
:对当前WordState
对象的引用。essayBody
:到目前为止生成的所有单词的列表。setState()
:用于更改state
属性的 Setter。addWord()
:将下一个单词添加到文章正文的方法。generateEssay()
:我们调用这个方法来生成我们的文章;当essayBody
的 length 大于length
时我们停止。printEssay()
:返回生成的论文的字符串。
在抽象类WordState
中,我们有:
wordList
:我们从中选择要生成的单词列表的抽象属性(以斜体表示)。generateWord()
:将生成的单词添加到文章上下文中的方法。nextState()
:返回下一个状态的抽象方法。
我们将使用NounState
作为从WordState
继承的所有其他具体状态的代表性示例。
wordList
:我们从中选择要生成的单词的名词列表。nextState()
:返回下一个状态。
现在,我们已经拥有了在代码中实际实现这一点所需的一切。让我们继续这样做吧!
首先,我们在名为essay_context.py
的文件中编写EssayContext
类。我们将放弃驼峰式命名法并改用蛇式命名法,因为,Python 是一条……蛇(抱歉)。
from word_state import WordState class EssayContext: def __init__(self, state: WordState): self.state = state self.essay_body: list[str] = [] def set_state(self, state: WordState): self.state = state def add_word(self, word: str): self.essay_body.append(word) def generate_essay(self, length: int): while len(self.essay_body) < length: self.state.generate_word(self) def print_essay(self) -> str: return " ".join(self.essay_body)
然后,我们将状态添加到名为word_state.py
的文件中。
import abc import numpy as np class WordState(abc.ABC): word_list: list[str] @classmethod def generate_word(cls, context: "EssayContext"): word = np.random.choice(cls.word_list) context.add_word(word) context.set_state(cls.next_state()) @classmethod @abc.abstractmethod def next_state(cls) -> "WordState": pass class NounState(WordState): word_list: list[str] = ["everything", "nothing"] @classmethod def next_state(cls): return VerbState class VerbState(WordState): word_list: list[str] = ["is", "was", "will be"] @classmethod def next_state(cls): heads = np.random.rand() < 0.5 if heads: return NegationState return AdjectiveState class NegationState(WordState): word_list: list[str] = ["not"] @classmethod def next_state(cls): return AdjectiveState class AdjectiveState(WordState): word_list: list[str] = ["fantastic", "terrible"] @classmethod def next_state(cls): heads = np.random.rand() < 0.5 if heads: return ConjunctionState return EndmarkState class ConjunctionState(WordState): word_list: list[str] = ["and", "but"] @classmethod def next_state(cls): return NounState class EndmarkState(WordState): word_list: list[str] = [".", "!"] @classmethod def next_state(cls): return NounState
最后,让我们添加代码来运行main.py
中的所有内容。
from essay_context import EssayContext from word_state import NounState if __name__ == '__main__': ctx = EssayContext(NounState) ctx.generate_essay(100) print(ctx.print_essay())
运行python main.py
给出以下输出(由于不确定性,每次都不同):
'everything is not terrible and nothing was terrible ! everything will be not fantastic but everything is fantastic . everything will be fantastic . nothing will be fantastic and nothing will be terrible ! everything was not fantastic and everything will be not terrible . everything was terrible . nothing was terrible but nothing will be fantastic ! nothing is not terrible . nothing was not fantastic but everything was not fantastic ! everything will be not fantastic but everything will be terrible ! everything will be not fantastic . everything is fantastic but nothing will be not terrible ! everything will be not fantastic but nothing was not fantastic !'
对于这样一个简单的系统来说还不错!我们还可以扩展各种单词列表或添加更多状态以使论文生成更加复杂。我们甚至可以引入一些 LLM API 来将我们的论文提升到一个新的水平。
状态机和状态模式非常适合建模和创建具有明确定义的“状态”概念的系统。也就是说,每个状态都有特定的行为和属性。扫地机器人正在清洁、对接或充电。您的电视可以打开或关闭,并且电视遥控器按钮的作用会根据电视的状态而有所不同。
它也非常适合生成或识别具有明确定义模式的序列。这适用于我们的论文生成示例。
最后,您可能会问:“这一切有什么意义?”为什么我们要费尽心思定义各种状态和类别来生成这篇“无限”的文章?我们可以编写 20 行(或更少)Python 代码来实现相同的行为。
简而言之,答案是为了更好的可扩展性。
想象一下,如果我们不再只有三五个州,而是有 50 个或 500 个州。这并非夸张;真正的企业系统确实具有这种程度的复杂性。突然间,状态模式由于其解耦和隔离而显得更具吸引力。我们可以一次只关注一种状态,而不必将整个系统保留在我们的脑海中。引入更改更容易,因为一种状态不会影响其他状态。它还允许更轻松的单元测试,这是可扩展和可维护系统的重要组成部分。
最终,状态模式不仅仅是管理状态;它还涉及管理状态。与所有设计模式一样,它是构建既复杂又可扩展、可维护的系统的蓝图。