From: Brendan Guild on
Dmitry A. Kazakov wrote in
news:bmmy63egmpu4$.hozmw5ipqs78.dlg(a)40tude.net:

> On Sat, 09 Sep 2006 17:20:27 GMT, Brendan Guild wrote:
>> That is true, but I am still not certain that I should make these
>> different types of properties into different classes rather than
>> merely giving them a member which distinguishes them. If your
>> domain was about cars, you could make red cars and black cars
>> into to different classes, but wouldn't it be better to just have
>> a color attribute?
>
> You are mixing different issues. How many colors a car cold have?
> vs. How many types of colors do exist?

That is true, but the number of property types that I expect to have
is greater than the number of colors that I expect to have, and even
worse, I expect new property types to be added frequently in
maintenance.

Just to be clear, when I say 'property type' I'm using domain
terminology. I do not expect property types to actually be
represented by types.

>> Each property has its own semantics and it must
>> be created and used deliberately by someone, depending on what is
>> needed in each part of the game. Entities might send Properties
>> on themselves and on each other, so those properties would
>> certainly be known, but others might not be.
>
> I doubt you could make "doesn't know" model very useful. If Entity
> is just a container of properties, then setting a property cannot
> influence Entity in any way, other than element change. But,
> normally, it should do something upon Set, or even Get. So either
> Property has to know Entity or reverse. The latter case is MD, the
> former case is mix-in, i.e. Property acts as a reference to Entity
> and delegates Set to Entity as a sequence of actions on Entity.

The entities could be like pieces on a chess board. They are used as
tools by the client and moved around according to the rules. I don't
think that they need to know anything. In fact, I have plans to
divide entities into two subclasses: Animate and Inanimate, which
will represent whether the entity has motivation to move itself. The
Inanimate entities will have absolutely know use for their own
properties, but the properties will affect how the animate entities
behave towards the inanimate ones.

> When you call Get it returns a new object of the type Property,
> which is a copy of some other object, contained by Entity. If
> Property are big, you might wish to use a referential semantics,
> based on some sort of GC, etc. BUT semantically that would change
> nothing, because the returned Property (a handle, smart pointer to
> some Property_Implementation, invisible to the clients) still had
> value semantics.

I have been thinking of making individual property objects immutable,
but it has also been suggested that I replace the set method of
Entity with something that the client calls in this way:

Instead of: entity->set(propertyType,property)
The client would call: entity->get(propertyType)->set(value)

Altering the value semantics of the property instead of replacing the
property.

>> The properties are created in a factory when the Entity is
>> created, and also whenever the Entity changes during the events
>> of the game. For example, an entity might start out with the
>> property '6 feet tall', so that property would be created by the
>> factory. Just for example, a year might pass and then a new
>> property might be created by whatever is looking after time: 1
>> year old. Then the '1 year old' property would be given to the
>> entity to mark its birthday.
>
> It is too loose to my taste. I see no behavior - is "1 year old"
> really a property of entity or just a perception of the entity by
> another entity? Then, what was the value of "1 year old" before
> one year passed? Could a client query it? This boils down to the
> same question "what does Entity know."

It seems like my terminology is causing me problems again. When I
made my first post I was using the words 'property' and 'attribute'
and 'member variable' as though they were the same, but then I
realized that something like 'age' was not actually a property, it
was a collection of properties and only specific ages were actual
properties. Therefore, I started calling 'age' a property type, while
'1 year old' becomes an actual property.

The reason for the change is that '1 year old' is represented by an
actual property object that contains the number 1. On the other hand,
'age' is not necessarily represented by any object, though I intend
to represented it by yet another object, called a 'property type'
object.

> You know, MD is a fundamental thing, either you have it or not.
> When you do, then even if you weren't aware of that, it will
> proliferate your design in the form of quite ugly patterns. BTW,
> the check is very simple, draw a chart: Entry'Class \
> Property'Class. It is a simple matrix rows are types of entities,
> columns are types of properties. When this matrix is not a
> permutation matrix (cannot be reordered to a diagonal by shuffling
> columns and rows), then you have it.

What are the elements of the matrix?

I am sure that the matrix will not be square, since I expect to have
just two types of entities and many property types, perhaps hundreds
of property types.

I'll take a guess at the elements of the matrix: The element (i,j) is
1 if entity type i can have property type j and 0 otherwise. In that
case, all the elements would be 1.
From: H. S. Lahman on
Responding to Guild...

