了解 python 中的命令-查询分离



我正在学习列表的方法(pop和remove之间的区别)
并注意命令-查询分离

它指出每个方法都应该是执行操作的命令或将数据返回给调用方的查询,但不能同时是两者。换句话说,提出问题不应该改变答案。[1] 更正式地说,方法只有在引用上是透明的并且因此没有副作用时才应该返回一个值。

我无法解释"提问不应该改变答案",
我阅读了ppt 维基百科链接,以频道内外的"光"标志为例。
它没有说明问题和答案之间的关系。

在我看来,应该是">回答问题不应该改变问题">
这是合理的说法

从理论上讲,这建立了理智的度量,人们可以在不同时修改该状态的情况下推理程序的状态。

我的扣除是否合理?

下面的类遵循 CQS 原则。它的每个方法都是查询或命令。该方法get提供有关状态的信息,并且不允许任何副作用。该方法set有副作用,因此返回None

class IntValue:
def __init__(self, x):
self.x = int(x)
def get(self):
return self.x
def set(self, x):
self.x = int(x)

特别是,以下内容将永远正确。

v = IntValue(1)
v.get() == v.get() # Always True

您永远不会通过get方法询问值并同时更改它。在这里,CQS 原则向您保证,您可以连续多次请求该值,并且仍然可以获得相同的答案

这很重要的原因是,程序中许多不希望的复杂性是由副作用引起的。

现在考虑一个封装列表的类似类。

class ListValue:
def __init__(self, x):
self.x = list(x)
def get(self):
return self.x
def set(self, x):
self.x = list(x)

上述问题在于,提出问题可能会导致答案发生变化。

v = ListValue([1, 2, 3])
lst = v.get() # [1, 2, 3]
lst.append(4)
v.get() # [1, 2, 3, 4]

在开发大型软件时,这通常是难以跟踪的错误来源。通过确保不能从ListValue访问状态,而只能访问该状态的视图,可以避免这些情况。

class ListValue:
def __init__(self, x):
self.x = list(x)
def get(self):
return deepcopy(self.x) # Here we return a copy of the list
def set(self, x):
self.x = list(x)

你已经明白了它的要点。在计算机科学术语中,这几乎意味着获取信息不应该以任何方式修改信息

它们的含义

更正式地说,仅当方法在引用上是透明的并且因此没有副作用时,它们才应返回值。

与软件和客户端之间的通信有关。这进一步说明了返回的任何信息必须是实际存储值的副本,并且不能用于以任何方式追溯该值。

以这个列表为例:

a = [1, 2, 3, 4, 5]

这是我系统中非常重要的列表,客户付钱给我以访问我的秘密列表。我将在这里编造一种方法,并说send()方法将把这个列表发送给我的客户。

如果我向我的客户send(a),这打破了上述原则。因为现在我的客户可以使用对许多讨厌的说法的引用:

for i in range(len(a)):
a[i] = None

现在,当我尝试访问 a 时,我得到的只是[None, None, None, None, None].它们被覆盖了,因为我向他们发送了引用而不是值。相反,我应该有send(a[:]),或者一份副本。现在,无论我的客户如何处理我发送给他的列表,它都不会影响我系统内部发生的事情。

我无法解释"提出问题不应该改变答案", 我阅读了链接的ppt维基百科,其中以频道内外的"光"标志为例。它没有说明问题和答案之间的关系。

Light?基本上是在问"灯亮吗?它返回 true 或 False。

Light?要做的错误做法是,如果灯关了就开灯,或者如果灯亮了就关灯。在没有任何其他混淆代码的情况下,连续执行三次Light?应返回相同的值三次。提出问题不应该改变答案。但是,如果问问题Light?改变了答案,你会得到"真,假,真"或"假,真,假"。

在我看来,应该是"回答问题不应该改变问题">

否,因为答案(返回值)无法更改问题(方法调用)。但是问题可以改变答案(如果你以这种方式编写方法)。

最新更新