Sign in to follow this  
Sol Blue

Name for a pattern? Archetypal data for kinds of objects

Recommended Posts

Starting with an example: your RTS has a Unit class. A text file describes the properties of different units, e.g:

Pikeman
health: 10
speed: 5

Archer
health: 10
speed: 7


Instead of subclassing Unit, you provide each Unit object with a reference to a Description object. Each Description is a Flyweight: there is one Description object holding the const data universal to all pikemen, and every Unit that is a pikeman holds a pointer to that Description.

What do you call the "Description" class? It's not just a Flyweight: the key thing I want to capture is that this is a way of implementing data-driven properties for a kind of something. I've considered other names: each Unit gets an "Archetype," or a "Template," or "Prototype," but all of those are the names for other patterns.

I am often in this situation, whether it be units, buildings, items, etc. I always need to get the master "info" or the "blueprint" or the "description."

Any suggestions?

Share this post


Link to post
Share on other sites
I'd consider this a plain case of the Flyweight pattern; however, the "flyweights" are the pikemen Unit objects that reduce to position, residue hit points etc., not the shared read-only pikeman Archetype.

You are simply introducing a class to share some read-only data they have in common among a large number of objects of the same class to avoid redundancy, memory waste, and confusion between immutable and mutable data; I don't see any special requirement making your case fundamentally different from the textbook GoF example (factoring font glyphs and metrics out of self-drawing letter objects in a text editor).

Sticking to a data-driven design with a single Unit class (with or without a smart flyweight structure behind the scenes) instead of a pointless hierarchy of Pikeman, Archer etc. subclasses entailing complicated creational patterns is a separate, earlier design decision.

Share this post


Link to post
Share on other sites
Quote:
Original post by theOcelot
Either "Description" or "Prototype" works for me. To be honest, this is the first thing that popped into my mind: The universal design pattern. You'll at least find it interesting, if you can wade through it.


Interesting...thanks for the link.

Quote:
Original post by LorenzoGatti
I'd consider this a plain case of the Flyweight pattern; however, the "flyweights" are the pikemen Unit objects that reduce to position, residue hit points etc., not the shared read-only pikeman Archetype.


Ah -- you may be right that I mis-identified the component that should be called the Flyweight. Thank you.

What would you call the data itself, the shared read-only data, as you put it? This is the name I'm stuck on in my code, after a couple hours yesterday of brainstorming. Loading game object properties from a file seems like a frequent-enough practice that there ought to be a general name for the loaded data. In a previous program I called it "TypeInfo," the types being Archer, Pikeman, etc., but I'm not really happy with that.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sol Blue
A text file describes the properties of different units, e.g:
Memento.

Quote:
Instead of subclassing Unit, you provide each Unit object with a reference to a Description object. Each Description is a Flyweight: there is one Description object holding the const data universal to all pikemen, and every Unit that is a pikeman holds a pointer to that Description.
Prototype.

Quote:
What do you call the "Description" class?
It's a template (not a pattern).

A practical note - flyweight, despite algorithmically being better, will often work poorly in practice.