>>Having said all this I now have to push back and ask: Why do you
>>feel you need to restrict access to Properties?
>
>
> There are two reasons that I originally sought to restrict access to
> Properties. The first is that I know in advance that there will be an
> unmanageably large number of properties. I concluded that if I could
> in any manner prevent certain procedures from accessing and modifying
> any but a small subset of the properties, it would simplify
> debugging.

It is the last sentence I am pushing back on. I am not sure I see how
that helps in debugging. When using the standard techniques we have
been talking about (e.g., multiple, restricted interfaces) one is really
addressing future maintenance. That is, the current clients using the
restricted interface aren't accessing the properties anyway; you are
just preventing them from doing so in the future.

The corollary is that if you have a large number of clients accessing
the properties in a particular interface, then you will have a debugging
problem whether you restricted access or not. That is, you are going to
have to provide the interface to every client that needs it anyway.

But that introduces a new question: Why do so many clients need to
access a given set of properties? I submit that your debugging will,
indeed, be easier if fewer clients need the properties. But let's look
a couple of issues before going after how to do that.

The first issue is the nature of a "client". Do you mean objects from a
large number of classes or a large number of objects from one or a few
classes? The former would be worrisome and I would look for a way to
isolate the update responsibility in a single or a few class(es) of
objects. IOW, delegate the actual update responsibility from the
numerous clients to a few delegatees. The latter (many objects from one
of a few classes) is much less of a problem because of the way
relationships limit participation in collaborations, which we have
talked about before.

The next issue is the nature of the access. Clearly write access from a
lot of contexts is a problem, which is why one doesn't want clients from
a lot of different classes. Read access, though, is usually only a
debugging problem in contexts where the read access must be synchronized
with write access (i.e., the value retrieved is timely). That's
potentially a nasty problem. However, it is really a flow of control
problem rather than an access problem. IOW, you have to resolve data
integrity constraints by defining the sequence of collaboration messages.

