What is the difference between intrinsic and extrinsic state as described in Flyweight Pattern?
Asked Answered
S

4

11

From the chapter on FlyWeight Pattern inside Gang of Four the FlyWeight pattern is applicable when most object state can be made extrinsic.

What does extrinsic state mean ? I get the feeling that this pattern is used for sharing of objects . If objects are to be shared , then how can that object even have any state at all ?

Stereochromy answered 20/1, 2013 at 14:49 Comment(1)
Extrinsic state means external to the objectThrave
M
2

Whatever the specific wording in that bulleted list, it is important to understand the message: Flyweight applies to the case where an important part of state can be shared among many objects because it is some data that is the same for all of them. Typically the shared state is immutable by nature (i.e., "universal truth"). The example with font faces makes this quite clear; an example from everyday Java is java.util.regex.Pattern, the flyweight, vs. Matcher, the client object that reuses it and holds local extrinsic state. Many Matchers can exist in parallel, all reusing the compiled regex on the inside.

This quote makes things clearer than the one from your question:

The more flyweights are shared, the greater the storage savings. The savings increase with the amount of shared state. The greatest savings occur when the objects use substantial quantities of both intrinsic and extrinsic state, and the extrinsic state can be computed rather than stored. Then you save on storage in two ways: Sharing reduces the cost of intrinsic state, and you trade extrinsic state for computation time.

