Bad design? So what's the solution?

Pages: 123
TheIdeasMan wrote:
Are you part of an Open Source project?
It is my project, and it is open source. I haven't pushed the first version to gh yet because I'm still deciding on a license. In other words, I'm the project manager. It's a small project but I'm using it to gain experience in a lot of areas of C++ rather than actual project management/team-member experience, which is harder to obtain.

TheIdeasMan wrote:
The interactions are the interface to the classes. They are not subclassed because they take pointers to the base class.
What I mean is, it seems pretty useless to make a Bee and Flower class that inherit from a LivingThing class and then have the interaction code for Flowers and Bees in the LivingThing class. You may as well have the same class to represent Bees and Flowers and just have them remember what kind they are - in other words it seems to circumvent/deprecate both OOP and RTTI.

It took me a moment to understand your CUnits example. By Extending is with e.g. classes Inches, Feet, Pounds, Tons, Miles, Quarts, Celsius, Fahrenheit, etc. I could see how that *might* make sense, but what if you wanted To/From English and To/From MyNewUnit and To/From ThatGuysUnits? Would you not then have to adjust CUnits? At that point, if you called one of the new functions on an instance from a plugin that was compiled before the new functions were added, would that not cause problems?

Basically, I'm trying to make it so that interactions between classes happen as close to the class types in question as possible. One problem I can't easily solve is cross-plugin interaction - that is, a relationship that cannot be solved via having sub-plugins or the like. I think in this case I will have to rely on the One Definition Rule and have the main game detect when two plugins have mismatched versions. As long as the classes each plugin rely on have the same compatibility version number, the plugins should be able to load even if one was updated without the other caring.

I appreciate your arguments, they get me to face problems that I haven't yet but that i would eventually run into later. I've solved tiny majority now, the rest should be the easy part - rendering (oh the pain! The agony!).

What I mean is, it seems pretty useless to make a Bee and Flower class that inherit from a LivingThing class and then have the interaction code for Flowers and Bees in the LivingThing class.


The Pollinate code is in the Bee class. The Living class has a virtual function declaration of PollinateMe. The Animal class has a PollinateMe function that returns false. Flowers have their specific PollinateMe functions in their own class.

Just wondering whether this is a trade off - RTTI or casting, versus virtual function declarations in a base class, with virtual function definition where needed.

With the Units conversion tree, I had the strategy of always converting (by calculation) from the original unit to an intermediate unit, then to the target unit. For example, to convert from yards to feet, I convert from yards to metres (always metres, no matter what the original was), then from Metres to Feet. It seems strange at first - why not just divide by 3.0? The answer is that, if there are n original units (metric say) & n target units (imperial) and I want to be able to convert between any of them, I now only need 2(n-1) conversion functions, as opposed to n squared conversion functions.

I should also point out that next level in the Units tree would have CDistance, CAngle, CArea, CVolume etc. Then after that there is specific units classes like metres, feet, yards,mm, cm etc. It is at this level the conversion functions exist.

And, all values used by the code are forced to go through this conversion process - there is no where I use a double as an argument to any function. Values used by the code everywhere are always the metric base units like metres, radians, Sqmetres etc. I don't even keep the original value, it's easier not to keep track of it, and convert it back again on output.

Once input conversion is done, there is no need to do any more conversion, until output conversion is needed.

So for this question:

...... but what if you wanted To/From English and To/From MyNewUnit and To/From ThatGuysUnits? Would you not then have to adjust CUnits?


No, because the interface always only needs those 2 functions. And a pointer to CFeet is a valid pointer to CUnits. The virtual functions to convert from Feet to Metres and vice versa are in the Feet Class. Maybe the example is too simplistic to compare to what you are doing, because it only needs 2 functions in the interface.

At that point, if you called one of the new functions on an instance from a plugin that was compiled before the new functions were added, would that not cause problems?


As long as any Units plugin extends the Units tree it should work fine, because the actual conversion functions are in the classes that are added, the interface remains the same.

What did you think of my analogy between your code and the Unix System example?

