Skip to main content
GameDev.net gamedev.net
🔒 Locked

"Is a"/"Has a" debacle solved with semantics.

Started by rKallmeyer Mar 22, 2006 at 3:23 PM 17 replies 2.6k views
Original Post
rKallmeyer
rKallmeyer
WARNING-- This isn't ground breaking by any stretch of the imagination, just a different perspective on a common problem. (And if the Bold bugs you I apologize -- Sometimes its hard to get stuff read without making it easy for people ;) So I was getting ready to start developing some tools for an upcoming project, and it was time to work on the GUI. (This is not a discussion about reinventing the wheel). I like working on GUI's because there are so many implicit relationships between objects, I always feel like my Object-Oriented Muscles are getting flexed. Designing a class layout that is easy to work with is the number one concern, but right after that comes ease-of-implementation. Assume I'm going to need a pretty full featured GUI (Windows, Buttons, Listboxs, Textbox’s, Scrollbars, Images, ECT...). Any one who has ever worked with a GUI, designed their own GUI, or even remotely considered the design challenges of creating their own GUI, knows that many GUI objects have similar attributes, properties, methods, events and interfaces. For example, an Image will generally have a width, height, left, and right positions. And these positions can be defined in several ways (relative to screen, parent, pixels vs. percent, ECT...). But so does a Window, a Frame, a button, a Scroll bar, ECT... Another example, the application programmer using the GUI will certainly need some sort of event notification of when a button is pressed. But he might also want the same sort of notification when an image, a Window, or a checkbox is pressed. The point being that almost all GUI objects can be thought of as different combinations of common properties or interfaces. There are two general strategies to take advantage of this situation from the programmer's perspective. 1 -- Every class derives from base classes that contain specific properties. Usually, these objects would be called "Dimensions", "Font", ECT... This approach is nice because it allows for a high level of code re-use with strong logical separations. However, many purveyors of OOP have issues with this method because the "Is a..."/"Has a..." distinction. Specifically, Classes shouldn't derive from "Font" because inheritance is an "Is a..." relationship, when the correct relation ship here is "Has a...". This distinction leads to the second common solution: 2 -- Every class contains several base objects that are each responsible for a specific peace of functionality. This is generally known as the composite pattern. This approach is nice because the "Is a"/"Has a" logical relationship is maintained. The problem I find with this method lies in the implementation details. For example, now that a Textbox is no longer derived from Font, I must write wrapper methods for every piece of Font functionality I would like to expose through textbox; like SetFont(), SetFontHeight(), SetCharSpacing(), ECT... In addition, in the previous class layout, I could have stored a list of all objects that contain dimensions, so that I might move everything to the right by 5 pixels. Now, in order to do the same thing, I will need some other much more complex solution to do a relatively small task. So my solution is a solution of semantics. In order to escape the implementation struggles of the composite pattern, I use the "Is a..." relationship correctly by simply changing the name of "Font" to "ObjectWithFont". Now, a Textbox can derive from ObjectWithFont, ObjectWithDimensions, and ObjectWithFrame. All the code is reused, no cumbersome wrappers need be written, and a Textbox certainly "Is a" ObjectWithFont, an ObjectWithDimensions, and an ObjectWithFrame. Suddenly the distinction between "Is a..."/"Has a..." relationships, and choosing the right one for a particular class design -- appears to be a petty semantic qualm of over-zealous design gurus. Thoughts?
DrGUI
DrGUI
Hey

Interesting....

I was thinking though that an ObjectWithFont isn't a Font, so I don't think it makes sense to be able to cast it into a font, you're losing vital information and basically changing the whole nature of the object.

Instead of writing lots of wrapper functions I would just expose the Font with a 'get' accessor. In my mad mind that makes more sense because a text box has a font [grin]

>Peace out<
hplus0603
hplus0603
The problem with ObjectWithFont is that your base classes may want to, in turn, derive from some helper bases. At that point, you get one helper base per ObjectWith... base class you derive from. Also, what if you want a different font for your input control label than for the actual input area?

What I would do is to create a generic container object that just contains properties (say, named floats and strings). Then I'd write behaviors that can tack on to this object of properties, using some mapping between object properties, and behavior parameters.

Last, the property object should expose a query-interface function for each behavior. In C++, it'd probably look like:
class I_Object {public:  template< class T > InterfaceToType< T >::Type * getBehavior() {    return (InterfaceToType< T >::Type*)search_components( typeid(T).name() );  }  void * search_components( char const * name ) {    if( pos = std::find_if( components_.begin(), components_.end(), ComponentImplements( name ) ) == components_.end() ) return 0;    return (*pos)->interface( name );  }};

You'd define actual protocols either as the protocol class (class I_Font), or as specific named classes used simply as type tags (class LabelFont { public: typedef I_Font Type; }). I realize I haven't shown the implementation of "InterfaceToType<>" but it's fairly straightforward.

Thus, you could have a "FontBehavior" and a "ClientRectBehavior" and a "MouseTrackBehavior" and a "ButtonDrawingBehavior" which all have different responsibilities. If you want to change the "label font" properties you do something like:
I_Font * font = object->getBehavior< LabelFont >()->SetFontSize( 12 );

Of course, this would crash if you didn't actually have a label font, but you get the idea :-)