Maggio answered 20/1, 2013 at 14:56 Comment(28)
what you mean by client facing objects ?Stereochromy
Can you please provide an example in your answer to clarify your overall answer ? I am specifically not clear on how an external objects state is not owned by the object itself.Stereochromy
but why do they say that if most state can be made extrinsic the applicability of FlyWieight pattern becomes practical ?Stereochromy
let us continue this discussion in chatStereochromy
Still some confusion prevails here . I thought that sharing (thus in a sense reusing) can be done only for intrinsic state . Extrinsic state can not be shared and hence reused . It is the state that varies from object to object. So why most state have to extrinsic for the applicability of this particular pattern is what is confusing me .Stereochromy
From the GoF book ".The key concept here is the distinction between intrinsic and extrinsic state. Intrinsic state is stored in the flyweight;it consists of information that's independent of the flyweight's context, thereby making it sharable.Extrinsic state depends on and varies with the fly- weight's context and therefore can't be shared.Client objects are responsible for passing extrinsic state to the flyweight when it needs it.". So it is basically stating just the opposite of what you are stating here . Hence the confusion.Stereochromy
Flyweights have nothing to do with mutability.Awning
@Maggyero Quote from Wikipedia: "To enable safe sharing, between clients and threads, Flyweight objects must be immutable. Flyweight objects are by definition value objects. The identity of the object instance is of no consequence therefore two Flyweight instances of the same value are considered equal."Maggio
This Wikipedia statement is plain wrong. Thread-safe objects can be safely mutated with locks. Gamma et al. never used such an unnecessary restriction in the canonical definition of the Flyweight pattern in their Design Patterns book.Awning
@Maggyero I contest your view that the WP statement is "plain wrong" because in almost all actual cases the shared state is immutable by nature, being a cached result of computation or retrieval from storage. Only when taken to the extreme level of pedantry could we observe that in some very narrow cases it will actually make sense to update the shared state, applying changes to a large number of objects used in different contexts. It doesn't help at all to explain the concept, and such mutable implementations are much more complex and bug-prone (eg. a client assuming immutability by mistake).Maggio
It is not my view, it is the view of the creators of the pattern. It is clear that the intrinsic state v. extrinsic state concept is not easy to explain, but I am not sure how adding immutability helps understand it. To me it can even harm the understanding, as it focuses on mutability instead of sharability. For instance, if class variables always belong to intrinsic state it is not because they are immutable (they can be mutable, and often are) but because they are shared by all objects.Awning
@Maggyero We have no say in whether the view that the "wikipedia article is plain wrong" is the view of the creators of the pattern. All you claim is that they don't specifically require it, but that is not under discussion.Maggio
@Maggyero they can be mutable, and often are -- I can't think of any example, can you? It's not the variables that are shared but the data and almost always the objects rely on the fact that their data doesn't change if they are not those changing it. Mutable common state is as bad as mutable global state.Maggio
"I can't think of any example, can you?" Mutable class variables can be used for counting instances of a class for example.Awning
@Maggyero And you would consider that kind of global state belonging to the Flyweight pattern? I certainly don't see the applicability. Refcounting is definitely not considered an instance of this pattern.Maggio
Why not? The pattern applicability is explicitly stated in the book and does not exclude reference counting or any other mutable intrinsic state. The pattern intent is stated as well: "Use sharing to support large numbers of fine-grained objects efficiently." The idea is to save storage space by avoiding data duplication. The fact that this data can vary does not change anything. Mutability is actually orthogonal to the pattern.Awning
@Maggyero You have shown an example where you are forced to use global state, that's why it's not applicable. Flyweight fundamentally applies to extracting instance-local state to shared objects. You cannot possibly put refcounting state into a single object instance. You should perhaps get some real-world experience with this pattern to understand its essence, I think then it will become clearer to you why it's almost always immutable.Maggio
Counting the number of flyweight instances (the physical level) instead of the number of unshared instances (the logical level) might be needed in an application, so I disagree. Another example this time using mutable instance variables: what is the problem with having flyweights representing Unicode characters with the following intrinsic state: an immutable code point (instance variable) for character identification and a mutable name (instance variable)?Awning
@Maggyero The problem is that the name can change under your nose, causing races. For example, you write String name = getName() and then proceed to do something with it, but the name changes in the meantime. Also, some other code could ask the flyweight client object for the name and store it locally. When the shared name changes, this doesn't propagate. You would have to make the mutability explicit somehow, maybe add change callbacks etc. The result would just not fit Flyweight anymore, it would be some concoction that partially resembles Flyweight.Maggio
Counting the number of flyweight instances (the physical level) instead of the number of unshared instances (the logical level) might be needed in an application -- that would certainly be the internal state of the flyweights, not exposed to the client objects. Even String has a mutable hashCode field because that kind of internal mutability doesn't count.Maggio
@Maggyero Here's an important point you may have overlooked in the GoF book, one of mandatory requirements to qualify for applying Flyweight: The application doesn't depend on object identity. Since flyweight objects may be shared, identity tests will return true for conceptually distinct objects. On the Wikipedia page they refer to this rule when they say the flyweight is a value object: its identity is its value. This is tantamount to saying the object is immutable. You can't even conceptually mutate a value, it just becomes another value.Maggio
"The application doesn’t depend on object identity. Since flyweight objects may be shared, identity tests will return true for conceptually distinct objects." It does not mean that flyweights are value objects at all (they can be value objects or identity objects, and the former would be even useless). The identity test of two logical entities is true because they are implemented by the same physical flyweight object (and an object is always equal to itself, be the equality based on value or identity).Awning
Value objects are objects whose equality is based on value instead of identity (identity is often the default), thanks to an overriden equality method. They have nothing to do with flyweight objects. Where do you see a flyweight implemented with an overriden equality method in GoF’s book? It would be useless since you are comparing a flyweight object to itself anyway (there is only one flyweight instance per kind thanks to the flyweight factory).Awning
And here is what the Value object Wikipedia article says about value objects: "Being small, one can have multiple copies of the same value object that represent the same entity: it is often simpler to create a new object rather than rely on a single instance and use references to it." This is just the opposite of the flyweight pattern intent! Again the Flyweight pattern Wikipedia article got it all wrong.Awning
@Maggyero I think you missed the point again. In this case the term "value object' is used for the fact that many client objects share the same instance, retrieved from a factory method. These instances are considered conceptually distinct (every client object has its own), but in fact they are shared among them (being equal by value). As long as you're only interested in equality and not identity, there is no problem with this. But as soon as you would allow the value to change behind their back, it would break the pattern.Maggio
@Maggyero But, if you sincerely believe all you say, why don't you take action and actually edit the Wikipedia page? Or at least bring it up at the Talk page? The whole world is reading that page and you seem to know something that everyone else has missed.Maggio
"But as soon as you would allow the value to change behind their back, it would break the pattern." Could you elaborate, what will break exactly?Awning
The value for all other, conceptually distinct objects will appear to have changed spontaneously.Maggio
B
15

