|
Prev: Name of construct
Next: Deriving - .NET example
From: H. S. Lahman on 12 Sep 2006 16:26 Responding to Guild... >>>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. > > > I am also ensuring that they really aren't accessing those > properties. You are right that they are not accessing the properties, > but it is sometimes nice to be able to make the stronger claim that > they cannot access the properties. So if something goes wrong and it > is connected to some group of properties, I will only ever need to > check those places that can access those properties. But why is the stronger claim nice? It only has value if you know that something rude /will/ happen if the wrong properties are accessed in the current context. If that is the case, then I would advocate using a "hard" mechanism that forces the maintainer to work at breaking things. (Better yet, which will raise an exception when the developer does try to break things to make sure everyone knows.) As far as looking at just those places, you will be search for the getter/setter anyway. (Hopefully each interface that does access the attribute will employ the same name for the accessor.) >>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. > > > I agree, but here is the situation: Each entity has behaviours that > either generate events (for animate entities) or react to events (for > inanimate entities). Those behaviours are based entirely on the > specification of the entity and there is no real pattern of which > properties are modified and which are merely accessed. The > specification of the problem essentially demands that the entities > vary wildly in behaviour. > > The behaviours will be implemented as strategies inside the entities. > There will be at least two classes of strategies, one for animate > entities and one for inanimate entities. The "inside the entities" worries me. If you are using the GoF Strategy pattern, the the Strategy object is a peer of the Context object, not part of its implementation. That concern aside, using the Strategy pattern seems like a good way to get around the too-many-clients problem above. Assuming the [Strategy] represents a generic behavior that produces different results based upon attribute data values for the Entity in hand, then one has only a few [Strategy] objects that act as clients for accessing attributes. To use the example from the other post, suppose you have a combat system that uses a generic algorithm based on hit points per round. You would only need one instance of that combat Strategy. Its results would depend on the attribute values of the relevant combatant Entities. So if you are debugging a problem related to, say, a cumulativeDamage attribute that seems to be letting Swordsmen kill Abrams Tanks, you only have one place to look no matter who the relevant Entities are. > Each main strategy class will have very many subclasses to implement > all the different behaviours and the factories will assign the > appropriate behaviour to the appropriate entities. These behaviour > strategy classes represent the bulk of the domain and they will each > have access to exactly those properties that they need, and > potentially to all the properties. OK, the first sentence seems like a problem. This sounds like every Entity and context result in a unique strategy. I would spend some time trying to identify the fundamental activities in the game (moving, building, fighting, trading, whatever). I would hope to prune that list to a small number to make the simulation viable. [I spent a decade doing economic simulations. Simple is definitely better. B-)] Then for each activity in the list I would look for the fundamental invariants of the activity and try to come up with a single generic algorithm to execute them. (One may not be possible if you have, say, land, sea, and air combat; but certainly just a few algorithms.) Then all you have to do is find a way to express Entity and context differences in terms of data values that the generic algorithm eats. Bottom line: if you really do have "very many" subclasses for different behaviors, I think that is symptomatic of a problem more basic than classes, properties, and relationships. I think it is time to rethink the basic way the game will work. [FWIW, back when I was doing simulations I found that complexity was the driving factor. Whenever I encountered substantial complexity in some aspect of the simulation I would treat it as a symptom that I hadn't grasped the fundamentals yet. I would then go into Navel Contemplation Mode and try to come up with a simpler view of what was happening. Invariably my best simulations were those that were simplest. It just took awhile to get there.] > The strategies will also likely be built using the Composite pattern > to create complicated strategies out of simple ones. Isn't that something that would be more relevant to the AI for NPCs? I would hope that the individual collaborations between game entities during execution would be pretty narrowly defined. >>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? > > > It is a lot of objects from a lot of classes, though all the objects > will also be from just a few abstract 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. > > > I believe I am in t
From: Brendan Guild on 12 Sep 2006 16:40 H. S. Lahman wrote in news:dHCNg.32528$Qb2.11396(a)trnddc08: > Responding to Guild... > > Maintenance doesn't count. We use the OO paradigm precisely > because we anticipate as yet undefined future change and want to > make it easy to accommodate it when we get there. IOW, the OO > paradigm takes care of that already. > > My pushback is that the domain _as you know it_ probably does > allow categorization. You are surely correct that the domain allows categorization if I ignore maintenance. There are simply a finite number of distinct entities and there is nothing preventing me form working out a way to categorize them into classes. But I am shocked that you would deliberately ignore known maintenance issues in your design and simply trust in your OO design to save you. I find it very hard to believe that maintenance can be safely ignored. > The issue is not the values of the attributes (though differences > in data domains may matter). It is whether all entities have > exactly the same attributes. For example, does a Wizard just cast > spells or does it also engage in offensive physical combat? Can > all NPCs engage in combat or do some always flee? Do some NPCs > always engage in combat but never communicate for trading, etc.? > Do some NPCs have unique physical characteristics that are > important to the game (e.g., predators with a keen sense of > smell)? Even if I can make claims using words like 'always' and 'never', I seriously doubt that I should use them in my design. If I were to actually build into my design that something could never happen, then how hard will it be for the maintainer to make it happen? You say that I should ignore maintenance, but I can only do that if I can convince myself that I will not be able to make design decision that will make maintenance difficult. Can all OO designs be maintained equally well? >> 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. > > Again, maintenance is not relevant. The basic thesis of OO > development is that one solves the problem in hand, not problems > that might be in the future. One depends upon the paradigm to > ensure the application is maintainable. But are you forced to depend upon the paradigm? I choose not to depend upon it because I can think of designs that would be crippling during maintenance. It is only thanks to my looking ahead that I choose to avoid those designs. > However, I have to think that your entities do have inherently > different behaviors. Delegating those behaviors via the Strategy > pattern is one way to deal with that. I am fine with that because > the categorization I am seeking is still there. It has just been > moved from Entity to Strategy. I do not think that the difference should be understated. From my perspective, the difference is huge. In the simulated world it is entities that have relationships. A troll picks up an entity to do the needed smashing, not a strategy. The strategy within that entity could be anything. I can create as complicated a subclassing tree as I choose and it won't interfere with physical relationships in the slightest. On the other hand, if I were to make a Weapon subclass of Entity and make it so that only Weapons could be used as weapons, then I am forcing the maintainer to make a decision that he might not want to make. > Nonetheless, I am concerned that different behaviors may also > require different parametric data. One can abstract all combat > into a single generic algorithm (say, hit points per round) and > then characterize an arbitrary variety of combatants with > differing values for a handful of knowledge attributes. But would > those same attributes be relevant to trading goods, building > things, or moving from one location to another? Probably not. So > the next question is: do all <animate> entities have the same > capabilities to perform game actions (move, trade, build, fight, > etc.)? If not, some will have attributes others don't have. Of course, the answer is no. Not all entities have the same capabilities and many properties will entirely useless for a entity without a capability from a certain set. The only reason I do not want to hard-wire a restriction such as 'Entity A can not do activity B' is because I know that I do not know what entities might need to do in maintenance, and I expect that entities will need to do the unexpected. > If you can honestly answer Yes to my last question above AND you > have defined generic algorithms for each of those activities whose > results differ based solely on input knowledge attribute values, > then you have my full blessing. The CIV series pioneered the use > of invariant algorithms and parametric data but even then there > were distinct classes for settlers, workers, and combat units. CIV is a strategy game, along the same lines a Chess, though much more complicated in its own way. If I were making a Chess emulator, I would not be concerned about making classes for pawns, knights, rooks, etc. Even if some maintainer were to get the crazy idea of adding a new piece to the board, it would be just one new class and not unacceptably difficult, since the relationships between Chess pieces are rather simple. There would be no relationship diagram in which the new piece must play the role of a rook (and be a subclass of Rook) in order to have the desired behaviour. CIV units do not use each other in various ways: as weapons, as clothing, as food, as sources of light, as projectiles, etc. Such relationships would create strict rules about what objects can play what roles. CIV does not have those mostly because it is a strategy game, not an adventure game. The rules for a strategy game must be carved in stone and they must be simple enough for the player to mostly keep in mind all at once. In an adventure game having simple rules and clear categories is actually a bad thing. >> 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. > > Actually, you can do it with single inheritan
From: H. S. Lahman on 12 Sep 2006 17:06 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. Yes, instantiation essentially means creation. But relationship instantiation is different than object instantiation. To instantiate the R1 relationship, one must have the two participating objects in hand. The generalization in this case is the R2 relation. The relation here is more like a type relation in that it describes how an object is created. When one instantiates R2 one is really instantiating a single object whose properties are resolved by the rules of inheritance (i.e., a union of all properties of classes in a direct line of descent from the root superclass to a leaf subclass). > 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. <aside for clarification> There are three aspects to relationships: implementation, instantiation, and navigation. There are basically three ways to implement a relationship: (A) a referential attribute like a pointer; (B) an explicit identity attribute, as in RDBs, combined with a search; and (C) passing an object reference as an argument to another object's method (that isn't a referential attribute setter). One instantiates the relationship by, respectively: (A) assigning an object reference to the referential attribute; (B) assigning an object identifier to the referential attribute; or (C) providing the right object reference as an argument. One navigates the relationship by "reading" the relationship to get a reference and sending a message to that address. The (B) implementation is rarely seen in OO applications because it it inherently inefficient compared to the others in a memory resident application. The (C) implementation is generally frowned upon because it requires the caller to know what object the receiver needs to collaborate with. Essentially one has: 1 R1 1 1 R2 * [A] -------------- [B] --------------- [C] When A passes a reference to C, A is essentially instantiating the R2 relationship temporarily. But the R2 relationship is a personal matter between [B] and [C]. The [A] object should not even know that relationship exists, much less who the participants should be in this collaboration context. If you recall, I said a few messages ago that one should encapsulate the rules an policies of instantiation away from the rules and policies of collaboration. A needs to collaborate with B, which is does by invoking B's method. But A doesn't need to know about instantiating R2 to do that. </aside for clarification> So... > 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. Not quite. Exactly the same protocol will be defined in [Deconstructor] as is provided by Serializer. The ConcreteX methods will implement that protocol exactly like the Serializer stubs filled in for each ObjectX. The problem is that the single ConcreteA method applies to /every/ instance of ObjectA. In Serializer that was taken care of by the implicit 'this' pointer. But when we delegate [Deconstructor] to a peer object the R1 relationship is *:1 and we have to instantiate it to write a particular ObjectA out. ************* There is nothing wrong with me that could not be cured by a capful of Drano. H. S. Lahman hsl(a)pathfindermda.com Pathfinder Solutions http://www.pathfindermda.com blog: http://pathfinderpeople.blogs.com/hslahman "Model-Based Translation: The Next Step in Agile Development". Email info(a)pathfindermda.com for your copy. Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php. (888)OOA-PATH
From: Brendan Guild on 12 Sep 2006 23:34 H. S. Lahman wrote in news:IJENg.3495$xh3.394(a)trnddc01: > Responding to Guild... > >> I am also ensuring that they really aren't accessing those >> properties. You are right that they are not accessing the >> properties, but it is sometimes nice to be able to make the >> stronger claim that they cannot access the properties. So if >> something goes wrong and it is connected to some group of >> properties, I will only ever need to check those places that can >> access those properties. > > But why is the stronger claim nice? It only has value if you know > that something rude /will/ happen if the wrong properties are > accessed in the current context. If that is the case, then I > would advocate using a "hard" mechanism that forces the maintainer > to work at breaking things. (Better yet, which will raise an > exception when the developer does try to break things to make sure > everyone knows.) I cannot predict what sort of bugs might result from incorrectly accessing properties, but accessing one property when another is intended will surely result in something bad. The mechanism that I have in mind is one that you would call soft because it is intended to be easy to allow access to any particular property, but it serves the purpose of restricting an object to accessing only certain properties. That restriction alone has a simplifying effect. The property type objects are peer objects of those objects that access properties and a property can only be accessed if relationships can be navigated to get to the required property type. Those relationships must be instantiated when the object is created and as long as those relationships are instantiated correctly, the correct properties will be accessed. This has an extra benefit because I can create variations in the objects from a single concrete class just by instantiating their relationships with property types differently. > The "inside the entities" worries me. If you are using the GoF > Strategy pattern, the Strategy object is a peer of the Context > object, not part of its implementation. You are correct. I was confused because the strategy is only used by the context, but the relationship is instantiated externally and the strategy might navigate the relationship backwards, so they are clearly peers. > That concern aside, using the Strategy pattern seems like a good > way to get around the too-many-clients problem above. Assuming > the [Strategy] represents a generic behavior that produces > different results based upon attribute data values for the Entity > in hand, then one has only a few [Strategy] objects that act as > clients for accessing attributes. It seems that we have different ideas about the purpose of the Strategy pattern! The Strategy class does not represent generic behaviour; it represents abstract behaviour with no actual behaviour implemented. The subclasses of Strategy implement the concrete behaviours and they are not generic either: each subclass implements a behaviour intended for a specific set of entities. I can see no benefit to the Strategy pattern if I am going to define just one generic behaviour that can apply to every entity. Strategy is intended to allow the behaviour vary between objects of the same class. The only way that Strategy allows me to make things more generic is by letting me vary the behaviour of an object along more than one dimension, and I certainly intend to do that. At the moment I can see no way to implement the behaviour of all the Entities with just one parameterized algorithm, unless you count a Strategy object as a parameter. Even worse, I fully expect to have to create a large number of strategies just to cover the range of behaviours that will be needed, and more strategies will be created in maintenance. >> Each main strategy class will have very many subclasses to >> implement all the different behaviours and the factories will >> assign the appropriate behaviour to the appropriate entities. >> These behaviour strategy classes represent the bulk of the domain >> and they will each have access to exactly those properties that >> they need, and potentially to all the properties. > > OK, the first sentence seems like a problem. This sounds like > every Entity and context result in a unique strategy. I would > spend some time trying to identify the fundamental activities in > the game (moving, building, fighting, trading, whatever). I would > hope to prune that list to a small number to make the simulation > viable. [I spent a decade doing economic simulations. Simple is > definitely better. B-)] I am not so sure that simple is better in this case. If I can see the simplicity, then the player might see it too. However, building complexity from simplicity would be the ideal way to go. That is why I intend to use composition to create more complicated strategies from simpler strategies, reducing the total number of classes needed and increasing the variety of objects that I have to work with. > Then for each activity in the list I would look for the > fundamental invariants of the activity and try to come up with a > single generic algorithm to execute them. (One may not be > possible if you have, say, land, sea, and air combat; but > certainly just a few algorithms.) Then all you have to do is find > a way to express Entity and context differences in terms of data > values that the generic algorithm eats. If I do that then that single generic algorithm would surely be hard- wired in as the only algorithm for dealing with that activity. That works until I come to an entity whose behaviour cannot be expressed as parameters to that algorithm, then I have to rewrite the algorithm and the would surely have a cascade of horrors throughout my design. I know that I should ignore maintenance in my design, but when I see a disaster like that coming, why not try to avoid it? > Bottom line: if you really do have "very many" subclasses for > different behaviors, I think that is symptomatic of a problem more > basic than classes, properties, and relationships. I think it is > time to rethink the basic way the game will work. I will do my best to keep the number of classes down, but when the entities are required to have wildly varying behaviour by the specifications, then there is on
From: Dmitry A. Kazakov on 13 Sep 2006 08:49
On Tue, 12 Sep 2006 18:22:05 GMT, Brendan Guild wrote: > Dmitry A. Kazakov wrote in > news:3jty6lxzzk5o$.1swpqox9fs2vv$.dlg(a)40tude.net: > >> On Mon, 11 Sep 2006 17:45:37 GMT, Brendan Guild wrote: >> >>> 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. >> >> That's a strange decision. If the groups of similar properties in >> the domain aren't mapped onto groups of objects of same type, >> then? > > It depends on what you mean by similar. All properties represented by > numbers will be of the same type in the language, and so will all > properties represented by strings. Why? [ The issue of string representation is a different thing. You can have both different types and string representations for persistence, networking, debugging etc. ] > I do not think there is any > benefit to creating more detailed types than that. Benefits are huge: 1. OO programming. You can't define different method on the same type 2. Static type checks >>> 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. >> >> This "affect" is what? Under "know" I mean the following mechanics >> of "affect": >> >> 1. When Entity knows a Property => the current value of the >> Property is available to Entity => the behavior of Entity can be >> parametrized by this value. > > That is true for some entities with some properties, especially > animate entities. But each property type has different semantics and > is used in a different way and known about by different objects. = it is not single dispatch. When semantics sufficiently depends on both the type of Entity and the type Property that is equivalent to MD. >> 2. When Entity does not know it, then its behavior cannot depend >> on Property. As a consequence, you need some outsider, which would >> choose an appropriate behavior according to the current state of >> Property. This outsider could be either the Property (= Property >> knows Entity), which would be a delegation model, or else there >> must be someone third who puts things together. > > A property does not know about entities, but there is an outsider, > called 'the client' who chooses an appropriate behaviour for the > entities. The client works with some properties, entities work with > other properties, and different entities work with still other > properties. And how the outsider decides when it makes what? If the decision is based on the types of Outsider, Entity and Property, that is triple dispatch. If the decision is based additionally on the values of, that is a total ad-hoc mess. Surely, you could map everything to one type void*, but that won't help, it would make it only worse. The problem is not in types, but in lack of OOA/D, because the responsibilities aren't clear. >> I don't see any difference in the variants above. In both cases >> you set a value. The second variant returns not a property, it >> does an iterator, or a reference to the property to set. > > That is true, but the difference I was trying to point out is that > the first has value semantics while the second has reference > semantics. Right. But reference semantics is an implementation detail, which does not influence the problem. [ In some languages reference is always polymorphic and value is always specific, but these are local language problems. ] >>>> 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. > [snip] >>> 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. > [snip] >> It is always square, just some cells might be never reachable in >> any [correct] program. > > This needs more explanation. I can take your word for it that the > matrix is always square, but I can no longer picture the matrix. The > matrix I see has far more columns than rows because I have far more > property types than entity types. Oops, sorry, it is rectangular of course. > Do you mean that I cannot have more property types than entity types, > or perhaps that I simply do not have more property types than entity > types, even though I think so? My guess is that you did not describe > the matrix that you intended. No, forget it, it was just a typo error. I meant rectangular. It is square for multi-methods for dyadic operations (like +, * etc). -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de |