Each object would look up values (display width, content text, what have you) on the master container object, rather than mirroring the properties internally. That way, you don't need to update all the mirror values; just the master value (and then invalidate the dependents).
enum Bool { True, False, FileNotFound };
rKallmeyer
rKallmeyer
Thanks for the indepth reply

Quote:
Original post by hplus0603
The problem with ObjectWithFont is that your base classes may want to, in turn, derive from some helper bases. At that point, you get one helper base per ObjectWith... base class you derive from.


I'm not exactly sure what you mean here. You could still derive ObjectWithFont from as many base classes as you want -- And assuming they are deriving from common bases, you would use virtual inheritance to be sure the final object would have only one copy.

Quote:
Also, what if you want a different font for your input control label than for the actual input area?


I'd think the best case in this situation would be to use an class "ObjectWithFonts. And let it manage the entire font scheme for different areas - Assuming that you would want the font scheme to be similar across different elements.

Quote:
What I would do is to create a generic container object...


Thats a pretty cool layout. And although I don't like the interface for behaviors (it seems a bit unwieldy), I can see why it might be required for systems that need an extreme amount of flexibility -- or if you already using properties for scripting or some such.

About safety here, you mentioned that it can crash already -- are there any ways you have found to make a system like this not prone to run-time programmer mistakes such as that?
Washu
Washu
Lets take an abstract look at a common example of a GUI inheritance scheme that I see commonly.
Control Scheme UML

Now, we can clearly see that this is an "is-a" relationship. A window "is-a" control and an image "is-a" control as well. Presumably the control will expose various properties that are shared amongst the controls. Some of those properties probably won't be used by all controls, for instance an Image control might not require a Font object instance, but in general most controls will (TextBox, CheckBox, RadioButton, Window, Label, and most others can find a use for a Font property). So, we can compose the Control object with various other objects and expose them to our derived instances through properties...
Control Composition UML

The idea then is to minimize the depth of our inheritance tree, as a deep inheritance tree is a nightmare to maintain. Of course we shouldn't use composition everywhere, because not everything is a "has-a" relationship. Note however that a Window has a dual relationship, where it "has-a" control (actually, it has many controls), and "is-a" control as well. But this is an associative relationship for the most part.

This should also be combined with other methods of building our objects while maintaining flexability, for instance, using decorators to add functionality. Of course, decorators have their own problems, which often involves something expecting one type, but ending up with a decorated form of the type (which is a sibling tree of the control in the inheritance heirarchy, and hence not directly related) instead.

Dealing with messages also poses an interesting problem. Do we pass the message through the tree, letting each parent decide if it should drop down, or do we find the controls that the message could affect and then dispatch it directly to them, ignoring their relationship. A good example of the latter would be text being entered into a text box. Typically the parent isn't notified of the event, while the focus control is. On the other hand, the former is certainly simpler to implement from the architectural standpoint, as you do not have to deal with spacial relationships except to identify the parent tree to dispatch the message to (which window).

GUIs certainly are an interesting facet of programming, with many different methods of building and solving their various problems. While I would say that no two GUIs are exactly the same, most are very close to each other.
In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.
Emmanuel Deloget
Emmanuel Deloget
I understand your needs and your solution, but as I think to it, it seems to be rather limited - a change in the way the frame (this is just an example) works in your textbox will need a change in the frame class - or the creation of a new textbox class that don't inherit the existing textbox class (thus, you break the "is a" relation between two different kind of textbox.

I'm not in favor of your solution because it seems to me that you don't want to have a strong software design because of some kind of lazyness. I understand that writing code can be dumb, but this is a necessary step if you want to have a working program. If you want to limit code writing, you may try to use a code generator - if no code generator suits your need, maybe it is time to create one [smile]. IMHO, the implementation should obey to the design - not the opposite (after all, it is probably simpler to put everything in a god class - why don't we do it?).

I'm often suspicious when I see multiple inheritance in a design - I'm not totally dumb and I also know that multiple inheritance is a viable feature of the C++ language, but I tend to think that most of the time it is not used correctly. This is the feeling I have when I think to your solution: by using multiple inheritance, you create a static hierarchy of classes that will be - I think - more difficult to evolve.

I'm also often suspicious when one tries to subvert natural relastionship bewteen classes - for example, changing a "has a" to a "is a" to simplify the implementation. Of course, adding a "has a" relation to a class will need you to add a bunch of another functions in order to encapsulate the new bahavior, but is it really that bad? Especially in a GUI system, where all the controls will inherit a base Window class (thus, the functions will have to be written only once for all the control hierarchy).

Remember: lazyness leads to frustration, frustration leads to anger, anger leads to suffering. Or something like that [smile].

I also tend to dislike hplus0603's solution - mostly because there's a nasty void* here [smile] - and because this is not, IMHO, a correct way to agregate properties. Of course, it may be needed in (for example) a scripted system where the properties are stored in a script object, but it still break a lot if things (you quit the realm of type safety (which is one of the coolest C++ feature)).

Regards,
rKallmeyer
rKallmeyer
Quote:
Original post by Emmanuel Deloget
I understand your needs and your solution, but as I think to it, it seems to be rather limited - a change in the way the frame (this is just an example) works in your textbox will need a change in the frame class - or the creation of a new textbox class that don't inherit the existing textbox class (thus, you break the "is a" relation between two different kind of textbox.


I think you are forgeting the benifit of virtual functions. Using your example, lets say we have implimented the entire class hierarchy and decide we want a special snazzy text box that renders it's frame upsidedown. All we have to do is create a new text box class that derives from the first textbox, and let it override the virtual DrawFrame() method defined in ObjectWithFrame.

As an alternative, you can add a new property to the frame class -- maybe a boolean value called "Draw_UpsideDown". I fail to see the problem with changing the frame class, when you have decided you want new behavior from the frame.

Quote:
I'm not in favor of your solution because it seems to me that you don't want to have a strong software design because of some kind of lazyness. I understand that writing code can be dumb, but this is a necessary step if you want to have a working program.


I believe you are missinterpreting effecient coding for laziness here. My goal is to find a solution that makes sense, is easy to use, and doesn't take me and my team the next 3 months to impliment (We've got much more important things to worry about then writting wrapper methods -- like making games.)

