Most textbooks about OOP I read, say you should use classes to describe abstract types. They also say you should use inheritance, when you want to add more functionality to a base class. And the last thing they say, among many others, is to use multiple inheritance to build complex types, that match in functionality all the base classes.
While I agree with the first two statements, I totally disagree with the last one. And this interlude should clarify that.
Before I go on with my rant, I want to explain a bit the philosophy I use when dealing with OOP: you always have to work with consumers and providers.
The consumer is the user of your class or class hierarchy. The consumer doesn’t care how your class does its job, but only that your class does it, when it does it or why it doesn’t.
The provider is your class or class hierarchy. Think about it as a mini library, the user of your library doesn’t care how you do your job as long as you do it.
In CTM I have a base class that describes the Cable Modem abstract type. All other objects from my project will have to work with this abstract type and are only interested in the modem status, synchronized or not and they don’t care how the object gets that status. My consumers won’t even care about the cable modem type, they just need a text that describes the modem so they can show that to the user. So, my CModemDetailsBase describes the abstract type my application works with.
In the future I plan to add more modem types to my application. So, I wrote a second class, CWebstarModem, that inherits from CModemDetailsBase class and contains the specific logic for this kind of modem. However, my consumers won’t care if they use a Webstar modem or any other type of modem as long as they can simply ask the modem object about it’s status. I added new functionality to a base class, but internal only.
I agree with the second statement from my first paragraph, but I’m not always using inheritance: I use it only and just only I want my extended class to be used as my base class. If I get no benefit from inheritance, I’m not using it. Examples below.
Good way: in my project, so far, I have a CModemDetailsBase and a CWebstarModem that inherits from the base class. Later, I will add a CSNMPModem and a CHTTPModem, first dealing with modems that allow you to query them through SNMP and the second with modems, like my current modem, that let you get the status through HTTP. This way, I will have to write the connection code for each type of modem just once. I still have my original functionality in place, allow the consumers to threat my objects in the same way and properly reuse code.
Bad way: let’s suppose you want to add checking functionality to your form fields. You want a simple method to have the fields in the form checked for proper values. One solution would be to extend the form fields classes, text box, checkbox, radio buttons etc and add the checking functionality. Not such a good idea, because now you will have to extend all types of form fields. Not to mention you will mix GUI code to controller code.
A better way to solve this problem would be to build a new type of object, CFormField, which will have a GUI member, the textbox, the checkbox, etc and a checking member, which will deal only with the value checking. Now, you can build a class hierarchy that deals only with checking values, instead of building a class hierarchy that deals with all kinds of mixes, CEmailTextbox, CNameTextbox, CNameSelect etc. Also, you can re-use the checkers in other places, outside of a form, let’s say to check if values coming from a web service are valid.
The second example has a third solution, which illustrates the third statement from the first paragraph, the one I disagree with. You could build a new class, that inherits from both GUI element and checking element. Now, you have an object, that is in the same time a GUI element and a checking element. This implies you already have the checking classes hierarchy and it assumes you will build all the mixes you need, like in the second example. Complexity of your code will go up, it will be harder to debug and what not.
In the end, here are some thumb rules for working with OOP: