什么是状态,可变状态和不可变状态?


32

这是一个新手问题,但我在Google上找不到足够的新手证明答案。

人们说“状态”是什么意思-在一般的编程中,特别是在OO编程中?

另外,什么是可变的和不可变的状态-同样,通常在编程中,尤其是在OOP中?


4
分享您的研究成果对每个人都有帮助。告诉我们您尝试过的内容以及为什么它不能满足您的需求。这表明您已花时间尝试自我帮助,这使我们免于重复显而易见的答案,并且最重要的是,它可以帮助您获得更具体和相关的答案。另请参阅“ 如何提问
2014年

Answers:


46

将值(数字,字符串,复杂的数据结构)与身份和时间点相关联时,您便具有状态。

例如,数字10本身不代表任何状态:它只是一个定义明确的数字,并且将始终是其自身:自然数10。又例如,字符串“ HELLO”是五个字符的序列,并且它完全由包含的字符及其出现的顺序描述。从现在起的五百万年里,字符串“ HELLO”仍将是字符串“ HELLO”:纯值。

为了拥有状态,您必须考虑一个世界,在这些世界中,这些纯价值与某种具有身份的实体相关联。身份是一个原始的想法:这意味着您可以区分两件事,而不管它们可能具有的其他任何属性。例如,具有相同型号,相同颜色的两辆汽车是两辆不同的汽车。

有了这些具有标识的事物,您可以将属性附加到它们,用纯值描述。例如,我的车具有蓝色的特性。您可以通过关联一对来描述这一事实

("colour", "blue")

到我的车上。该对(“颜色”,“蓝色”)是描述该特定汽车状态的纯值。

状态不仅与特定实体相关,而且还与特定时间点相关。所以,你可以说今天我的车有状态

("colour", "blue")

明天我将其重新粉刷成黑色,新状态为

("colour", "black")

请注意,实体的状态可以更改,但是其身份根据定义不会更改。好吧,当然,只要实体存在,就可以制造和销毁汽车,但是它将在其整个生命周期中保持其身份。谈论尚不存在的事物的身份没有任何意义。

如果附加到给定实体的属性的值随时间变化,则表示该实体的状态为mutable。否则,您说状态是不可变的

最常见的实现是将实体的状态存储在某种变量(全局变量,对象成员变量)中,即存储状态的当前快照。然后使用分配来实现可变状态:每个分配操作都会用新快照替换以前的快照。此解决方案通常使用内存位置来存储当前快照。覆盖内存位置是一种破坏性操作,用新快照替换快照。(在这里,您可以找到有关此面向位置的编程方法的有趣演讲。)

另一种选择是将实体的后续状态(历史)视为值的流(可能是无限序列),例如,参见SICP的第3章。在这种情况下,每个快照都存储在不同的内存位置,并且程序可以同时检查不同的快照。当不再需要未使用的快照时,可以对其进行垃圾回收。

两种方法的优缺点

  • 方法1消耗较少的内存,并且由于不涉及复制,因此可以更有效地构造新快照。
  • 方法1隐式地将新状态推送到持有对该状态的引用的程序的所有部分,方法2将需要某种机制将快照推送至其观察者,例如以事件的形式。
  • 方法2可以帮助防止不一致的状态错误(例如,部分状态更新):通过定义显式函数来从旧状态中生成新状态,可以更容易地区分在不同时间点生成的快照。
  • 方法2更具模块性,因为它允许轻松生成关于状态的视图,这些视图独立于状态本身,例如使用诸如map和的高阶函数filter

1
请注意,对象并不是唯一具有状态的东西。如果程序使用(可变)全局变量,则该程序本身被称为具有状态。同样,如果一个函数具有一个变量,该变量可以记住各个函数调用中的值,则该函数是有状态的。
2014年

2
@Doval:您可以将全局状态视为全局世界对象的状态。据我所知,该视图用于例如Ruby。记住状态的函数仅用一种方法就与对象同构。常见的基本思想是将值与身份或场所相关联,即某些事物可以保留值(可能是可变值)但保留其身份。
Giorgio

3
当然,我原则上同意。我只是要确保Prog理解有状态性不是OOP独有的。我认为“一切都是对象”的思路自然而然。
2014年

@Doval:您提到了有状态的函数,该函数可以在不同的调用之间记住值。我可以想到的一个示例是C中的静态局部变量。另一个示例是闭包(捕获在其上下文中定义的变量的函数)。闭包在某种程度上是对象的双重对象:闭包是仅使用一种方法的对象,而对象是在相同变量上定义的闭包的集合。您可能知道所有这些,但是我想在这里总结一下。如前所述,通常,您可以将状态存储在某些内存位置中,并使用不同的机制对其进行访问。
乔治

11

状态只是有关内存中保存的某些事物的信息。

作为面向对象的简单练习,可以将类视为千篇一律,将cookie视为对象。您可以使用cookie切割器(类)创建cookie(实例化对象)。假设Cookie的属性之一是其颜色(可以通过使用食用色素来更改)。该Cookie的颜色以及其他属性也是其状态的一部分。

可变状态是在创建对象(cookie)之后可以更改的状态。不变状态是无法更改的状态。

不可变对象(其中没有任何国家可以改变)当你正在处理成为重要的并发,一个以上的处理器在您的计算机在同一时间,对象上操作的能力。不变性保证您可以依靠状态在对象的生存期内保持稳定和有效。