Quote:
If you want to limit code writing, you may try to use a code generator - if no code generator suits your need, maybe it is time to create one [smile]. IMHO, the implementation should obey to the design - not the opposite (after all, it is probably simpler to put everything in a god class - why don't we do it?)


Forgive me but I personally think investing the time and resources into setting up, maintaining, and running a code generator for the specific purpose of "making the implimentation obey the design" and no practical or function benifit -- a complete waste of time. Especially in the case that an alternative solution exists that provides a logical and functional benifit to the implimentation and use.

Quote:
I'm often suspicious when I see multiple inheritance in a design - I'm not totally dumb and I also know that multiple inheritance is a viable feature of the C++ language, but I tend to think that most of the time it is not used correctly. This is the feeling I have when I think to your solution: by using multiple inheritance, you create a static hierarchy of classes that will be - I think - more difficult to evolve.


It is exactly this feeling (and you are not alone) that I think is an issue of semantics, or perception -- rather then fundemental problems with multiple inheritance or static design heirarchies. How is using multiple derived components any more or less static then using one? And when you try to think of examples, please be sure to point out an issue specific to multiple inheritance, and not a bad design issue in general.

I would argue that using multiple inheritance correcly is no more static then any other correcly implimented design pattern. There is a general problem with inheritance hierarchies getting to deep or to large in general, but that is a problem of complexity, and not a problem of multiple inheritance. You could just as easily run into the same problem with any other aspect of programming: you stretch the design to far, and your brain can't wrap around it as quick and easy, so you are much more likely to make mistakes that effect the design in ways you couldn't forsee.

Quote:
I'm also often suspicious when one tries to subvert natural relastionship bewteen classes - for example, changing a "has a" to a "is a" to simplify the implementation. Of course, adding a "has a" relation to a class will need you to add a bunch of another functions in order to encapsulate the new bahavior, but is it really that bad? Especially in a GUI system, where all the controls will inherit a base Window class (thus, the functions will have to be written only once for all the control hierarchy).


A textbox "Has a" Font -- but you could also look at it like a textbox "Is a" ObjectWithFont. Do you see that by changing the name, and the design focus of your base classes, you can achieve the correct "Is a" relationship?

rKallmeyer
rKallmeyer
Quote:
Original post by DrGUI
I was thinking though that an ObjectWithFont isn't a Font, so I don't think it makes sense to be able to cast it into a font, you're losing vital information and basically changing the whole nature of the object.

Instead of writing lots of wrapper functions I would just expose the Font with a 'get' accessor. In my mad mind that makes more sense because a text box has a font [grin]

I might not have been clear enough in the OP. When you change Font to ObjectWithFont, you must also change the design focus of the class to reflect that is now an object containing specific font related functionality.

For example, in the font class you might have a setName() method, where in the ObjectWithFont class you might have setFontName() method. It is a subtle but important difference.

I suppose its also important to note that you still might have a Font class that is contained by ObjectWithFont in a "Has a" relationship -- especially if your Font class does more then render the 2d text that your GUI needs.

As far as exposing font with a get accessor, I'm all for doing it in the ObjectWithFont class, but if you do that for textbox class, then you loose the polymorphic association between all objects that contain fonts. You can no longer hold a list of all objects that contain fonts and say scale the height by 2 or something similar.
Sneftel
Sneftel
Quote:
Original post by Nuget5555
A textbox "Has a" Font -- but you could also look at it like a textbox "Is a" ObjectWithFont. Do you see that by changing the name, and the design focus of your base classes, you can achieve the correct "Is a" relationship?

Is a car an ObjectWithAWheel?
rKallmeyer
rKallmeyer
Quote:
Original post by Sneftel
Quote:
Original post by Nuget5555
A textbox "Has a" Font -- but you could also look at it like a textbox "Is a" ObjectWithFont. Do you see that by changing the name, and the design focus of your base classes, you can achieve the correct "Is a" relationship?

Is a car an ObjectWithAWheel?


In this example no. A car is an ObjectWithWheels -- which would imply that there is a special relationship between the wheels that needs to be reflected in some sort of programming construct. (Like a suspension system or all-wheel drive).

This idea works with collections of objects just as easily as single objects. Be it a simple list of wheels, or a more complex case where each wheel interacts with one another in a specific way, you can convey this relationship quite easily.

Or did I misunderstand your five words of profound question-hood? ;)
Sneftel
Sneftel
Huh. Are both a car and a tricycle an ObjectWithWheels? For that matter, are both a Unicycle and a FerrisWheel an ObjectWithWheel? Is a BankAccount an ObjectWithInteger?

