在享元模式中,内部状态和外部状态有什么区别?

11

四人帮中的FlyWeight模式章节来看,当大多数对象状态可以变为外在状态时,FlyWeight模式是适用的。

外在状态是什么意思?我感觉这个模式用于对象共享。如果对象要共享,那么它怎么可能有任何状态呢?


5
“Extrinsic state” 表示某物体外部的状态。 - Cratylus
4个回答

14

让我们以文字处理器为例:

文字处理器处理字符对象。字符对象的状态是字符内容、字体、样式、位置等(就文字处理器而言)。不同的文档使用字符的不同实例。假设我们只处理 a-z 字符,不同的文档使用来自 a-z 字母池中的字母,但可能应用不同的字体/样式。因此,如果我们将字符的内容与字体/样式分开,就可以共享这些字符,这是有意义的,因为不同类型的字符总数较少(在我们的案例中为26个,否则为一个常数),而不同文档中使用的字符实例却不同。共享这些字符实例意味着根据内容共享字符实例,并对这些字符外部应用上下文,如字体/样式。字符内容是内在状态,而字体/样式是外在状态。将状态分离为内在状态和外在状态导致了上述示例中大量的存储节省。


很好的解释和例子。+1 - Diganta
因此,内在状态是表示对象的基本数据(例如字符内容)的状态。而外在状态是应用于具有其内在数据/基本数据的对象的变体,例如样式。就像一个带有XLST样式表(旧参考)或HTML和CSS的XML文档一样。 - Tore Aurstad

11

外在状态 - 指属于对象上下文(外部)或仅适用于该实例的状态。

内在状态 - 指自然属于 'FlyWeight' 对象的状态,因此应该是永久或不可变的(内部)或与上下文无关。


2
无论在项目列表中具体措辞如何,理解其信息很重要:Flyweight适用于状态的一个重要部分可以在许多对象之间共享,因为它是所有对象都相同的数据。通常,共享状态本质上是不可变的(即“普遍真理”)。字体面孔的例子使这一点非常清楚;来自日常Java的示例是java.util.regex.Pattern,即Flyweight,与使用它并保存本地外在状态的客户端对象Matcher。许多Matcher可以并行存在,所有的对象都在内部重复使用编译后的正则表达式。
这段引述比你的问题更加清晰:

共享的flyweight越多,存储节约就越大。随着共享状态的增加,节约也会增加。当对象使用大量内在和外在状态,并且外在状态可以计算而不是存储时,节约最大。然后,您通过两种方式节省存储空间:共享减少了内在状态的成本,而将外在状态交换为计算时间。


1
你能否在回答中提供一个例子来澄清你的整体回答?我特别不清楚外部对象状态如何不被对象本身所拥有。 - Geek
享元模式与可变性无关。 - Géry Ogam
@Maggyero 我反对你的观点,即 WP 语句是“完全错误”的,因为在几乎所有实际情况下,共享状态本质上是不可变的,它是计算或从存储中检索的缓存结果。只有当极端地追求学究式时,我们才能观察到在一些非常狭窄的情况下,更新共享状态实际上是有意义的,可以将更改应用于在不同上下文中使用的大量对象。这并没有帮助解释概念,这样的可变实现更加复杂且容易出错(例如,客户端错误地假设不可变性)。 - Marko Topolnik
@Maggyero 我们无法决定“维基百科文章是错误的”这种观点是否是模式创建者的观点。你所声称的只是他们并没有明确要求它,但这并不是我们正在讨论的问题。 - Marko Topolnik
我想不到任何例子,你能给一个吗?可变类变量可以用于对类的实例进行计数。例如,可以参考此链接 - Géry Ogam
显示剩余23条评论

1

Gang of Four的轻量级设计模式引入了“内在状态”和“外在状态”的概念:

关键概念在于“内在状态”和“外在状态”的区分。内在状态存储在享元中;它由与享元无关的信息组成,因此可以共享。外在状态取决于并随享元的上下文而变化,因此不能共享。客户对象负责在需要时向享元传递外在状态。

换句话说,一个对象的状态可以被分解为内在状态和外在状态,其中内在状态是一组对象的所有状态的交集,而外在状态则是对象状态与内在状态的差异。由于内在状态在组中的每个对象中都重复出现,因此可以通过用单个存储单个内在状态的轻量级对象替换对象组来节省空间。然而,轻量级对象无法存储组中对象的多个外在状态,因此外在状态存储在外部,并在每个客户端对象的请求中传递给轻量级对象。这种优化的通信协议通常被称为无状态协议,因为轻量级对象不存储外在状态。无状态协议的示例包括IP和HTTP(更一般地,任何REST协议,其中内在状态称为资源状态,外在状态称为应用程序状态)。
例如,让我们以三个对象及其各自的客户端为例:

o1 ← c1
o2 ← c2
o3 ← c3

我们可以将每个对象的状态分解为相对于这三个对象:

state 1 = 内在状态外在状态 1
state 2 = 内在状态外在状态 2
state 3 = 内在状态外在状态 3

其中:

内在状态 = state 1state 2state 3
外在状态 1 = state 1 \ 内在状态
外在状态 2 = state 2 \ 内在状态
外在状态 3 = state 3 \ 内在状态

这里的内在状态是重复的。因此将其存储在单个享元对象中(并将外在状态移动到客户端)可以节省空间:

o ← c1, c2, c3
(这是一行编程代码,无法翻译成中文)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接