|
Prev: Name of construct
Next: Deriving - .NET example
From: Brendan Guild on 4 Sep 2006 05:17 I have a rather awkward problem domain and I have found a potential solution, but it is rather tentative and unsettling. The problem with it is that it exposes member variables almost directly to the user, almost as if they were public. The domain I am dealing with is an adventure game that involves many various items and creatures. Each item or creature will have many properties that make it distinct from the others, but the nature of these properties is not all known in advance. The entities in the game will move around and have interactions which will be affected by the properties of each entity in some manner and each interaction might make use of any fixed set of properties, up to all the properties. Enough of these properties and interactions are known to make a working implementation, but it is guaranteed that new properties and interactions will be created in maintenance, enough so that I cannot depend on any fixed set of properties. The natural solution is to represent each of these amorphous entities as an object and the properties of each object are dictated by the class of the object. Using many classes all the possible combinations of properties needed for the game can be created, and of course many more classes would be added in maintenance. Unfortunately, I don't know which entities will interact with which and I have no means of creating a reliable class hierarchy to organize them. In each interaction, each entity would need to be dynamically analyzed to determine which class it belongs to so that its properties could be accessed. I might do that with the visitor pattern or a switch statement, but I found another way. Since I cannot make any reliable relationship diagrams between the entities, I represent them all as objects of a single class, like this pseudocode: class Entity { Object get(Property); void set(Property, Object); } In Entity I replace a potentially very large number of getters and setters by a single parameterized pair. Entity has no encapsulation and no real methods, but the great thing about it is that its public interface will never have to change during maintenance. The Property class is completely featureless, with no members of any sort, so creating new properties is trivial. At first I thought that this was a horrible design because it is making everything public, but then I realized that the member variables are not truly public because they can only be accessed when the correct Property object is available. Since I will not be making the Property objects globally available, what can access what will be very strictly controlled. Then I realized that I could create more complicated properties that behave just like ordinary properties in that they get and set values, but they are actually composed of other properties and use them to compute a derived property. That realization suddenly makes this idea all the more interesting! Does this match some design pattern that I should know about? Is it just a horrible idea?
From: H. S. Lahman on 4 Sep 2006 13:03 Responding to Guild... How refreshing to have an actual problem description on this forum rather that some fragments of solution code... > I have a rather awkward problem domain and I have found a potential > solution, but it is rather tentative and unsettling. The problem with > it is that it exposes member variables almost directly to the user, > almost as if they were public. > > The domain I am dealing with is an adventure game that involves many > various items and creatures. Each item or creature will have many > properties that make it distinct from the others, but the nature of > these properties is not all known in advance. > > The entities in the game will move around and have interactions which > will be affected by the properties of each entity in some manner and > each interaction might make use of any fixed set of properties, up to > all the properties. > > Enough of these properties and interactions are known to make a > working implementation, but it is guaranteed that new properties and > interactions will be created in maintenance, enough so that I cannot > depend on any fixed set of properties. > > The natural solution is to represent each of these amorphous entities > as an object and the properties of each object are dictated by the > class of the object. Using many classes all the possible combinations > of properties needed for the game can be created, and of course many > more classes would be added in maintenance. Right. Trying to organize Entity into a generalization relationship would probably lead to a combinatorial explosion of subclasses. If you delegate properties 1 R1 possesses * [Entity] ---------------------- [Property] A | <subclasses> your generalization tree is limited to flavors of [Property] and the R1 relationship captures the notion of a collection of a specific set of Properties for a given Entity. [Caveat. There is potentially a problem with the '1' multiplicity for R1 because the same properties might be relevant for different entities. However, I am guessing that Property instances are likely to have attribute values that are unique to the Entity they are associated with (e.g., a Skill system for the Entity can modify the Property values).] > > Unfortunately, I don't know which entities will interact with which > and I have no means of creating a reliable class hierarchy to > organize them. In each interaction, each entity would need to be > dynamically analyzed to determine which class it belongs to so that > its properties could be accessed. I might do that with the visitor > pattern or a switch statement, but I found another way. Good. B-) > > Since I cannot make any reliable relationship diagrams between the > entities, I represent them all as objects of a single class, like > this pseudocode: > > class Entity { > Object get(Property); > void set(Property, Object); > } > > In Entity I replace a potentially very large number of getters and > setters by a single parameterized pair. Entity has no encapsulation > and no real methods, but the great thing about it is that its public > interface will never have to change during maintenance. I assume that "Property" in the pseudo code is actually a property type code while "Object" is a reference to a property instance. Basically you are implementing a variation of the R1 relationship above. The get/set operations would actually be methods of the R1 collection class, which would be a member of [Entity]. Assuming there is at most only one Property instance for each property type, then the R1 collection class would own the smarts for finding the right one. That provides separation of concerns and encapsulation. Without further information I would be inclined to simplify the setter by making a [Property] have a type attribute and specializing the R1 collection class to be able to access that attribute to do its search (or provide an ordered list). Then the setter only needs the Property reference. That would allow the actual client to add properties to an entity without knowing what they were. I don't know whether that would be attractive in you particular game. > > The Property class is completely featureless, with no members of any > sort, so creating new properties is trivial. I am missing something here. How can a Property be featureless if there seem to be different flavors (i.e., there is a "new" property)? There has to be something to distinguish, say, a Spell property from a Sword property. [Note that this sort of flavorizing of properties is pretty common in games. One bundles a suite of attributes for things like Combat and Movement properties. That sort of structure is clearly reflected in the modding configuration files for games like CIV.] > > At first I thought that this was a horrible design because it is > making everything public, but then I realized that the member > variables are not truly public because they can only be accessed when > the correct Property object is available. Since I will not be making > the Property objects globally available, what can access what will be > very strictly controlled. Right. R1 is just a relationship that restricts what properties a particular entity has. Whoever uses the getter necessarily has a context where collaborating with the property is important. The R1 collection class now owns the responsibilities for ensuring correct access (responding NO_SUCH_PROPERTY) and locating particular properties. However, I would point out that the client of the getter is someone who needs to collaborate with a specific Property, not the Entity itself. So what one really has is: [Client] | 1 | | R2 | | accesses properties of | * [Entity] | 1 | | R1 | | possesses | * [Property] The collaboration path is really R2 -> R1 where [Entity] is basically just a placeholder node on the path. Thus the semantics of [Entity] is not relevant to whatever the Client wants to do with the Property. Your application will generally be more robust if you make relationship implementation and navigation orthogonal to the act
From: Daniel T. on 5 Sep 2006 07:12 Brendan Guild <dont(a)spam.me> wrote: > I have a rather awkward problem domain and I have found a potential > solution, but it is rather tentative and unsettling. The problem with > it is that it exposes member variables almost directly to the user, > almost as if they were public. > > The domain I am dealing with is an adventure game that involves many > various items and creatures. Each item or creature will have many > properties that make it distinct from the others, but the nature of > these properties is not all known in advance. > > Does this match some design pattern that I should know about? Is it > just a horrible idea? It sounds to me like you are looking at the problem from a non-OO perspective. How about this problem description instead. Your domain involves characters that can do a limited number of things (move, fight, pick-up, drop.) The items that a character is carrying modify its ability to do those things in various ways. class Character: def acquire(self, item): self.items.append( item ) def move(self, location): if self.canMoveTo( location ): self.doMove( location ) def canMoveTo(self, location): result = false; for item in items: result = result or item.affectMove( self, location ) return result def doMove(self, location): "actually move the character" -- There are two things that simply cannot be doubted. Logic and our ability to sense the world around us. Doubt those, and you no longer have anyone to discuss it with, nor any ability to discuss it.
From: Rick Elbers on 5 Sep 2006 17:10 Op Tue, 05 Sep 2006 11:12:03 GMT schreef "Daniel T." <daniel_t(a)earthlink.net>: > Brendan Guild <dont(a)spam.me> wrote: > >> I have a rather awkward problem domain and I have found a potential >> solution, but it is rather tentative and unsettling. The problem with >> it is that it exposes member variables almost directly to the user, >> almost as if they were public. >> >> The domain I am dealing with is an adventure game that involves many >> various items and creatures. Each item or creature will have many >> properties that make it distinct from the others, but the nature of >> these properties is not all known in advance. >> >> Does this match some design pattern that I should know about? Is it >> just a horrible idea? > This matches "many various" design patterns. >It sounds to me like you are looking at the problem from a non-OO >perspective. How about this problem description instead. > >Your domain involves characters that can do a limited number of things >(move, fight, pick-up, drop.) The items that a character is carrying >modify its ability to do those things in various ways. > >class Character: > def acquire(self, item): > self.items.append( item ) > > def move(self, location): > if self.canMoveTo( location ): > self.doMove( location ) > > def canMoveTo(self, location): > result = false; > for item in items: > result = result or item.affectMove( self, location ) > return result > > def doMove(self, location): > "actually move the character" > There are two things that simply cannot be doubted. Logic and our > ability to sense the world around us. Doubt those, and you no longer > have anyone to discuss it with, nor any ability to discuss it. How many logics we know about ? About the "ability to sense the world around us" Hume will applaud to you. Alas "the great doubter" wouldnt. And he is more famous:-) Rick
From: Brendan Guild on 6 Sep 2006 14:20
H. S. Lahman wrote in news:E%YKg.14590$%_1.641(a)trndny07: > Responding to Guild... >> Since I cannot make any reliable relationship diagrams between >> the entities, I represent them all as objects of a single class, >> like this pseudocode: >> >> class Entity { >> Object get(Property); >> void set(Property, Object); >> } >> >> In Entity I replace a potentially very large number of getters >> and setters by a single parameterized pair. Entity has no >> encapsulation and no real methods, but the great thing about it >> is that its public interface will never have to change during >> maintenance. > > I assume that "Property" in the pseudo code is actually a property > type code while "Object" is a reference to a property instance. Yes, exactly. I realize that despite my care I somehow managed to still assume some domain knowledge when writing my problem description. Each property of an entity is like a member variable of a class. I hadn't considered calling them a property type, but that is what it is. For example, one might have a property type called 'height' and a property instance of that type such as '6 feet tall'. Then a better pseudo-code would be: class Entity { PropertyInstance get(PropertyType); void set(PropertyType, PropertyInstance); } And there is an assumption that each entity can have at most one property of each property type at any time. In other words, you assumed correctly but I should have been more clear. Perhaps I should consider the possibility of having multiple instances of a property type in a single entity, though I doubt that will be necessary. The usage of the getter and setter is intended to be much like this: get(propertyA) is a more general equivalent to getPropertyA() set(propertyA, propertyValue) is equivalent to setPropertyA(propertyValue) Or, alternatively, just like having each property type as a public member variable of the class entity. > Without further information I would be inclined to simplify the > setter by making a [Property] have a type attribute and > specializing the R1 collection class to be able to access that > attribute to do its search (or provide an ordered list). Then the > setter only needs the Property reference. That would allow the > actual client to add properties to an entity without knowing what > they were. In other words, wrapping up the property and property type into one value. That is certainly an interesting possibility and now that I recognize my misuse of the word 'property', I realize that it makes perfect sense to do it that way. However, property instances and property types would be most often have to be dealt with separately because no similar simplification can be made to the getter. Most computations are sure to involve getting several properties from some entities based on their property types, then using those properties in some formula and setting the resulting property to some entity. The property type of the property to be set will be known well in advance, but the specific property would have to be computed at the time. In practice, a property that knows it own type, such as '6 feet tall' would have to be constructed just before it was set from the number 6 that was just calculated and the property type 'height'. >> The Property class is completely featureless, with no members of >> any sort, so creating new properties is trivial. > > I am missing something here. How can a Property be featureless if > there seem to be different flavors (i.e., there is a "new" > property)? There has to be something to distinguish, say, a Spell > property from a Sword property. Using the terminology that I originally used, a Property had only its identity and that was all one could use. Now I would call such a thing a PropertyType. I had considered things like 'height' and 'age' to be properties of people, but of course it is only specific heights and ages which are properties, while the abstract concept of height or age is merely a property category containing actual properties like '6 feet tall' and '50 years old'. A property type needs no attributes because its only purpose is to distinguish '6 feet tall' from '6 years old' and to act as a way to access a value like 6 from an entity as if using a key to access a value from a hash table. I distinguish a Spell property from a Sword property by using the identity of the corresponding property type. The property type is not connect to the property once the property has been extracted from the entity, but I can still easily keep track of what sort of property it is by variable names, comments and execution tracing. For example: int ageInstance = elderlyPerson.get(agePropertyType) int heightInstance = elderlyPerson.get(heightPropertyType) ageInstance and heightInstance are just numbers with no way to distinguish what sort of properties they represent except lexically by looking at which property type was used to 'get' them. I am not entirely certain that I have correctly interpretted your advice, but I will continue to think about it. > R1 is just a relationship that restricts what properties a > particular entity has. Whoever uses the getter necessarily has a > context where collaborating with the property is important. The > R1 collection class now owns the responsibilities for ensuring > correct access (responding NO_SUCH_PROPERTY) and locating > particular properties. > > However, I would point out that the client of the getter is > someone who needs to collaborate with a specific Property, not the > Entity itself. So what one really has is: > > [Client] > | 1 > | > | R2 > | > | accesses properties of > | * > [Entity] > | 1 > | > | R1 > | > | possesses > | * > [Property] > > The collaboration path is really R2 -> R1 where [Entity] is > basically just a placeholder node on the path. Thus the semantics > of [Entity] is not relevant to whatever the Client wants to do > with the Property. > > Your application will generally be more robust if you make > relationship implementation and navigation orthogonal to the > actual collaborations needed to solve the problem in hand. To see > this consider what abstract action language (AAL) code in a UML > OOA model would look like for the [ |