What I'm getting at here is that you're conflating type with usage. Calling something an ObjectWithFont is meaningless because you're not describing what it uses the font for. Maybe it's the font used to display text. Maybe it's a font being used solely for its kerning relationships. Maybe the object needs multiple fonts (like one font for a label, and another for items). Maybe the object is a ObjectWithFont, and inherits from a different object which also is an ObjectWithFont. Should those fonts be the same? Who knows!
rKallmeyer
rKallmeyer
Quote:
Original post by Sneftel
Huh. Are both a car and a tricycle an ObjectWithWheels? For that matter, are both a Unicycle and a FerrisWheel an ObjectWithWheel? Is a BankAccount an ObjectWithInteger?

What I'm getting at here is that you're conflating type with usage. Calling something an ObjectWithFont is meaningless because you're not describing what it uses the font for. Maybe it's the font used to display text. Maybe it's a font being used solely for its kerning relationships.


You are certainly correct. As the discussion was originally about GUIs, I spoke of ObjectWithFont as though it was ObjectWithFontUsedToRenderGUIText. Although the naming is already quite out of hand without the "UsedToRenderGUIText". Sparing the extra text, we can put the classes into namespaces to give the user at least a good idea what the object is for, and in the case that a car and a tricycle are in the same namespace, we can call one ObjectWith20InchCarWheels, and another ObjectWith3InchTricycle wheels.

However these distinctions aren't really useful to this discussion, because the original assumption was that we were using a class named Font. It sounds like you have issue with the naming of Font more then name of ObjectWithFont. You certainly don't need to use a name like Font, you could use whatever name suits you best, in whatever style you'd like.

I've commonly heard many people use the "Renderable" Base class in much the same way I describe. The problem with "ables" is that it gets hard to find suitable words to describe certain properties. For example, Renderable, Messageable, Clickable, all make sense - but how does one explain that an object uses a font? "Fontable"?

So I was left with "ObjectWithFont". Pretty crappy I know, in fact that's where I thought I would get the most response. Maybe a solution like "FontComponent" would be better. I'm open for suggestions...

Quote:
Maybe the object needs multiple fonts (like one font for a label, and another for items). Maybe the object is a ObjectWithFont, and inherits from a different object which also is an ObjectWithFont. Should those fonts be the same? Who knows!


The issue of multiple fonts is the same as multiple wheels. In practice I would more likely use an ObjectWithGUISkin that defined all sorts of gui related visual variations - a font for captions, a font for text, a color for captions, ect... ; The example using ObjectWithFont was simple because it was an example.

As for an object inheriting from ObjectWithFont, and inheriting another object that also inherits from ObjectWithFont -- Clearly in this case a design mistake has been made. If the object needs mutliple fonts then let it derive from a class which defines the relationships - like ObjectWithGUISkin. Then the derived class can juse continue using the GUISkin functionality in whatever way it needs.



------

Is it just me, or are most of these responces about bad design in general and not really about a bad inheritance scheme.

I feel like I'm responding to alot of:

"You can't drive a 6 speed car because a drunk person already has enough trouble with 5 gears".

/end obscure anology ;)
Sneftel
Sneftel
Quote:
Original post by Nuget5555
I've commonly heard many people use the "Renderable" Base class in much the same way I describe. The problem with "ables" is that it gets hard to find suitable words to describe certain properties. For example, Renderable, Messageable, Clickable, all make sense - but how does one explain that an object uses a font? "Fontable"?

Well, look at what differentiates those gramatically. They all specify what can be done to/with the object. You couldn't call something a "Fontable" because you can't "Font" something. What methods could it even have? (getFont is an example of one that it can't have, because an object might use multiple fonts to render GUI text.)
Fruny
Fruny
Food for thought: the Liskov substitution principle.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Emmanuel Deloget
Emmanuel Deloget
Interresting and challenging discussion :)

Quote:
Original post by Nuget5555
I think you are forgeting the benifit of virtual functions. Using your example, lets say we have implimented the entire class hierarchy and decide we want a special snazzy text box that renders it's frame upsidedown. All we have to do is create a new text box class that derives from the first textbox, and let it override the virtual DrawFrame() method defined in ObjectWithFrame.

As an alternative, you can add a new property to the frame class -- maybe a boolean value called "Draw_UpsideDown". I fail to see the problem with changing the frame class, when you have decided you want new behavior from the frame.

OCP? Introducing potential bugs in something that is already working? Adding complexity to a simple system?

Quote:
Original post by Nuget5555
I believe you are missinterpreting effecient coding for laziness here. My goal is to find a solution that makes sense, is easy to use, and doesn't take me and my team the next 3 months to impliment (We've got much more important things to worry about then writting wrapper methods -- like making games.)

I understand this - but a design that rocks can give you an effective tool to use, while a lazy design may end up as a tool that needs constant modifications. So yes, you have more important things to do -- like making a working game that don't take ages to be coded because of a design problem somewhere in your base code.

Quote:
Original post by Nuget5555
Forgive me but I personally think investing the time and resources into setting up, maintaining, and running a code generator for the specific purpose of "making the implimentation obey the design" and no practical or function benifit -- a complete waste of time. Especially in the case that an alternative solution exists that provides a logical and functional benifit to the implimentation and use.

You are forgiven. I tend to think the same (code generators are useful if they already exist).

Quote:
Original post by Nuget5555
It is exactly this feeling (and you are not alone) that I think is an issue of semantics, or perception -- rather then fundemental problems with multiple inheritance or static design heirarchies. How is using multiple derived components any more or less static then using one? And when you try to think of examples, please be sure to point out an issue specific to multiple inheritance, and not a bad design issue in general.

I would argue that using multiple inheritance correcly is no more static then any other correcly implimented design pattern. There is a general problem with inheritance hierarchies getting to deep or to large in general, but that is a problem of complexity, and not a problem of multiple inheritance. You could just as easily run into the same problem with any other aspect of programming: you stretch the design to far, and your brain can't wrap around it as quick and easy, so you are much more likely to make mistakes that effect the design in ways you couldn't forsee.

I agree with you - multiple inheritance is a tool, and all tool can be used correctly. The problem is that some tools are harder to use correctly than others, and multiple inheritance is one of them - in fact, inheritance is already a difficult tool to use, and multiple inheritance is even harder. For example, let's speak about the single responsability principle. Using composition, the SRP can be enforced to a correct level (because the inner objects are dealing with their own responsability). Using inheritance, you effectively inject the responsabilities of your base class in your inherited class. SRP's dead.

Don't misunderstand me: inheritance is not Teh Evil!!1one - it is still a very powerfull tool, but one has to use it correctly. That's why OOD tend to favor composition over inheritance, and that's why (Peter?) Coad's rules are so restrictive (don't follow them completely: they lead to over-engineered designs).

Quote:
Original post by Nuget5555
A textbox "Has a" Font -- but you could also look at it like a textbox "Is a" ObjectWithFont. Do you see that by changing the name, and the design focus of your base classes, you can achieve the correct "Is a" relationship?


Changing the name don't change the semantic of the class. So, your ObjectWithFont class is a Font? If yes, then does it mean that finally, your TextBox is a Font too? I guess it is not. In fact, I'm pretty sure that your ObjectWithFont "has a" Font.

Now that I am awake, I see some other weaknesses in your design. For example, how will your drawframe class know what is the size of the frame? I see two solutions: it can ask the size to the ObjectWithDimension class, or its DrawFrame method takes the size as a parameter - meaning that the child class is responsible for the drawing of the frame. The first solution is not viable. The second solution needs that you write every draw() method of every control - in the end, you'll end up with more code than you are willing to write (after all, you are all about code writing efficiency).

Moreover, giving to your textbox class the responsability for drawing a custom kind of frame (using your DrawFrame() virtual method) is IMHO a no-no. First, this should not be the work of the CustomTextBox class, despite the fact that this customization is about drawing the frame. It requires the internal knowledge of how the frame works - thus, you are exposing implementation details to your child class, effectively breaking the encapsulation.

The other thing that bugs me deals with the classical control class hierarchy: after all, a textbox is a control with a font, a comboxbox too, all controls with fonts are ObjectWithFonts, thus in the end, a TextBox should not inherit directly from ObjectWithFont. As a consequence, I see no benefits at all to this approach - comparing to a "traditional" approach where TextBox "is a" Control and Control "has a" Font (and a dimension, and a rendering policy, and so on). In fact, I don't see what you are saving, considering that you added one class (TextBox is a Control, Control is an ObjectWithFont, ObjectWithFont has a Font).

I hope I'm clearer now :)

Regards,
rKallmeyer
rKallmeyer
Quote:
Original post by Emmanuel Deloget
Interresting and challenging discussion :)

Quote:
Original post by Nuget5555
I think you are forgetting the benefit of virtual functions. Using your example, lets say we have implemented the entire class hierarchy and decide we want a special snazzy text box that renders it's frame upside down. All we have to do is create a new text box class that derives from the first textbox, and let it override the virtual DrawFrame() method defined in ObjectWithFrame.

As an alternative, you can add a new property to the frame class -- maybe a boolean value called "Draw_UpsideDown". I fail to see the problem with changing the frame class, when you have decided you want new behavior from the frame.

OCP? Introducing potential bugs in something that is already working? Adding complexity to a simple system?


I believe you are implying that altering the frame class to give it additional functionality is inherently dangerous and should be avoided. This would only be the case if the frame class were poorly designed in the first place. Just because I wish to implement a new inheritance scheme doesn't mean I wish to leave behind all the benefits of traditionally excepted "good design".

In this case, I would hope that the frame class was designed and implemented in such a way to allow easy modification and expansion to it's base functionality. If not, this is again a problem with bad design in general, not with multiple inheritance.

Quote:
Quote:
Original post by Nuget5555
I believe you are misinterpreting efficient coding for laziness here. My goal is to find a solution that makes sense, is easy to use, and doesn't take me and my team the next 3 months to implement (We've got much more important things to worry about then writing wrapper methods -- like making games.)