A copy-on-write is typically better. The observation is like this:
- majority of objects are not interesting and will not change or be customized (or are simpler to customize via templates)
- the few interesting objects will be heavily customized
- overhead of per-property will be large (for ints it's almost not worth doing, for strings other caching techniques exist)

Flyweight is one of prime examples of a worthless *design* pattern. It's an implementation detail, which brings together worst of all - huge overhead in implementation and no absolutely design benefits. The text editor example for flyweight is mostly nonsensical, it's not where the problems really lie.


As far as saving resources
- for memory it does not matter (there will either be much too much of it or way too little)
- for persistence - the "flyweight" can be performed by computing delta state from template.


Flyweight also introduces a potentially killer bug - what happens if something is loaded from stored state, but template has changed in between.

With full copy-on-write, interesting objects store their full state as it was when they were created. Uninteresting ones (those not touched) can change whenever, so they might as well use default template.

Full flyweight also introduces the huge dependency (same problem as C++ compilation complexity). Changing the very base template means each and every object needs to be updated, which be prohibitive (imagine having to load entire database and update all objects ever created and even those destroyed by still tracked).

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
Quote:
Original post by Sol Blue
A text file describes the properties of different units, e.g:
Memento.
A Memento is supposed to contain data pertaining one object, not many sets of many objects; and to be used to recall or reconstruct formerly existing object states, while this file is simply an input.
Quote:
A practical note - flyweight, despite algorithmically being better, will often work poorly in practice.

A copy-on-write is typically better.

Typically for what application? Not this one.
Quote:
The observation is like this:
- majority of objects are not interesting and will not change or be customized (or are simpler to customize via templates)
- the few interesting objects will be heavily customized
A very strange idea. Every Unit instance is going to have his own position, current hit points, movement destination etc. and to share massive amounts of constants, Strategy objects etc. with other Units of the same type. Each Unit instance is equally interesting and it contains the same data fields as any other; there are no "customizations", since the templates contain shared read-only data, not defaults that can be overridden by copy-on-write mechanisms.
Quote:
- overhead of per-property will be large (for ints it's almost not worth doing, for strings other caching techniques exist)

Quite the opposite: the single pointer or pseudo-pointer used by the Flyweight pattern has a fixed cost, with the same overhead regardless of what read-only fields have been factored away.
In fact, even an Archetype that doesn't contain any field is likely to be useful to compare Unit types (e.g. pikemen grouping themselves with other pikemen).
Quote:
Flyweight is one of prime examples of a worthless *design* pattern. It's an implementation detail, which brings together worst of all - huge overhead in implementation and no absolutely design benefits. The text editor example for flyweight is mostly nonsensical, it's not where the problems really lie.

You are, evidently, not talking about the same Flyweight pattern that I, theOcelot, Sol Blue and the GoF book are discussing.
Quote:
As far as saving resources
- for memory it does not matter (there will either be much too much of it or way too little)

Even if the memory usage can decrease from low to very low rather than from too much to acceptable, wasted main memory translates directly to wasted cache and thus decreased speed.
Quote:


Flyweight also introduces a potentially killer bug - what happens if something is loaded from stored state, but template has changed in between.

Do you mean something like initializing a level, creating Unit instances, then deciding that pikemen should have a speed of 15 instead of 12? A silly use case, which would nonetheless work correctly: you change the pikeman "Archetype", preferably not in the middle of an update, and next time a pikeman Unit accesses it, the new value is used.
Quote:

With full copy-on-write, interesting objects store their full state as it was when they were created. Uninteresting ones (those not touched) can change whenever, so they might as well use default template.

There are no "interesting" object with an extensive history of property changes: every unit is going to be processed uniformly. Maybe other sorts of game have complex units that don't share much with one another and work well with prototypes and COW references, but units in a RTS are highly standardized.
Quote:

Full flyweight also introduces the huge dependency (same problem as C++ compilation complexity). Changing the very base template means each and every object needs to be updated, which be prohibitive (imagine having to load entire database and update all objects ever created and even those destroyed by still tracked).

You are definitely misunderstanding both the Flyweight pattern and its application to the OP's needs.
There are only two classes (Unit and Archetype): altering either causes no dramatic consequence on code, as the changes are normally new features on Unit reading new fields in Archetype loaded from richer data files.
There are no bad dependencies between object instances either: in the unusual case that an Archetype instance is modified at runtime, Unit instances will access the altered Archetypes (which they are already linking to) at the first opportunity.
There is no database of redundant data to update: altering the rules in unit definitions invalidates saved games, which are the only place where Unit instances are persisted.

Share this post


Link to post
Share on other sites
Quote:
Original post by LorenzoGatti
Quote:
Original post by Antheus
Quote:
Original post by Sol Blue
A text file describes the properties of different units, e.g:
Memento.
A Memento is supposed to contain data pertaining one object, not many sets of many objects; and to be used to recall or reconstruct formerly existing object states, while this file is simply an input.


Yes, which is precisely what the text configuration file is - it uses memento to store templates.

Quote:
Typically for what application? Not this one.
Especially for this one.

Quote:
A very strange idea. Every Unit instance is going to have his own position, current hit points, movement destination etc. and to share massive amounts of constants, Strategy objects etc. with other Units of the same type. Each Unit instance is equally interesting and it contains the same data fields as any other; there are no "customizations",
Each of these is a "customization". Each position is unique, each destination, each AI state...

Quote:
Quite the opposite: the single pointer or pseudo-pointer used by the Flyweight pattern has a fixed cost, with the same overhead regardless of what read-only fields have been factored away.


Let's say... 10,000 units. Each one has 100 properties. That is one million pointers already. If all properties are accessed each time (aka all units are "interesting"), that means a whole lot of pointer chasing.

Full-state does not exclude caching of shared heavy state (textures, lookup tables, constant tables, ...).

Quote:
Even if the memory usage can decrease from low to very low rather than from too much to acceptable, wasted main memory translates directly to wasted cache and thus decreased speed.
No, wasted memory doesn't translate to anything. Accessing it does. What is better - having entire unit state in 4 consecutive cache lines or having its 100 properties accessed by one pointer chase each?

Quote:
There is no database of redundant data to update: altering the rules in unit definitions invalidates saved games

Level definitions as well.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
Let's say... 10,000 units. Each one has 100 properties. That is one million pointers already. If all properties are accessed each time (aka all units are "interesting"), that means a whole lot of pointer chasing.

Perhaps you're not thinking of the same thing everyone else is... I believe they're talking about something like this:
struct SharedReadOnlyData
{
float MaxHealth;
float MaxSpeed;
/* ... 98 other properties ... */
};

struct InstanceData
{
const SharedReadOnlyData* Type;
float Health;
float Speed;
Vector3 Position;
};
Which is one pointer per unit instance. It's essentially flyweight, but at a lower granularity than per-property and explicitly controlled by engineering. If one day, design says "Hey we need to buff max speed sometimes" then you can add a MaxSpeed field to the instance data and utilize copy-on-write mechanisms. In the worst (best?) case, you have an external system that can generically and flexibly handle COW semantics for any new property design wants to buff/modify/tweak. But at the lowest level, the data structures are written around the 95% usage case, and the last 5% can be handled at a higher level.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this