Let's take an example of a Word processor:

A Word processor deals with Character objects. The state of Character objects is the character content, the font, style,location etc (as far as the Word processor is concerned). Different documents use different instances of a character. Assuming we are just dealing with a-z chars, different documents use letters from a-z pool but might apply a different font/style. So, if we separate the content of the character from the font/style we can share these characters and this makes sense because the total different types of characters are less (26 in our case but a constant otherwise) compared to different instances of characters used in different documents. Sharing these character instances would mean to share the Character instances content wise and apply context like font/style externally to these characters. Character content is intrinsic state and font/style is extrinsic state. Separating state into intrinsic and extrinsic states led to huge storage savings in the above example.

Bust answered 21/1, 2013 at 8:34 Comment(2)
Nice explanation with the example.. +1Campanulate
So intrinsic state is state that represents the essential data of an object, such as character content. While extrinsic state are variants applied to the object with its intrinsic date / essential date, such as styling. Like a XML document together with a XLST style sheet (old reference) or HTML and CSS.Optician
H
11

extrinsic - state that belongs to the context of the object (external) or unique to that instance

intrinsic - state that naturally belongs to the 'FlyWeight' object and thus should be permanent or immutable (internal) or context free.

Hanuman answered 20/6, 2014 at 8:18 Comment(0)
M
2

Whatever the specific wording in that bulleted list, it is important to understand the message: Flyweight applies to the case where an important part of state can be shared among many objects because it is some data that is the same for all of them. Typically the shared state is immutable by nature (i.e., "universal truth"). The example with font faces makes this quite clear; an example from everyday Java is java.util.regex.Pattern, the flyweight, vs. Matcher, the client object that reuses it and holds local extrinsic state. Many Matchers can exist in parallel, all reusing the compiled regex on the inside.

This quote makes things clearer than the one from your question:

The more flyweights are shared, the greater the storage savings. The savings increase with the amount of shared state. The greatest savings occur when the objects use substantial quantities of both intrinsic and extrinsic state, and the extrinsic state can be computed rather than stored. Then you save on storage in two ways: Sharing reduces the cost of intrinsic state, and you trade extrinsic state for computation time.