And my Mediator design proposal?

Basically, I'm trying to make it so that interactions between classes happen as close to the class types in question as possible.


Well you could have the virtual function definitions much lower down the tree, but you would have to redefine more (all?)of them because they aren't being inherited.

With the idea of having to have an AcquireMe function (that returns false because that is breaking the rules) in the Monster or Enemy class, I am thinking there may be some other funky / clever combination of Design Patterns that might improve this. I did think about putting the Actions into the class that uses them (instead of an Action tree), but the Execute function in Mediator needs to be declared with a pointer to Action. Might need to ruminate on that a bit more.

I appreciate your arguments, they get me to face problems that I haven't yet but that i would eventually run into later. I've solved tiny majority now, the rest should be the easy part - rendering (oh the pain! The agony!).


It is good for me too, most of my stuff is mathematical - converting a formula to code is not that hard generally. Other things I am designing involve storing 5 million 3d points, but still be able to carry out operations on any subset of those.

Anyway it's 3.15am again here in Melbourne, Australia, so I will sign off for now. Cheers




TheIdeasMan wrote:
The Pollinate code is in the Bee class. The Living class has a virtual function declaration of PollinateMe. The Animal class has a PollinateMe function that returns false. Flowers have their specific PollinateMe functions in their own class.
Where the implementation is doesn't matter. The interaction (in your solution) occurs at the LivingThing level instead of directly between bees and flowers.
TheIdeasMan wrote:
No, because the interface always only needs those 2 functions.
CUnit only needs methods to convert to/from metric to be able to convert to/from english, myunits, and thatguysunits at the CUnit level?
TheIdeasMan wrote:
As long as any Units plugin extends the Units tree it should work fine, because the actual conversion functions are in the classes that are added, the interface remains the same.
Eh, I hadn't intended that to be about the CUnit example. See my previous Quote&Response.
TheIdeasMan wrote:
What did you think of my analogy between your code and the Unix System example?
You made it sound like the users had different public interfaces from each other, which didn't make sense to me.
TheIdeasMan wrote:
And my Mediator design proposal?
I don't see how the Mediator design pattern works without knowing about all existing classes and their interactions.
TheIdeasMan wrote:
Well you could have the virtual function definitions much lower down the tree, but you would have to redefine more (all?)of them because they aren't being inherited.
I'm not sure what you mean by this...?
TheIdeasMan wrote:
With the idea of having to have an AcquireMe function (that returns false because that is breaking the rules) in the Monster or Enemy class, I am thinking there may be some other funky / clever combination of Design Patterns that might improve this.
In my case, players in "creative" or "sandbox" mode would be able to "acquire" e.g. a monster.
Last edited on
CUnit only needs methods to convert to/from metric to be able to convert to/from english, myunits, and thatguysunits at the CUnit level?


Yes, because of these:

Two times conversion.
For example, to convert from yards to feet, I convert from yards to metres (always metres, no matter what the original was), then from Metres to Feet.


And a pointer to CFeet is a valid pointer to CUnits.


This works because of the VTABLE.

..... because the actual conversion functions are in the classes that are added, the interface remains the same.


The function declaration:
1
2
//pure virtual - doesn't make for Units not to have their own conversion functions
virtual CUnits* ToMetric(CUnits* Imperial) = 0 ;


The function call:
1
2
3
4
5
6
7
8
//must be a dynamically created pointers using new
//an object in Metres units to put the answer into
CMetres *pMyHeightMt = new CMetres;
 //an object in Feet units
CFeet *pMyHeightFt = new CFeet(6.0);

//The actual function call
pMyHeightMt = ToMetric(pMyHeightFt); //pMyHeightFt is a valid CUnits pointer even though it is a CFeet type 


At one stage I was going to overload ToMetric:

1
2
3
4
5
CMetres* ToMetric(CFeet*);
CMetres* ToMetric(CYards*);
CMetres* ToMetric(CMiles*);
CMetres* ToMetric(CInches*);
//similar for all the other distance units 