通常,对象的状态保存在“私有或成员变量”中,并通过“属性”或getter / setter方法进行访问。


3
为了Prog的利益,价值永远不变的事实也很重要,因为它更容易推论。您可以根据需要在许多函数/方法中使用它,并且您知道他们无法更改它。在可变状态下,您必须跟踪该对象如何被用来确定其现在值的历史。如果使其不可变,不会使程序复杂化,那将是不必要的精神开销。
2014年

谢谢回答。因此,基本上,在OOP中,当有人说“状态”时,通常是指“对象的成员变量”吗?如果是这样,那么“可变状态”是公共变量,或者在OOP中更常见,可以通过setter方法更改的私有变量-而“不可变状态”仅仅是私有成员变量?
Aviv Cohn 2014年

1
只要通过填充初始值就永远不写入对象的私有成员,就可以模拟不变性。不变性可以执行不提供setter方法,需要的初始值来使用构造参数来设定,在功能样式编写,采用常数等:使用多种方法
罗伯特哈维

1
我认为状态是某个实体的某些财产的价值。“已发货”是一种状态。“税率”也是如此。某物的重量是一种状态。您目前处于清醒状态还是睡眠状态都是一种状态。事物的颜色是一种状态。有关某种事物的有意义的信息,存储在某种计算机内存中。
罗伯特·哈维

1
在许多语言中,可以通过将成员变量声明为“ const”或“ final”来强制实现不变性。此类变量只能由构造函数初始化。不要假设私有变量是不可变的-它们仍然可以由类自己的成员函数(方法)修改。
西蒙B

7

我认为,在比较有状态API和无状态API时,术语“状态”(与“成员变量”之类的具体状态相反)最有用。在不提及API的情况下尝试定义“状态”有点像在不提及编程语言的情况下定义“变量”或“功能”。大多数正确答案仅对已经知道单词含义的人有意义。

有状态与无状态

  • 状态 API是一种“记忆”您到目前为止已调用的函数以及带有哪些参数的API,因此,下次调用函数时,它将使用该信息。“记住”部分通常使用成员变量来实现,但这不是唯一的方法。
  • 一个无状态的 API是一个,每一个函数调用完全取决于传递给它的参数,而不是其他。

例如,OpenGL可能是我所知道的最有状态的API。如果我想稍微简化一下,我们可以说它看起来像这样:

glSetCurrentVertexBufferArray(vba1);
glSetCurrentVertexBufferObject(vbo1);
glSetCurrentVertexShader(vert1);
glSetCurrentFragmentShader(frag1);
// a dozen other things
glActuallyDrawStuffWithCurrentState(GL_TRIANGLES);

几乎每个函数都只是用来传递OpenGL需要记住的某些状态,然后最后调用一个简单的函数来完成所有绘制。

(过于简化的)OpenGL的无状态版本可能看起来像这样:

glActuallyDrawStuff(vba1, vbo1, vert1, frag1, /* a dozen other things */, GL_TRIANGLES);

您会经常听到人们说状态少的API更容易推理。如果您可以控制参数的数量,我通常会同意。

可变与不可变

据我所知,这种区别仅在您可以指定初始状态时才有意义。例如,使用C ++构造函数:

// immutable state
ImmutableWindow windowA = new ImmutableWindow(600, 400);
windowA = new ImmutableWindow(800, 600); // to change the size, I need a whole new window

// mutable state
MutableWindow windowB = new MutableWindow(600, 400);
windowB.width = 800; // to change the size, I just alter the existing object
windowB.height = 600;

很难实现一个不“记住”大小的窗口类,但是您可以决定用户在创建窗口后是否应该能够更改它的大小。

PS在OOP中,“状态” 通常表示“成员变量”,但这的确不止于此。例如,在C ++中,方法可以具有静态变量,而lambda可以通过捕获变量而成为闭包。在这两种情况下,这些变量在对函数的多次调用中均保持不变,因此可能符合状态的要求。常规函数中的局部变量也可以视状态而定,这取决于它们的使用方式(我在main()中经常使用的变量经常会计数)。


很棒的答案。非常感谢您,您真的帮助我迅速掌握了这一点。我几乎不知道,我已经使用了很长时间,并且不知道它叫什么。
the_endian '16

2

用外行话

词典的状态:

一种。关于存在的条件或存在方式

  1. 状态-某种事物关于其主要属性的方式;

事物的状态是其属性在任何给定时刻具有的一组值。

在OOP中,对象的状态是任何给定时刻其属性值的快照。

Thing t = new Thing();
t.setColor("blue");
t.setPrice(100)
t.setSize("small");

它的状态是颜色为蓝色,价格为100,尺寸较小。

如果以后再做:

t.setColor("red");

您可以更改其属性之一,但也可以整体更改状态,因为对象不再与原来相同。

有时设计类是为了使它们的属性值在创建后无法更改。它们的所有属性值都传递给构造函数,或者从数据库或文件之类的源中读取,但是在那一刻之后就无法更改这些值,因为没有“ setter”方法或任何其他方式更改对象内部的值。

Thing t = new Thing("red",100,"small");
t.setColor("blue") -->> ERROR, the programmer didn't provide a setter or any other way to change the properties values after initialization.

这就是所谓的无法改变的状态。您所能做的就是销毁对象,创建一个新对象并将其赋值给相同的引用或变量。

Thing t = new Thing("red",100,"small");
t = new Thing("blue",100,"small");
// I had to create a new Thing with another color since this thing is inmutable.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.