Maggio answered 20/1, 2013 at 14:56 Comment(28)
what you mean by client facing objects ?Stereochromy
Can you please provide an example in your answer to clarify your overall answer ? I am specifically not clear on how an external objects state is not owned by the object itself.Stereochromy
but why do they say that if most state can be made extrinsic the applicability of FlyWieight pattern becomes practical ?Stereochromy
let us continue this discussion in chatStereochromy
Still some confusion prevails here . I thought that sharing (thus in a sense reusing) can be done only for intrinsic state . Extrinsic state can not be shared and hence reused . It is the state that varies from object to object. So why most state have to extrinsic for the applicability of this particular pattern is what is confusing me .Stereochromy
From the GoF book ".The key concept here is the distinction between intrinsic and extrinsic state. Intrinsic state is stored in the flyweight;it consists of information that's independent of the flyweight's context, thereby making it sharable.Extrinsic state depends on and varies with the fly- weight's context and therefore can't be shared.Client objects are responsible for passing extrinsic state to the flyweight when it needs it.". So it is basically stating just the opposite of what you are stating here . Hence the confusion.Stereochromy
Flyweights have nothing to do with mutability.Awning
@Maggyero Quote from Wikipedia: "To enable safe sharing, between clients and threads, Flyweight objects must be immutable. Flyweight objects are by definition value objects. The identity of the object instance is of no consequence therefore two Flyweight instances of the same value are considered equal."Maggio
This Wikipedia statement is plain wrong. Thread-safe objects can be safely mutated with locks. Gamma et al. never used such an unnecessary restriction in the canonical definition of the Flyweight pattern in their Design Patterns book.Awning
@Maggyero I contest your view that the WP statement is "plain wrong" because in almost all actual cases the shared state is immutable by nature, being a cached result of computation or retrieval from storage. Only when taken to the extreme level of pedantry could we observe that in some very narrow cases it will actually make sense to update the shared state, applying changes to a large number of objects used in different contexts. It doesn't help at all to explain the concept, and such mutable implementations are much more complex and bug-prone (eg. a client assuming immutability by mistake).Maggio
It is not my view, it is the view of the creators of the pattern. It is clear that the intrinsic state v. extrinsic state concept is not easy to explain, but I am not sure how adding immutability helps understand it. To me it can even harm the understanding, as it focuses on mutability instead of sharability. For instance, if class variables always belong to intrinsic state it is not because they are immutable (they can be mutable, and often are) but because they are shared by all objects.Awning
@Maggyero We have no say in whether the view that the "wikipedia article is plain wrong" is the view of the creators of the pattern. All you claim is that they don't specifically require it, but that is not under discussion.Maggio
@Maggyero they can be mutable, and often are -- I can't think of any example, can you? It's not the variables that are shared but the data and almost always the objects rely on the fact that their data doesn't change if they are not those changing it. Mutable common state is as bad as mutable global state.Maggio
"I can't think of any example, can you?" Mutable class variables can be used for counting instances of a class for example.Awning
@Maggyero And you would consider that kind of global state belonging to the Flyweight pattern? I certainly don't see the applicability. Refcounting is definitely not considered an instance of this pattern.Maggio
Why not? The pattern applicability is explicitly stated in the book and does not exclude reference counting or any other mutable intrinsic state. The pattern intent is stated as well: "Use sharing to support large numbers of fine-grained objects efficiently." The idea is to save storage space by avoiding data duplication. The fact that this data can vary does not change anything. Mutability is actually orthogonal to the pattern.Awning
@Maggyero You have shown an example where you are forced to use global state, that's why it's not applicable. Flyweight fundamentally applies to extracting instance-local state to shared objects. You cannot possibly put refcounting state into a single object instance. You should perhaps get some real-world experience with this pattern to understand its essence, I think then it will become clearer to you why it's almost always immutable.Maggio
Counting the number of flyweight instances (the physical level) instead of the number of unshared instances (the logical level) might be needed in an application, so I disagree. Another example this time using mutable instance variables: what is the problem with having flyweights representing Unicode characters with the following intrinsic state: an immutable code point (instance variable) for character identification and a mutable name (instance variable)?Awning
@Maggyero The problem is that the name can change under your nose, causing races. For example, you write String name = getName() and then proceed to do something with it, but the name changes in the meantime. Also, some other code could ask the flyweight client object for the name and store it locally. When the shared name changes, this doesn't propagate. You would have to make the mutability explicit somehow, maybe add change callbacks etc. The result would just not fit Flyweight anymore, it would be some concoction that partially resembles Flyweight.Maggio
Counting the number of flyweight instances (the physical level) instead of the number of unshared instances (the logical level) might be needed in an application -- that would certainly be the internal state of the flyweights, not exposed to the client objects. Even String has a mutable hashCode field because that kind of internal mutability doesn't count.Maggio
@Maggyero Here's an important point you may have overlooked in the GoF book, one of mandatory requirements to qualify for applying Flyweight: The application doesn't depend on object identity. Since flyweight objects may be shared, identity tests will return true for conceptually distinct objects. On the Wikipedia page they refer to this rule when they say the flyweight is a value object: its identity is its value. This is tantamount to saying the object is immutable. You can't even conceptually mutate a value, it just becomes another value.Maggio
"The application doesn’t depend on object identity. Since flyweight objects may be shared, identity tests will return true for conceptually distinct objects." It does not mean that flyweights are value objects at all (they can be value objects or identity objects, and the former would be even useless). The identity test of two logical entities is true because they are implemented by the same physical flyweight object (and an object is always equal to itself, be the equality based on value or identity).Awning
Value objects are objects whose equality is based on value instead of identity (identity is often the default), thanks to an overriden equality method. They have nothing to do with flyweight objects. Where do you see a flyweight implemented with an overriden equality method in GoF’s book? It would be useless since you are comparing a flyweight object to itself anyway (there is only one flyweight instance per kind thanks to the flyweight factory).Awning
And here is what the Value object Wikipedia article says about value objects: "Being small, one can have multiple copies of the same value object that represent the same entity: it is often simpler to create a new object rather than rely on a single instance and use references to it." This is just the opposite of the flyweight pattern intent! Again the Flyweight pattern Wikipedia article got it all wrong.Awning
@Maggyero I think you missed the point again. In this case the term "value object' is used for the fact that many client objects share the same instance, retrieved from a factory method. These instances are considered conceptually distinct (every client object has its own), but in fact they are shared among them (being equal by value). As long as you're only interested in equality and not identity, there is no problem with this. But as soon as you would allow the value to change behind their back, it would break the pattern.Maggio
@Maggyero But, if you sincerely believe all you say, why don't you take action and actually edit the Wikipedia page? Or at least bring it up at the Talk page? The whole world is reading that page and you seem to know something that everyone else has missed.Maggio
"But as soon as you would allow the value to change behind their back, it would break the pattern." Could you elaborate, what will break exactly?Awning
The value for all other, conceptually distinct objects will appear to have changed spontaneously.Maggio
P
1

