From: <red...@pr...> - 2003-11-08 16:36:33
|
This is the second email ----- Forwarded message from red...@pr... ----- Date: Thu, 30 Oct 2003 23:49:44 +0000 From: red...@pr... Reply-To: red...@pr... Subject: Xenocide To: ma...@pr... Hi Vlad, >>I have reviewed the archives you sent to me and my comments are below. I >>also attached the very first implemetation of UI framework together with its >>project file, so you can compile it if you will. Please review it and send >>to me your comments. Then, I will post it to the list as well. Pay attention >>to hierarchy, class and package names, please. >>I pretty much guessed your hierarcy, but my 'ui' directory is immediately >>under 'xenocide' where you have it under 'xenocide/client'. I guess you are >>thinking to have more than just UI there, like network communication for >>example. I can move 'ui' directory under the 'client' and adjust package >>names to include Client in there too. You guess it almost flawlessly, I had separated client because we already have a server directory, so a client one was the better approach to get something uniform. >>Datainterfaces and its subdirectories. >>I notice that namespace for all interfaces is Xenocide::Interfaces. It could >>be fine not to separate them by behavior or object, but I think we should >>make a namespace Xenocide::DataInterfaces to distinguish from other >>(network???) kind. Noted and modified I think that is a very interesting idea to separate them, from other type of interfaces. >>Content of datainterfaces\behavior. >>I don't think that behavioral interfaces should have any data members. For >>example, size is not a property for IStorable object. All data attributes >>must belong to Object-kind interfaces. Also, I am not sure whether there >>should be any productID members, as these are attributes of object, but not >>behavior. About behaivioral interfaces (in general, IStorable excluded), I dont see a reason to impose that big constrain on behaivioral interfaces, cause most of the times you can use certain data methods to define the semantics of the class. That is something that you cant do in Java, Eiffel have the best method to do it via assertions, preconditions, postconditions and class invariants. C++ do not offer that kind of facilities, so partial implementation via static methods calling virtual internal methods is the less invasive approach (You can see the NetworkLib's Connection object for an example) to fix the semantics. However for that approach to work you still need the data members defined. About the IStorable object, it is an old interface (I made it even before Alpha 1) I had just retrofited it and didnt revisited it (yet) I wanted to know exactly what you think about it. My reason at the time was to define a simple interface for every storable object. If you have to look for certain object you need its ID or something to know what it is. Because that interface was supposed to be the first in the inheritance tree for objects. Maybe a solution could be to put it a better name and move it to objects directory. >>IStore is not a behaviour, but rather ability. It probably should be put in >>separate directory, where other similar interface would go (ILabaratory, >>IWorkshop perhaps). I like the idea of separating between objects/abilities/behaivior, and definitly IStore is an ability. You can store products or you dont. Better name for it or is it OK??? >>Next, I do not think that interface should inherit from each other. In your >>example, IWearable is a child of IStorable. Again, interfaces in this case >>describe behavior. Thing, you can wear is not necessarily could be stored >>(although, it is not true in UFO probably). The implementing object must >>inherit from both interfaces if it exposes behavior of both. Certainly we will need to find a better name for the IStorable case or a better class hierarchy... About the inheritance with interfaces, I do not see any reason why we couldnt describe similar (or extended) behaiviors with interface inheritance... For instance you can do it in Java too, and if we keep the same kind of style like NEVER mix interface inheritance with implementation inheritance there shouldnt be any problem. I can think of a very interesting example... For instance in UFO you have Elerium, Laser Pistols, etc. All of them can be stored in a Storage room, be it an Aircraft, General Stores, etc. And for instance some of them can be STORED in the players backpacks. But to avoid the common pitfall of UFO (have a lot of useless items when arming your soldiers in a base attack), you create a sub interface that is descendant from the one that define storable objects that can be carriable by the soldier (the most correct solution). If not you can have non storable objects that can be carriable, something that is absurd in every simulation game. >>IAircraft seemed OK to me. I only want to suggest to add another set of >>methods to get/receive the number of HWP the aircraft can carry, as those >>are different from units. Yes you are right, but we will have to think how to accomplish that cause the more I think about transport aircraft the most I think the interface is not quite right, for instance how do you check you have enough space. Do we impose a limit on HWP carry or we use the weight/size/units space combination? There are lots of important interface decitions to make there that are quite gameplay oriented. What do you think? >>As for the Soldier issue you mentioned there, then >>I am not quite sure about the problem. Could you explain it further? Soldier inheritance tree originally was like this: Personnel was the base X-Corp class Soldier inherits from Personnel Scientist inherits from Personnel Engenieer inherits from Personnel However if we want the aircraft to be used by both Aliens and Humans then we must modify the inheritance tree and find a new better abstraction. That's why I took it out from the code I sent you. >>IWeapon. I think that there is very little difference between >>isValidWeaponAmmunition() and acceptAmmunition() methods. Do you? There is a very subtle but important difference between them, If I would have implemented the class you would have seen it without a problem. isValidWeaponAmmunition() is an static linked method (note, not dynamic) that states the semantics of the validation method, which means make sure that the ammunition clip is valid for the weapon. On the other side, acceptAmmunition is the only method you override to cause the isValidWeaponAmmunition know of subclasses, and with the added value that you have the check needed to for the loadAmmunitionClip method too. It is exactly the same as the internalMethods from the NetworkLib connection. --------- (SORT OF) ACTUAL CODE ---------- class HeavyPlasma { protected: virtual Bool acceptAmmunition (IAmmunitionClip* ammunition) { if (dynamic_cast<HeavyPlasmaClip*> (ammunition) != NULL) return true; else return false; }; }; class RocketLauncher { protected: virtual Bool acceptAmmunition (IAmmunitionClip* ammunition) { if (dynamic_cast<SmallRocket*> (ammunition) != NULL) return true; else if (dynamic_cast<LargeRocket*> (ammunition) != NULL) return true; else if (dynamic_cast<IncendiaryRocket*> (ammunition) != NULL) return true; else return false; }; } OR class RocketLauncher { protected: virtual Bool acceptAmmunition (IAmmunitionClip* ammunition) { if (dynamic_cast<IRocket*> (ammunition) != NULL) return true; else return false; }; } ------------------------------------------ Then you delegate the actual decition whether the ammo clip is valid to that method. With the plus that you can have for instance and AdvanceRocketLauncher that shoots only EMPRockets or all type of available rockets (using either the first of second version). >>Also, I would not use IWeapon for all kind of weapons. I think, that there should be >>an interface like IFireable for all weapons which can fire. In this case >>there would be any problems with non-fireable weapons like stun rod, etc. >>IAmmunitionClip. No comments on that one. I had though about it in the past, however if you think it this way: A Stunt Rod is indeed a fireable weapon, just only that is a Zero Range weapon. After all you have to trigger a button to fire the electric charge :D ... In that way we have two solutions either have IFireable, IRangeFireable (that inherit from IFireable) or we put the hierarchy upside down and make the range an attribute. I had found that the second way gives you a better design (not as flexible but easier to understand and implement) after explaining to a friend how to model a tanks simulator for his OOP class. For instance you have Tanks, and some tanks have only radio equipment but others have fixed turrets (assault tank)... Then an advance tank have a movable turret (heavy assault tank). Then the property of movable should be via inheritance or fixed turrets are not more than a specialized class of turret with a harder constrain (responding I cannot move in that direction, if we wanna aim we have to move the tank :D ). >>IMountableWeapon. Again, I don't think we should inherit an interface from >>interface. Especially here: Mountable is description of behavior and Weapon >>is an object. I suggest another interface Imountable. Wrong name, changed to IAircraftWeapon and added IVehicleWeapon for HWP. >>In general, this is all great. It finally start to feel like there is some >>gameplay present. And this is what I love! I do like this part too as we can feel nearer of a playable game, but the infrastructure work is as important as this if we want to make a mantenible/extendable game. On the other hand, it is cool we can make a windowed (not graphic) client to test all this :D ... like see what is in the stores and all that... BTW as we are getting a little more involved in the design (that I didnt expect that with the first mail) I will be sending this to Ermete too, and I will send him your mail too so we add their comments on this before release it to the mailing list. Greetings Federico ----- End forwarded message ----- |