I understand this - but a design that rocks can give you an effective tool to use, while a lazy design may end up as a tool that needs constant modifications. So yes, you have more important things to do -- like making a working game that don't take ages to be coded because of a design problem somewhere in your base code.

There you go again with the "lazy". I can give you my word that this ObjectWith___ idea was not created out of laziness, it was more like I wanted to create a design that rocks, that could also give me an effective tool to use ... ;)

Quote:
Quote:
Original post by Nuget5555
It is exactly this feeling (and you are not alone) that I think is an issue of semantics, or perception -- rather then fundamental problems with multiple inheritance or static design hierarchies. How is using multiple derived components any more or less static then using one? And when you try to think of examples, please be sure to point out an issue specific to multiple inheritance, and not a bad design issue in general.

I would argue that using multiple inheritance correctly is no more static then any other correctly implemented design pattern. There is a general problem with inheritance hierarchies getting to deep or to large in general, but that is a problem of complexity, and not a problem of multiple inheritance. You could just as easily run into the same problem with any other aspect of programming: you stretch the design to far, and your brain can't wrap around it as quick and easy, so you are much more likely to make mistakes that effect the design in ways you couldn't for see.

I agree with you - multiple inheritance is a tool, and all tool can be used correctly. The problem is that some tools are harder to use correctly than others, and multiple inheritance is one of them - in fact, inheritance is already a difficult tool to use, and multiple inheritance is even harder. For example, let's speak about the single responsibility principle. Using composition, the SRP can be enforced to a correct level (because the inner objects are dealing with their own responsibility). Using inheritance, you effectively inject the responsabilities of your base class in your inherited class. SRP's dead.

Don't misunderstand me: inheritance is not Teh Evil!!1one - it is still a very powerfull tool, but one has to use it correctly. That's why OOD tend to favor composition over inheritance, and that's why (Peter?) Coad's rules are so restrictive (don't follow them completely: they lead to over-engineered designs).

Yes, multiple inheritance can certainly be harder to use correctly than simpler constructs. However I think the greatly increased efficiency, and eventual ease-of-use outweighs the initial hardships. You could say the same thing about almost every programming construct invented since the pointer. And just because a construct has been around a while doesn't mean its necessarily ever been used in the right way.

I think it is easy to forget that we are still in the infancy of programming development -- we don't have the luxury of 3000 years of time-tested mathematical theories, or even hundreds of years with physics or modern biology.

Quote:
Quote:
Original post by Nuget5555
A textbox "Has a" Font -- but you could also look at it like a textbox "Is a" ObjectWithFont. Do you see that by changing the name, and the design focus of your base classes, you can achieve the correct "Is a" relationship?


Changing the name don't change the semantic of the class. So, your ObjectWithFont class is a Font? If yes, then does it mean that finally, your TextBox is a Font too? I guess it is not. In fact, I'm pretty sure that your ObjectWithFont "has a" Font.


No, check a few posts up, I clarified that ObjectWithFont is not a Font. You were correct in assuming it "Has a" font.

Quote:
Now that I am awake, I see some other weaknesses in your design. For example, how will your drawframe class know what is the size of the frame? I see two solutions: it can ask the size to the ObjectWithDimension class, or its DrawFrame method takes the size as a parameter - meaning that the child class is responsible for the drawing of the frame. The first solution is not viable. The second solution needs that you write every draw() method of every control - in the end, you'll end up with more code than you are willing to write (after all, you are all about code writing efficiency).

Bifurcation (also known as false dilemma)
There are several other completely plausible solutions to this problem that pose no such design restrictions. If you can't think of any, let me know and I will oblige.

Quote:
Moreover, giving to your textbox class the responsability for drawing a custom kind of frame (using your DrawFrame() virtual method) is IMHO a no-no. First, this should not be the work of the CustomTextBox class, despite the fact that this customization is about drawing the frame. It requires the internal knowledge of how the frame works - thus, you are exposing implementation details to your child class, effectively breaking the encapsulation.

Quote:
Quote:
As an alternative, you can add a new property to the frame class -- maybe a boolean value called "Draw_UpsideDown". I fail to see the problem with changing the frame class, when you have decided you want new behavior from the frame.

OCP? Introducing potential bugs in something that is already working? Adding complexity to a simple system?

It seems to me as though you are contradicting yourself? I had already proposed the alternative of giving the frame class the ability to render itself in a new way -- and you said that would introduce design errors. But now you are implying that expanding the textbox is not a viable solution. So if not the textbox, and not the frame, who should render the frame? And when you do answer -- great! you've only just proved my point that adding functionality onto existing components is completely possible, and doesn't need to Introduce bugs or complexity to a simple system

Quote:
The other thing that bugs me deals with the classical control class hierarchy: after all, a textbox is a control with a font, a ComboBox too, all controls with fonts are ObjectWithFonts, thus in the end, a TextBox should not inherit directly from ObjectWithFont. As a consequence, I see no benefits at all to this approach - comparing to a "traditional" approach where TextBox "is a" Control and Control "has a" Font (and a dimension, and a rendering policy, and so on). In fact, I don't see what you are saving, considering that you added one class (TextBox is a Control, Control is an ObjectWithFont, ObjectWithFont has a Font).

The font was clearly a bad example, because in most GUI systems the exposure of graphical resources is fairly common to all objects (hence the 'graphical' in 'graphical user interface').

In fact, having spent another day thinking about it, I have all but dropped the idea entirely for use with GUI objects, but simply for the reason that I'd rather give all objects more functionality then they need (IE - derive from a common control class that contains the union of common control functionality) then spend the extra time and effort keeping the objects as tight as they can be.

However*** I will use the ObjectWith____ idea for some of the sub classing at a higher level, and here is a concrete example:

The relationship between ListBox's and ComboBox's can be best described by saying they are both an object with a list. A ComboBox IS NOT a ListBox. This list is visually identical (when exposed from the combobox) and the api for adding/removing objects from the list is identical.

ObjectWithList contains a map of strings to (void*, int, boost::any -- whatever you find the least offensive). It draws the strings from first last in a top-down manner, marking the selected string in some manner and keyboard input allows the user to cycle the keys from top to bottom.

some example methods:

public:
addItem(const std::string& rName, const boost::any& rData);
const std::string& getSelectedName() const;
const boost::any& getSelectedData() const;
removeItem(const std::string& rName);

protected:
setListVisible(bool bVisible);
setListDimensions(const Vector4& rDimensions);

A Listbox derives from ObjectWithList. the listbox calls setListVisible(true) upon construction.

A ComboBox derives from ObjectWithList. the combobox calls setListVisible(false) upon construction. the combobox toggles the visibility of the listbox when the dropdown button is clicked.

Both the ComboBox and ListBox update the dimensions of the list using setListDimensions when the event handler OnDimensionChange() is fired. The ListBox mearly passes the dimensions through, and the ComboBox adjusts the y coordinate to make room for the textbox that appears above the list.

I have only written the list functionality once. OOD is strictly enforced. Anyone see any problems here?

Emmanuel Deloget
Emmanuel Deloget
Hello again.

I just want to say that I was exposing an opinion - hence the "IMHO", "I think", and so on that are in my posts. While I wouldn't doo what you are trying to do, I believe that you should do what you want.

In this particular case, I even encourage you to implement your solution. I also encourage you to try to see how a composition-based solution would have been implemented, and compare the resulting code (I agree that comparing non-existent code to something that exists is very difficult). I'm pretty sure that a composition-based solution is simpler, smaller, easier to maintain and easier to extend.

Now, to answer some particular questions:

Quote:
There you go again with the "lazy". I can give you my word that this ObjectWith___ idea was not created out of laziness, it was more like I wanted to create a design that rocks, that could also give me an effective tool to use ... ;)