Gang of Four’s Flyweight design pattern introduces the concept of intrinsic and extrinsic states:

The key concept here is the distinction between intrinsic and extrinsic state. Intrinsic state is stored in the flyweight; it consists of information that’s independent of the flyweight’s context, thereby making it sharable. Extrinsic state depends on and varies with the flyweight’s context and therefore can’t be shared. Client objects are responsible for passing extrinsic state to the flyweight when it needs it.

In other words, the state of an object can be decomposed with respect to a group of objects as an intrinsic state and an extrinsic state, where the intrinsic state is the intersection of the states of all objects of the group and the extrinsic state is the difference of the state of the object and the intrinsic state. Since the intrinsic state is duplicated in each object of the group, space can be saved by replacing the group of objects by a single flyweight object storing a single intrinsic state. The flyweight object cannot however store the multiple extrinsic states of the objects of the group, so the extrinsic states are stored outside and passed to the flyweight object in each request from client objects. Such an optimised communication protocol is often called a stateless protocol since the flyweight object does not store extrinsic state. Examples of stateless protocols include IP and HTTP (and more generally any REST protocols, where intrinsic state is called resource state and extrinsic state is called application state).

For instance, let’s take three objects with their respective clients:

o1 ← c1
o2 ← c2
o3 ← c3

We can decompose the state of each object with respect to the three objects:

state 1 = intrinsic stateextrinsic state 1
state 2 = intrinsic stateextrinsic state 2
state 3 = intrinsic stateextrinsic state 3

where:

intrinsic state = state 1state 2state 3
extrinsic state 1 = state 1 \ intrinsic state
extrinsic state 2 = state 2 \ intrinsic state
extrinsic state 3 = state 3 \ intrinsic state

Here the intrinsic state is duplicated. So storing it in a single flyweight object (and moving the extrinsic states into clients) saves space:

o ← c1, c2, c3

Part answered 6/4, 2020 at 0:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.