[Obviously, that gets easier if the clients are from one or a few
classes. It is no accident that in UML one defines flow of control in
an Interaction Diagram in terms of messages between object classes. No
matter how many objects are in each class, the collaboration sequence
defined applies to them all. Ain't it great when a Plan comes together?]

So the point I am getting to here is that your debugging problem may be
less about restricting access than about abstracting responsibilities,
delegation, and flow of control. That brings me back to my concerns
about Entity as the Mother Of Game Artifacts.

So far Entity sounds like a composite of a gazillion disparate game
artifacts. While that may be a single class of objects, its members
might not be cohesive. So if you broke Entity up into a bunch of
independent classes you might see that your debugging problem was due to
objects from many different classes accessing a group of properties. At
that point the notion of delegation to isolate updates and mitigate data
integrity problems might become clearer as a solution.

Alas, that is speculation without more detailed knowledge of the problem
space.

> The second reason is something which I decided to not mention because
> it introduces another design complexity. I would like to generate new
> property types during calculations that can be used on existing
> entities without modification. By this I mean, property types that
> generate properties when needed from existing properties instead of
> simply looking up the property in a collection. Here's a diagram:
>
> * sums
> [PropertyType]-----------------+
> A |
> | |
> +----------+----------+ |
> | | 1 |
> [PrimitivePropertyType] [SumPropertyType]----+
>
> It's the Composite pattern that I like so much. When someone requests
> a property from an entity, the collection first checks for the
> existence of the property, but if it does not exist, then an abstract
> method of PropertyType is called to compute the property as needed,
> given the collection object itself.
>
> For example, suppose we had three property types: 'weight', 'body
> mass', 'armor mass'. The value of the 'weight' property should always
> be the sum of the 'body mass' and the 'armor mass'. Instead of
> storing 'weight' and keeping it updated, or calculating it as needed,
> I create a new property type object called 'weight' that acts just
> like any other property type, except for the fact that no actual
> property exists in the collection.

This is a rather special problem related to data integrity. In fact, it
is so special that in UML OOA/D models one uses a special notational
flag to identify dependent attributes like 'weight' whose value depends
upon the values of other, independent attribute values. What such
dependent attributes mean is that the Class Model is not in Third Normal
Form based on Codd's Relational Data Model. (To paraphrase 3NF: an
object knowledge attribute's value must depend on the object's identity
and nothing but the object's identity.)

The bottom line is that the developer must ensure that data integrity is
preserved for any context where 'weight' is read. The normal way that
is implemented during OOP is: (A) provide no data store for 'weight';
(B) provide a getter for 'weight' that computes the value from 'body
mass' and 'armor mass'; and (C) provide no setter for 'weight'.

[Alas, that is not sufficient in some situations. If the dependent
attributes are supplied in pairs but they ar
From: H. S. Lahman on
Responding to Guild...

>>In that context it is fair to look at the type of the object, just
>>as the factory object looks at the type of the object in its input
>>stream to decide what constructor to invoke. So this is a context
>>(object stream processing) where _dynamic_cast is justified --
>>simply because the language doesn't provide anything better.
>
>
> It seems counter-intuitive that using dynamic cast would be superior
> to the Serializer pattern, but I suppose it makes sense!

I see it as lesser of evils. The OOPL doesn't provide an elegant way to
deal with streams of arbitrary objects vs. lack of object cohesion.

>>I would also point out is there there is a dispatch to the right
>>processing for persistence of a particular object in both
>>Serializer and my "deconstructor" approach. In Serializer it is
>>elegantly polymorphic because a common interface to the grunt code
>>was provided. In fact, you can do exactly the same thing by
>>providing the same common interface for the deconstruction
>>methods:
>>
>> * parses R1 1
>>[ObjectA] --------------------- [Deconstructor]
>> + saveIt()
>> A
>> | R2
>> +----------------+-----------...
>> | |
>> [ConcreteA] [ConcreteB]
>>
>>When you read the object type you can instantiate the R1
>>relationship so that ObjectA is processed by the ConcreteA
>>deconstructor when saveIt() is invoked. Essentially what you have
>>done is moved the polymorphic dispatch from the object to be saved
>>into a GoF Strategy pattern.
>
>
> This is something I had not thought of. So each persistence object
> contains a reference to a strategy object for encoding itself for the
> persistence subsystem. That very nicely does the job without any
> dynamic casts, though the relationship R1 is perhaps unfortunate.

Alas, you still need to know what the type is of ObjectA to assign the
R1 relationship. B-(

Also, the [Deconstructor] is essentially the persistence object that
corresponds to the problem object (i.e., the ConcreteA object has
exactly the same code to support the protocol as the local methods in a
serialized ObjectA). Some other object, which I didn't show in the
diagram, manages the overall process of saving objects. It "walks" the
objects to be saved, instantiates R1, and invokes saveIt() for each one.
But you would have a similar functionality for Serializer to "walk"
those objects.

There are variations on the theme. The [Deconstructor] above might do
nothing except read the attributes and pass them back to the caller as a
value list. Then the caller would encode the values in the persistence
format and write them out. That has the advantage that the code of
encoding the persistence format and actually writing it out is only done
once for all objects. (Of course if the Serializer implementation is
clever it will do the same thing by providing a callback for the local
methods to use.)

>
> Similar relations would of course be present for similar tasks, such
> as displaying. And there will always be classes of objects beyond my
> control, so for them I might to do something like this:
>
> 1 R1 contains *
> [MyObject] --------------------- [TheirObject]
> | 1 | *
> | R2 * | 1
> +---------------------------- [Deconstructor]
>
> Where objects of class MyObject is in a relationship with objects of
> class TheirObject, so when it comes time to encode MyObject, I need
> to encode all the related TheirObjects. Since I do not control
> TheirObject, I cannot install a strategy inside TheirObject, but I
> can use the R2 relationship to find all the relevant Dconstructors.

Yes, display works pretty much the same way; just a different target for
the output messages.

Actually you have pointed out an advantage I didn't think of. If you
need to save, say, third party library instances, you can't really do
that with Serializer because they didn't compose with the protocol. But
there is nothing to prevent you from using the separate deconstructor
approach because you know what the attributes are that must be read. So
you can write a [Deconstructor] for it. (Doesn't work, though, if their
instance has private state data; but then you are screwed anyway.)

>>As I reasoned in the other post, I think information hiding is
>>overrated except when explicit requirement restrictions on access
>>exist and then I would already have put "hard" mechanisms in
>>place. IOW, I usually provide read access of all attributes as a
>>matter of course in defining interfaces unless there are specific
>>requirements to the contrary.
>
>
> Information hiding is also useful for proving correctness. Some
> objects have much more complicated internal state than the external
> state, and one great benefit of it is that you can be sure that the
> behaviour of other objects will not depend on the complicated
> internal state, only on the simple external state.

I have I disagree with that. Internal state data does affect
interactions with other objects whenever the internal state results from
multiple interactions. Consider a stack. It had better have the right
items enqueued and the right internal item count before the object you
are testing starts popping from it in the context the test case assumes.
Lacking direct access to initialize that internal state properly one
often faces some tedious setup work to initialize the stack before the
test case can execute.

Unit testing is the only context where I think the C++ friend is useful
for exactly that reason. For unit test you need to access the private
state data.

> That could save a lot of time in black-box testing, for example, and
> it just allows for fewer possible interactions between objects which
> makes thinking about the possible ways for things to go wrong easier.

It makes test case specification much easier but it makes test case
implementation much harder. B-) I have seen systems where one had to
run hundreds of thousands of stimuli events just to get the system into
an internal state where a particular test case could be run.
From: Brendan Guild on
I'm going to have to break my response up into more than one post
because while I can answer some of these questions quickly, others
will require greater thought.

H. S. Lahman wrote in news:DgjNg.4729$Qb2.535(a)trnddc08:
> So far Entity sounds like a composite of a gazillion disparate
> game artifacts. While that may be a single class of objects, its
> members might not be cohesive. So if you broke Entity up into a
> bunch of independent classes you might see that your debugging
> problem was due to objects from many different classes accessing a
> group of properties. At that point the notion of delegation to
> isolate updates and mitigate data integrity problems might become
> clearer as a solution.

The domain specifically disallows any categorization of entities based
upon the roles that entities can play. It does this by not supplying
an exhaustive list of entities in advance, leaving the doors open for
more entities to come in during maintenance, and it is expected that
any arbitrary categorization of entities will be broken by future
entities.

> Alas, that is speculation without more detailed knowledge of the
> problem space.

I suspect that the kind of knowledge you would like is mostly in the
area where the domain is most fluid. The domain is no more specific
about the sorts and roles of entities than:

Entities are physical objects in a simulated world that are smaller
than a room and larger than the smallest object a person can see.
Entities always exist with some position, either inside another entity
or on a map of the simulated environment. Some entities initiate
events in the world which can include the following: moving another
entity from one position to another, altering the physical state of
any entity in arbitrary ways.

And of course there are the fundamental physical properties such as
'size', 'weight', 'strength', etc. I won't go into a list of known
property types because I am sure it's not important, and the list
could not be exhaustive or invariant during maintenance.

And of course even this is not truly a complete description, because
during maintenance there will always be crazy ideas for new roles for
entities.

> However, I have the feeling that this is a red herring because you
> are really interested in a more general dynamic assignment of
> properties akin to the notion of skills that some Gnomes might
> acquire over their game lives. For properties like that, I think
> making them first class objects and treating them like the A-V
> pairs related to the entity that we talked about is a more
> standard solution.

Perhaps, though I am most worried about the fact that if properties
are not represented by a collection relation as we discussed, then the
interface to Entity will be forced to change during maintenance and
all the unpleasant side-effects that might have.

> It's your problem domain, but I'm still skeptical that there
> aren't significant differences among your animate entities and
> among your inanimate entities (e.g., gnome vs. troll or chair vs.
> stairway).

You are right that there would be significant differences between
animate and to a less extend inanimate entities. A gnome and a troll
would have different properties, though probably only quantitative
differences, not actually different property types. They would also
have different behaviours that would likely be represented using the
Strategy pattern.

Despite the differences, the semantics of the properties that are
shared between entities, even animate and inanimate ones, do not
change from entity to entity. 'Height' has the same meaning for a
troll or a gnome, and the same theoretical range of values (though
likely quite different in practice.) A 10 foot gnome and a 10 foot
troll are both just 10 foot tall entities with different behaviours.

All of the properties of entities have semantics which do not vary
over categorizations, because there are no categorizations in the
domain.

> I see this as a matter of basic problem space abstraction. I've
> done a lot of OO design in the past few decades and I can't even
> recall a time where I was tempted to use multiple inheritance.
> But I think that demonstrates that we employ fundamentally
> different approaches to OO abstraction.
>
> For example, my reaction to, "...an object that should be in
> several disjoint classes", is instinctively: How could that be?
> Problem space entities are identifiable and have unique,
> /intrinsic/ characteristics at a given level of abstraction.
> (This obviously doesn't apply to computing space artifacts like
> String and Array that are reused during OOP and appear as ADTs
> during OOA/D.)

Suppose we have broken the entities down into subclasses. Monster and
Weapon seem like a reasonable division. Weapons can be held and used
to inflict horrible injuries upon Monsters. Monsters can hold Weapons
and can sustain horrible injuries from Weapons held by other Monsters.
Weapons have the attribute 'injuryPotential', meaning the amount of
injury that is inflicted when using the weapon. Monsters have an
attribute called 'injury' indicating the amount of injury already
sustained by the Monster.

Suppose the game is finished and all the design work is done, but now
for some reason you are in the role of maintainer and the domain has
shifted slightly to include a new Monster called a 'Jaws' that
inflicts horrible injury by biting, but is only a few inches long. If
a monster is wearing appropriate gloves then the Monster can pick up a
'Jaws' and use it as a Weapon to inflict horrible injury, but if the
Monster is not wearing gloves, then the 'Jaws' can wiggle around and
bite the hand that holds it.

So 'Jaws' naturally has a 'injuryPotential' attribute and an 'injury'
attribute and it can fit into the role of a weapon or a monster. If
you would not use multiple inheritance here, then what would you do?
Surely you would be at least a little tempted.

I will conclude my response later.
From: Brendan Guild on
H. S. Lahman wrote in news:KkkNg.167$cf2.119(a)trndny07:

> Responding to Guild...
>>>
>>> * parses R1 1
>>>[ObjectA] --------------------- [Deconstructor]
>>> + saveIt()
>>> A
>>> | R2
>>> +----------------+-----------...
>>> | |
>>> [ConcreteA] [ConcreteB]
>>>
>>>When you read the object type you can instantiate the R1
>>>relationship so that ObjectA is processed by the ConcreteA
>>>deconstructor when saveIt() is invoked. Essentially what you
>>>have done is moved the polymorphic dispatch from the object to be
>>>saved into a GoF Strategy pattern.
>>
>>
>> This is something I had not thought of. So each persistence
>> object contains a reference to a strategy object for encoding
>> itself for the persistence subsystem. That very nicely does the
>> job without any dynamic casts, though the relationship R1 is
>> perhaps unfortunate.
>
> Alas, you still need to know what the type is of ObjectA to assign
> the R1 relationship. B-(
>
> Also, the [Deconstructor] is essentially the persistence object
> that corresponds to the problem object (i.e., the ConcreteA object
> has exactly the same code to support the protocol as the local
> methods in a serialized ObjectA). Some other object, which I
> didn't show in the diagram, manages the overall process of saving
> objects. It "walks" the objects to be saved, instantiates R1, and
> invokes saveIt() for each one.

Perhaps this is a terminology failure again, but I wonder if
'instantiates' means what I think it means. To me instantiation is a
kind of creation that makes a specific thing from a generalization,
such as a specific R1 relationship between two specific objects.

But surely there is no point in instantiating a relationship if you
know that it is only going to be used once, by saveIt(). The work I
expected you to use was 'navigates', as in: It walks the objects to
be save, navigates R1, and invokes saveIt() for each one. Surely the
factory instantiates R1, at the only time it could be done with
dynamic casts.

I has assumed that each ObjectA contained a reference to some
ConcreteA, because then the relationship could be created by the
factory and navigated by the persistence procedure.

>> Information hiding is also useful for proving correctness. Some
>> objects have much more complicated internal state than the
>> external state, and one great benefit of it is that you can be
>> sure that the behaviour of other objects will not depend on the
>> complicated internal state, only on the simple external state.
>
> I have I disagree with that. Internal state data does affect
> interactions with other objects whenever the internal state
> results from multiple interactions. Consider a stack. It had
> better have the right items enqueued and the right internal item
> count before the object you are testing starts popping from it in
> the context the test case assumes. Lacking direct access to
> initialize that internal state properly one often faces some
> tedious setup work to initialize the stack before the test case can
> execute.

That is a good point, but making the internal state of a stack
accessible does not simplify analyzing the correctness of the
program. It might make implementing the test easier, but that is an
implementation detail for 'friend' keywords and similar tricks. With
an accessible internal state you have all the same complexities, plus
a lot of new possible interactions between the stack and the client.
First  |  Prev  |  Next  |  Last
Pages: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Prev: Name of construct
Next: Deriving - .NET example