My English vocabulary is rather limited. I can't find a better word to express my idea. I don't imply that you are lazy, I want to say that this is a simple solution - but not the better.

Quote:
There are several other completely plausible solutions to this problem that pose no such design restrictions. If you can't think of any, let me know and I will oblige.

I'd like to know, because I can't think of any other alternative using your design (but now, it's 3 a.m, so I'm a bit tired).

Quote:
It seems to me as though you are contradicting yourself?

No, not at all. I simply point the problems I see. Modifying the Frame class each time you want to add a new functionality breaks tha OCP, while exposing the virtual DrawFrame() method breaks both the SRP and the encapsulation principle. If "not the textbox not the frame", then who? The only solution is an external object - a strategy. If you implement strategies for drawing frames, (hence you use the composition pattern in your object: your class now "has a" frame drawing strategy) what is the interest of all this design? You are doubling the number of classes!

Quote:
In fact, having spent another day thinking about it, I have all but dropped the idea entirely for use with GUI objects, but simply for the reason that I'd rather give all objects more functionality then they need (IE - derive from a common control class that contains the union of common control functionality) then spend the extra time and effort keeping the objects as tight as they can be.

Guess what: this is a more traditional use of the compostion pattern - the kind of use I was trying to advocate in my previous posts [smile].

Quote:
I have only written the list functionality once. OOD is strictly enforced. Anyone see any problems here?

For your listbox/combobox, I hope that you see that the LSP is broken: a combobox ObjectWithList parent don't act like a listbox ObjectWithList. Now, the list object in the combobox acts like a listbox. Again, this is a better job for the composition pattern (a combobox is a object that contains both a textbox and a listbox that can be invisible). Now, concerning the dimension update mecanism, I have nothing to add - a composition-based design would use the same solution.

So far, what I understand of your design is that you are going to use a composition scheme through inheritance. I still fail to see the advantages of this method vs. a traditional application of the composition pattern. However, I see numerous problems - lots of broken design principles. Of course, I may misunderstand your solution, and maybe I'm just reluctant to such kind of multiple inheritance usage.

A point about software design: when I design a software for a customer, I always begin to think like this: I am the client and I want to add a functionality to the software, do I have to modify the existing code or can I just add tthe behavior I want? If I answer to the question by 'the former', then I erase everything and I redo my design. In you design, tha answer to this question is still unclear (again, IMHO). In your head, it is currently "the later". But as I understand it, a good number of design principles are not used, and it may more likely lead to a "the former" answer.

Regards,
rKallmeyer
rKallmeyer
Quote:
Original post by Emmanuel Deloget
Hello again.

I just want to say that I was exposing an opinion - hence the "IMHO", "I think", and so on that are in my posts. While I wouldn't doo what you are trying to do, I believe that you should do what you want.

