I have just been ruminating over this, and have trouble understanding this item, just in regard to what he is saying about protected members - what he says about public members I am fine with. So far I have read up to Item 27.
For those that don't have the book, basically he is saying that: the arguments against public data members apply equally to protected members; and Protected members are a problem because of the amount of code that can be broken when one needs to change a protected member.
I wish to say that I am reticent about arguing against a renowned expert author with a Ph.D. in Computer Science :) It is not so much me arguing per se, but more to put some questions to benefit my education and incrementing my
ThingsLearnt
variable quite a lot!!
The problem I have with this is it seems to go against the purpose of having protected members being effectively private in derived classes. If members are private in a base class, derived classes cannot have access to them, having public access functions in the base class does not help when one has a derived object. How to remedy this - especially if one is modelling real world situations?
Would the problem of being able to cope with changes to protected members, be better handled by making the data member a class of it's own, with it's own interface? Then the member could be public (as it has it's own interface) and inherited, any changes needed could be made internally to the member class. If the data member class now holds more info, then more interface functions can be added. In doing this, the data member's internal variables would be private - it is probably unlikely in this case that one would derive further classes from this. Is this really what he is saying?
Here is some code to explain what I was thinking - there is a lot stuff left out:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
class CNutrition {
private:
//variables to cover energy, fat, protein, carbs, salt, vitamins etc.
public:
//ctors & dtors etc
void DisplayAmountPerServe();
//other interface functions here
};
class CHumanFood {
protected:
CHumanFood(); //can't make an obj of this type
double m_Weight;//should make this a class to help future proof it
public:
CNutrition Nutrition;//has it's own interface
double getWeight();
};
class CFuitAndVeg : public CHumanFood {
protected:
std::string m_Variety;//should make this a class to help future proof it
FuitAndVeg(); //can't make an obj of this type
public:
//functions to access data
};
class CFruit : public CFuitAndVeg {
//this class is needed for generic function declarations like this:
// MakePie (CPastry *, CFruit*);
protected:
CFruit(); //can't make an obj of this type
};
class CApple : public CFruit {
public:
CApple(); //make various apples with this class
};
//similar arrangements for veges, meat, bakery etc
//bakery might have more functions like this:
// MakePie (CPastry*, CVege*);
// MakePie (CPastry*, CMeat*);
// MakePie (CPastry*, CVege*, CMeat*);
|
So now if one wants to change the underlying structure of CNutrition to include things like % of Recommended Daily Intake (%RDI) and grams per 100 grams, as well as the existing grams per serving (CNutrition::InsertGramsPerServeInfo), then these extra interface functions can be added.
With the m_Weight member, maybe we want to add the ability to specify different units such as KG, grams, Pounds, Ounces say, and allow conversions between them for output. Same story - make it a class from the start to avoid breaking code later.
Then there is the m_Variety name, often these have 4 digit numbers associated with the name, so make this a class as well.
I have also been ruminating on the Gang of Four Design Patterns book, so the other problem I have is with the idea of preferring Composition / Aggregation over inheritance. I guess it only applies in certain situations - these are the issues I am having:
First, it ruins polymorphism - that is the ability to do this say:
1 2
|
//A variety of different fruit
Bakery::MakePie (CPastry* Pastry, CFruit* Fruit){}
|
Can I conclude that it is OK to have an arbitrarily large inheritance tree if it is being used for the purpose of polymorphic function calls like this example of a Command Design Pattern?
1 2 3
|
//A Tile is occupied by a CEnemy and / or a CResource of some kind
//each of these classes has lots of derived classes
CCommandMgr::Execute(CAction* Action, CPlayer* Player, CWeapon* Weapon, CTile* Tile){}
|
Second, if a class has more than 1 child at a particular level in the tree, then it ruins the idea of inheritance of data members. One shouldn't have to specify the composite member for each child at that level. Is that not one of the reasons why the idea of inheritance was invented? With member functions, one can write them using templates, so that they become algorithms - which is great, but we have to remember about inheritance of interface too.
Some one else also said (possibly
helios)that it is a bad idea to have atomic objects (such as points in a CAD system say) as being part of a large inheritance tree - rather have them as concrete classes. I can see the sense in that good advice. I can also see the advantages of the Composite Design Pattern to build complex objects.
So, what are the situations where composition is a good idea given the other considerations? Or have I identified some exceptions to the rule - maybe there are others I haven't thought of?
What do people think about having a moderated forum, where members can put forward different examples of Design Patterns, or does such a thing exist already? I may need to follow my own advice to others & look on Google or wiki :D
Any way I look forward to your replies - Cheers.