But then I found out about the VTABLE, and how to take advantage of it. I discovered that I could just have declare 1 virtual function with pointers to the base class.

It is exactly the same with my Bee example. The vector is of pointer to Living things, but it is loaded with pointers to Bear, Tiger, HoneyBee and BumbleBee. The Pollinate function is also declared to take a LivingThing pointer parameter and quite happily deals with these various types as arguments, and calls the right function.

I am trying to find the link where I read about it. This isn't the one I read originally - but it will do.

Article wrote:
One of the key features of derived classes is that a pointer to a derived class is type-compatible with a pointer to its base class.


Funnily enough it is found here: LOL :D
http://www.cplusplus.com/doc/tutorial/polymorphism/

This is a VERY handy feature. It saves you from RTTI, casting, function overloading, and other kinds of complexity. I have mentioned this several times - just wondering whether you do understand this now?


I don't see how the Mediator design pattern works without knowing about all existing classes and their interactions.


For the same reason as quoted in the article.

I'm not sure what you mean by this...?


Instead of having PollinateMe declared in LivingThing and defined in Animal, you could have PollinateMe defined in each Animal. But this is crazy (my alternative idea) because you should make use of inheritance, not defy it.

Maybe it is the names of the functions that provide some level of confusion. You could have a virtual function bool IsFlower in LivingThing, with redefinitions in Animal & Flower. But that is exactly the same thing, with a different name.

You made it sound like the users had different public interfaces from each other, which didn't make sense to me.


OK, maybe you had 1 UserGroupManager that dealt with all the users and all the groups - hey presto a Mediator Pattern!!

However, in your Bee example, You had BeeManager, FlowerManger, RockManager and the Tick function is overloaded to do disparate things (Display, Wear to the rock, Pollinate). Presumably there would be a AnimalManager as well.

This translates to UserManager & GroupManager in the Unix example, or PlayerManager & EnemyManager & TileSurfaceTypeManager & ResourceManager plus overloaded functions for the Actions in the Game example.

With my use of the virtual functions, function overloading is avoided.

In my case, players in "creative" or "sandbox" mode would be able to "acquire" e.g. a monster.


Ok, it depends what the rules are. I meant Acquire to be for resources such as Medipack or Ammo, you could have CreateMinion to domesticate a monster.

We haven't had much input from other senior members on this site, I will have a go at asking these questions on LinuxQuestions, if that is all right? Do you mind if I point them to this thread?
TheIdeasMan wrote:
This works because of the VTABLE.
You keep saying this like I have no idea what it is. I know very well what it is and how it works. I see how your example works now, but it doesn't apply to my scenario.
TheIdeasMan wrote:
It is exactly the same with my Bee example.
I'm not sure about your example, but it sure doesn't apply to mine. And you also continually talk to me as if I have no idea what polymorphism is - I do, and it bothers me that you keep treating me this way.
TheIdeasMan wrote:
With my use of the virtual functions, function overloading is avoided.
The methods are virtual. At lower and lower derivation levels the Tick(Thing) and Tick(Bee) and Tick(Flower) are called appropriately. The reason they are overloaded like this is to give access to the correct manager without casting and to solve problems with multiple inheritance. I guess my example was far too short for you to extrapolate the concept correctly - sorry for that, I just haven't had time because of school.

TheIdeasMan wrote:
We haven't had much input from other senior members on this site, I will have a go at asking these questions on LinuxQuestions, if that is all right? Do you mind if I point them to this thread?
Because most senior members already recognize that I solved the problem and require no further assistance. It's just you and me discussing this because neither of us understand what the other means.
Last edited on
Ok, I was trying to avoid over explaining things to someone who knows much more than me - I mentioned that a couple of times, and you said you appreciated my comments. But your replies weren't telling me you understood the same way as me. I understood your scenario, I guess we just disagree on which is better.

So, truce if you like. Thanks for spending all this time explaining your side, and really good luck with your project, and your studies.

Cheers.
Topic archived. No new replies allowed.
Pages: 123