In this particular case, I even encourage you to implement your solution. I also encourage you to try to see how a composition-based solution would have been implemented, and compare the resulting code (I agree that comparing non-existent code to something that exists is very difficult). I'm pretty sure that a composition-based solution is simpler, smaller, easier to maintain and easier to extend.

(chuckle) It sounds like I might have given you the impression that I was taking offense. Quite the contrary, I appreciate good-spirited criticism as it makes for a better programmer. Hence the post on gamedev~

Quote:
Now, to answer some particular questions:

Quote:
There you go again with the "lazy". I can give you my word that this ObjectWith___ idea was not created out of laziness, it was more like I wanted to create a design that rocks, that could also give me an effective tool to use ... ;)

My English vocabulary is rather limited. I can't find a better word to express my idea. I don't imply that you are lazy, I want to say that this is a simple solution - but not the better.

Well It might be that we are coming from two different programming worlds. This doesn't surprise me at all because I've got a rather specific scope of programming experience - primarily in embedded systems and games, in which I've had to adopt a pretty strict rapid-application-development mind frame. And how I work and who I work with pretty much dictate that the simplest solution is almost invariably the best one.

Quote:
Quote:
There are several other completely plausible solutions to this problem that pose no such design restrictions. If you can't think of any, let me know and I will oblige.

I'd like to know, because I can't think of any other alternative using your design (but now, it's 3 a.m, so I'm a bit tired).


"how will your drawframe class know what is the size of the frame?"

1)ObjectWithFrame and TextBox both virtually derive from ObjectWithDimensions. ObjectWithFrame only needs to call getDimensions().

2)TextBox derives from ObjectWithFrame and calls setFrameDimensions() every time it's own dimensions change.

3)All Gui Renderables could use a draw queue interface where in it's constructor, TextBox calls something like addToDrawQueue(&ObjectWithFrame::DrawFrame) -- passing in the function pointer. Now its up to the third party to send in the dimensions to DrawFrame.

Quote:
Quote:
It seems to me as though you are contradicting yourself?

No, not at all. I simply point the problems I see. Modifying the Frame class each time you want to add a new functionality breaks tha OCP, while exposing the virtual DrawFrame() method breaks both the SRP and the encapsulation principle. If "not the textbox not the frame", then who? The only solution is an external object - a strategy. If you implement strategies for drawing frames, (hence you use the composition pattern in your object: your class now "has a" frame drawing strategy) what is the interest of all this design? You are doubling the number of classes!


I like strategies, and use them quite frequently. However I don't think they work everywhere, and this is a case where I think keeping the FrameDrawing logic inside the frame is best.

Quote:
Guess what: this is a more traditional use of the compostion pattern - the kind of use I was trying to advocate in my previous posts [smile].

Glad I could make your day ;)

Quote:
Quote:
I have only written the list functionality once. OOD is strictly enforced. Anyone see any problems here?

For your listbox/combobox, I hope that you see that the LSP is broken: a combobox ObjectWithList parent don't act like a listbox ObjectWithList. Now, the list object in the combobox acts like a listbox. Again, this is a better job for the composition pattern (a combobox is a object that contains both a textbox and a listbox that can be invisible). Now, concerning the dimension update mecanism, I have nothing to add - a composition-based design would use the same solution.

Would I be asking for trouble if I thought sticking to LSP 100% of is pretty ludicrous? Why do two derived classes of the same super class need to "act like each other" as you put it? Correct me if I'm wrong, but isn't the point of inheritance to allow specialization of derived classes?

If you mean to say that using an ObjectWithList as a common interface for both ComboBoxes and ListBoxes will not work then that is incorrect. As far as pre/post conditions, properties, and logic -- Treating a ListBox and/or a ComboBox as an ObjectWithList works perfectly. Maybe you are assuming I am dragging more functionality along into ObjectWithList then I laid out?

Quote:
So far, what I understand of your design is that you are going to use a composition scheme through inheritance. I still fail to see the advantages of this method vs. a traditional application of the composition pattern. However, I see numerous problems - lots of broken design principles. Of course, I may misunderstand your solution, and maybe I'm just reluctant to such kind of multiple inheritance usage.

I think a better way to look at it is that I intend to use all the tools available to me in order to get the task done right. Compositions work in some cases, Strategies work in others, and Inheritance has a place as well. I think in this case a combination of everything works the best without being overly engineered or complicated -- as it might turn out to be if I maintained strict adherence to only ONE design philosophy.

As far as broken design principles go -- I'm deliberately trying to break a few by proposing a different solution. I've tried to make it clear that I have no respect for those who came before me -- Its not as though I just accidentally screwed up [wink].

Quote:
A point about software design: when I design a software for a customer, I always begin to think like this: I am the client and I want to add a functionality to the software, do I have to modify the existing code or can I just add tthe behavior I want? If I answer to the question by 'the former', then I erase everything and I redo my design. In you design, tha answer to this question is still unclear (again, IMHO). In your head, it is currently "the later". But as I understand it, a good number of design principles are not used, and it may more likely lead to a "the former" answer.

I am somewhat confused.

Are you trying to say you "do I have to modify the existing design?" -- Because saying that you can add new features without touching code at all implies some sort of super-hero programming wizardry.

If you do mean design, or you mean that you wish to implement new features while touching as little code as possible -- then great. Me too [smile]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.