Just for fun I am working on a XUL implementation for Windows. In XUL, UI elements can be written in XML like this:
<window width="800" height="600"></window>
I am considering a system for getting and setting element attributes. It's working pretty well but I am not certain if the use of diamond inheritance is potentially hazardous here. I've posted a complete code sample below:
#include <boost/lexical_cast.hpp>
#include <string>
#include <map>
class Attribute
{
public:
virtual void get(std::string & outValue) = 0;
virtual void set(const std::string & inValue) = 0;
static int String2Int(const std::string & inString)
{
return boost::lexical_cast<int>(inString);
}
static std::string Int2String(int inValue)
{
return boost::lexical_cast<std::string>(inValue);
}
};
class Width : public Attribute
{
public:
Width(){}
virtual void get(std::string & outValue)
{
outValue = Int2String(getWidth());
}
virtual void set(const std::string & inValue)
{
setWidth(String2Int(inValue));
}
virtual int getWidth() const = 0;
virtual void setWidth(int inWidth) = 0;
};
class Height : public Attribute
{
public:
Height(){}
virtual void get(std::string & outValue)
{
outValue = Int2String(getHeight());
}
virtual void set(const std::string & inValue)
{
setHeight(String2Int(inValue));
}
virtual int getHeight() const = 0;
virtual void setHeight(int inHeight) = 0;
};
class Element : public Width, // concerning the is-a vs has-a philosophy
public Height // => see my note below
{
public:
Element() :
mWidth(0),
mHeight(0)
{
// STATIC CAST NEEDED HERE OTHERWISE WE GET COMPILER ERROR:
// error C2594: '=' : ambiguous conversions from 'Element *const ' to 'Attribute *'
mAttrControllers["width"] = static_cast<Width*>(this);
mAttrControllers["height"] = static_cast<Height*>(this);
}
void setAttribute(const std::string & inAttrName, const std::string & inAttrValue)
{
Attributes::iterator it = mAttrControllers.find(inAttrName);
if (it != mAttrControllers.end())
{
Attribute * attribute = it->second;
attribute->set(inAttrValue);
}
}
std::string getAttribute(const std::string & inAttrName)
{
std::string result;
Attributes::iterator it = mAttrControllers.find(inAttrName);
if (it != mAttrControllers.end())
{
Attribute * attribute = it->second;
attribute->get(result);
}
return result;
}
virtual int getWidth() const
{
return mWidth;
}
virtual void setWidth(int inWidth)
{
mWidth = inWidth;
}
virtual int getHeight() const
{
return mHeight;
}
virtual void setHeight(int inHeight)
{
mHeight = inHeight;
}
private:
typedef std::map<std::string, Attribute *> Attributes;
Attributes mAttrControllers;
int mWidth;
int mHeight;
};
int main()
{
Element el;
el.setAttribute("width", "800");
el.setAttribute("height", "600");
int w = el.getWidth();
int h = el.getHeight();
return 0;
}
I think it's ok since the base class Attributes has no data members, so no conflicts can arise there. But I thought I'd check with the community. Your insights are much appreciated!
Edit
About the "is-a" vs "has-a", and "favor composition over inheritance" remarks I have this to say:
There
is an advantage to inheritance here.
If Element inherits Width then it is
forced to implement the getWidth and
setWidth methods. So adding an
attribute implies an 'automatic'
update of Element's interface.
I originally named these classes
AttributeController, WidthController
and HeightController, but I found
them too verbose. You could say that my Element is an Attribute controller. (Ok, that's lame, but not untrue!)
Further proof: the definitions of Width and Height
do not contain any data members. The
Element class actually has them.
The Width and Height classes only
provide the interfaces. So it's more of a can-do relationship.
In your scenario, Element should probably not inherit from Width and Height, but instead, Width and Height should be data members of element. It's composition as opposed to is-a since arguably an Element is-not-a Width or Height but is composed of a Width and Height (and probably some other stuff too).
Element should inherit Width only if you need to use Element objects as Width ones . Inheritance is not for code reuse.
Maybe would be worth for you taking a look into boost::program_options library. I like the fancy way they have for registering properties.
If I was going to do this, I'd do a virtual inherit on Attribute. I don't think it will matter significantly, but that will minimize the duplication if it does end up mattering.
First of all, use virtual inheritance if you are facing face a diamond problem, that is, use public virtual instead of just public on the base classes.
Secondly, it's not well defined what your get- and set-will do as there are two implementations. The only time I use multiple inheritance is when I extend pure virtual classes (aka interfaces). It's a good reason for Java to not support multiple inheritance.
Third, and more importantly, this seems like a classic case between a misunderstanding of inheritance ("is a") vs aggregation ("has a") in object orientation. You can two very simple guidelines when determining if a class should inherit another class. If you have class A and class B which inherits class A, the sentence "A is a B" should make sense. If "A has a B" sounds better, you should really consider letting B be a member of A instead.
In your case the sentences "Element is a Height" and "Element is a Width" really doesn't make sense. "Element has a Height" makes perfectly sense.
Related
I'm trying to learn decorator design and I came up with something awesome, but I don't know if my idea will compile. So I created some classes:
this is the base class
class parameter
{
public:
parameter(){}
parameter(double mini, double maxi, double def) :
mini(mini),
maxi(maxi),
def(def)
{}
double mini, maxi, def;
double val;
virtual double getValue() { return val; }
virtual void setValue(double v) { val = v; }
};
This class stores smoothedParameters. smoothedParameter will add itself to the SmootherManager when they need to be smoothed and remove themselves when they are finished.
class SmootherManager
{
public:
SmootherManager() {}
juce::Array<smoothedParameter *> CurSmoothingList;
void add(smoothedParameter * sp)
{
CurSmoothingList.addIfNotAlreadyThere(sp);
}
void remove(smoothedParameter * sp)
{
CurSmoothingList.removeFirstMatchingValue(sp);
}
void doSmoothing()
{
for (auto & sp : CurSmoothingList)
sp->incValue();
}
};
This class takes values over time and outputs a smoothed value.
class smoothedParameter : public parameter
{
public:
//smoothedParameter(){}
smoothedParameter(double smoothingSpeed, SmootherManager & manager, parameter * p) :
smoothingSpeed(smoothingSpeed),
manager(manager),
p(p)
{}
double smoothingSpeed;
SmootherManager & manager;
parameter * p;
rosic::ExponentialSmoother smoother;
double getValue()
{
return smoother.getCurrentValue();
}
void setValue(double v)
{
p->setValue(v);
smoother.setTargetValue(p->getValue());
if (!smoother.finishedSmoothing())
manager.add(this);
}
void incValue()
{
smoother.getSample();
if (smoother.finishedSmoothing())
manager.remove(this);
}
};
This class takes a value and modifies it over time via a list of modifiers.
class modulatedParameter : public parameter
{
public:
modulatedParameter(parameter * p) : p(p) {}
juce::Array<modifier *> modulationInputs;
parameter * p;
double getValue()
{
double totalMod = 0;
for (const auto & m : modulationInputs)
totalMod += m->val;
return totalMod * p->getValue();
}
void setValue(double v)
{
p->setValue(v);
}
void add(modifier * sp)
{
modulationInputs.addIfNotAlreadyThere(sp);
}
void remove(modifier * sp)
{
modulationInputs.removeFirstMatchingValue(sp);
}
};
So here's how it works. You have a smoother and a modulator. If you construct a smoother inside the modulator, you get a smoothed modulator. If you construct a modulator inside a smoother, you get a non-smoothed modulator.
Here's how I wanted to use the classes:
// create the smoother manager
SmootherManager smManager;
// create modulatable parameter
auto mp = new modulatedParameter(new parameter(0.0, 1.0, 0.0));
// create a smoothable parameter
auto sp = new smoothedParameter(0.01, smManager, new parameter(0.0, 1.0, 0.0));
// create a modulatable parameter where its modifiers are smoothed
auto mp_sp = new modulatedParameter(new smoothedParameter(0.01, smManager, new parameter(0.0, 1.0, 0.0)));
// create a parameter where values are smoothed, but the modulation is not
auto sp_mp = new smoothedParameter(0.01, smManager, modulatedParameter(new parameter(0.0, 1.0, 0.0)));
ok! here's problem.
modifier myMod;
// add a modifier to sp_mp, can't do it, sp_mp has no add function.
sp_mp->add(&myMod);
I'm trying to add a modulator to the modulatedParameter of smoothedParameter. I thought of a way, but this seems wrong.
auto mp = new modulatedParameter(sp_mp->p);
mp->add(&myMod)
sp_mp = new smoothedParameter(0.01, smManager, mp));
Any time I want to add/remove a modifier, I have to go through several steps. I could think of a way to remedy this but I am just so lost as to what is a practical approach because I don't know all the possibilities of C++. The point of decorator design is that objects can have a different set of functions. ...It seems like I'd need to have an "add/remove" function for every class, defeating the purpose of this design.
The point of decorator design is that objects can have a different set
of functions.
No, the point of decorator is to get the ability of flexibly extending the object`s base functionality, while preserving its core. Usually, the word "flexibly" presumes making this extension at run-time (dynamically).
Meanwhile, C++ is statically-typed language. It means that the type of an object/variable defines, what you are allowed to do to it and what you are not. sp_mp->add(&myMod); possible IIF the type (class) of the variable sp_mp has add(...) function. This decision is made at compile-time and no design pattern can change this fact, just bare with it. C++ compiler won't let you call functions/use member variables of the variable which are not part of its type.
No matter what you do, the interface of existing type is defined statically. Wanna change it? Do it at compile-time.
Now, taking into account everything was said, we can make a logical conclusion:
If you want to add some new functions to an existing type - create a new type.
Here is a more or less classic (I believe) Decorator implementation. *I did not used shared pointers just because... OP did not use them either :)
class ICore
{
public:
virtual std::string Description() = 0;
void Describe() {
std::cout << "I am " << Description() << std::endl;
}
};
class Core final : public ICore
{
public:
std::string Description() override {
return "Core";
}
};
class IDecorator : public ICore
{
protected:
ICore* core;
public:
IDecorator(ICore* _core)
: core{ _core }
{ }
virtual ~IDecorator() {
delete core;
}
};
class Beautiful final : public IDecorator
{
public:
Beautiful(ICore* _core)
: IDecorator{ _core }
{ }
public:
std::string Description() override {
return "Beautiful " + core->Description();
}
};
class Shiny final : public IDecorator
{
public:
Shiny(ICore* _core)
: IDecorator{ _core }
{ }
public:
std::string Description() override {
return "Shiny " + core->Description();
}
};
int main()
{
ICore* core = new Core;
ICore* decorated_core = new Beautiful{ new Shiny{ core } };
core->Describe();
decorated_core->Describe();
delete decorated_core;
return 0;
}
Output:
I am Core
I am beautiful shiny Core
As you see, here Decorator did not change an interface (class prototype) - no new functions were added to the core. Also, it did not change any existing functionality. What it did, however, was the extension of the already existing behavior. It literally decorated the description of the core with 2 new word. And note - this decoration happened at runtime. If we decided to change the decoration order from new Beautiful{new Shiny{core}} to new Shiny{new Beautiful{core}} the word order would change too (from beautiful shiny Core to shiny beautiful Core).
However, if you really-really want to fulfil your primary intent - adding a brand new function with decorator... There is a way, which lets you imitate such behavior. It would look ugly in C++14 so here is a C++17 code:
class Core
{
public:
void CoreFunctional() {
std::cout << "Core functional." << std::endl;
}
};
template<typename T>
class Extend : public virtual T
{
public:
Extend() = default;
Extend(const T&) { }
public:
void ExtendedFunctional() {
std::cout << "Extended functional." << std::endl;
}
};
template<typename T>
class Utility : public virtual T
{
public:
Utility() = default;
Utility(const T&) { }
public:
void UtilityFunctional() {
std::cout << "Utility functional." << std::endl;
}
};
int main()
{
Core core;
core.CoreFunctional();
auto decorated_core = Utility{Extend{core}};
decorated_core.CoreFunctional();
decorated_core.ExtendedFunctional();
decorated_core.UtilityFunctional();
}
The output is just as you would expect, but I am not really sure, if that may be considered to be a decorator...
The point of decorator design is that objects can have a different set of functions. ...It seems like I'd need to have an "add/remove" function for every class, defeating the purpose of this design.
No. Decorator pattern, as almost all the most known patterns, is all about interfaces and thus (in C++) virtual member functions.
You define your base class (either an abstract one or a concrete one you want to use as a base) where methods that can be decorated are virtual.
A decorator decores something that exists, it neither adds nor removes functions.
Whenever you define a decorator, you end up overriding those methods to enrich them and iteratively call the base class implementation of the same method. Then you pass around pointers/references to the base class and the user doesn't know if they are decorated or not. Just call it and the right thing will happen.
Let's consider this. If you add a new method, how could you invoke it from a reference or a pointer to the base class? You cannot, so you need the actual type, that is the derived one.
This defeats the purpose of the design, not the fact that you must add a method to a base class to be able to decorate it in a derived one.
If you are looking for a pattern that lets you add or remove functions from a class, consider mixins or whatever. That's not the goal of the decorator.
BIG EDIT
So after gathering some feedback from all of you, and meditating on the XY problem as Zack suggested, I decided to add another code example which illustrates exactly what I'm trying to accomplish (ie the "X") instead of asking about my "Y".
So now we are working with cars and I've added 5 abstract classes: ICar, ICarFeatures, ICarParts, ICarMaker, ICarFixer. All of these interfaces will wrap or use a technology-specific complex object provided by a 3rd party library, depending on the derived class behind the interface. These interfaces will intelligently manage the life cycle of the complex library objects.
My use case here is the FordCar class. In this example, I used the Ford library to access classes FordFeatureImpl, FordPartsImpl, and FordCarImpl. Here is the code:
class ICar {
public:
ICar(void) {}
virtual ~ICar(void) {}
};
class FordCar : public ICar {
public:
ICar(void) {}
~FordCar(void) {}
FordCarImpl* _carImpl;
};
class ICarFeatures {
public:
ICarFeatures(void) {}
virtual ~ICarFeatures(void) {}
virtual void addFeature(UserInput feature) = 0;
};
class FordCarFeatures : public ICarFeatures{
public:
FordCarFeatures(void) {}
virtual ~FordCarFeatures(void) {}
virtual void addFeature(UserInput feature){
//extract useful information out of feature, ie:
std::string name = feature.name;
int value = feature.value;
_fordFeature->specialAddFeatureMethod(name, value);
}
FordFeatureImpl* _fordFeature;
};
class ICarParts {
public:
ICarParts(void) {}
virtual ~ICarParts(void) {}
virtual void addPart(UserInput part) = 0;
};
class FordCarParts :public ICarParts{
public:
FordCarParts(void) {}
virtual ~FordCarParts(void) {}
virtual void addPart(UserInput part) {
//extract useful information out of part, ie:
std::string name = part.name;
std::string dimensions = part.dimensions;
_fordParts->specialAddPartMethod(name, dimensions);
}
FordPartsImpl* _fordParts;
};
class ICarMaker {
public:
ICarMaker(void) {}
virtual ~ICarMaker(void) {}
virtual ICar* makeCar(ICarFeatures* features, ICarParts* parts) = 0;
};
class FordCarMaker {
public:
FordCarMaker(void) {}
virtual ~FordCarMaker(void) {}
virtual ICar* makeCar(ICarFeatures* features, ICarParts* parts){
FordFeatureImpl* fordFeatures = dynamic_cast<FordFeatureImpl*>(features);
FordPartsImpl* fordParts = dynamic_cast<FordPartsImpl*>(parts);
FordCar* fordCar = customFordMakerFunction(fordFeatures, fordParts);
return dynamic_cast<ICar*>(fordCar);
}
FordCar* customFordMakerFunction(FordFeatureImpl* fordFeatures, FordPartsImpl* fordParts) {
FordCar* fordCar = new FordCar;
fordCar->_carImpl->specialFeatureMethod(fordFeatures);
fordCar->_carImpl->specialPartsMethod(fordParts);
return fordCar;
}
};
class ICarFixer {
public:
ICarFixer(void) {}
virtual ~ICarFixer(void) {}
virtual void fixCar(ICar* car, ICarParts* parts) = 0;
};
class FordCarFixer {
public:
FordCarFixer(void) {}
virtual ~FordCarFixer(void) {}
virtual void fixCar(ICar* car, ICarParts* parts) {
FordCar* fordCar = dynamic_cast<FordCar*>(car);
FordPartsImpl* fordParts = dynamic_cast<FordPartsImpl*>(parts);
customFordFixerFunction(fordCar, fordParts);
}
customFordFixerFunction(FordCar* fordCar, FordPartsImpl* fordParts){
fordCar->_carImpl->specialRepairMethod(fordParts);
}
};
Notice that I must use dynamic casting to access the technology-specific objects within the abstract interfaces. This is what makes me think I'm abusing inheritance and provoked me to ask this question originally.
Here is my ultimate goal:
UserInput userInput = getUserInput(); //just a configuration file ie XML/YAML
CarType carType = userInput.getCarType();
ICarParts* carParts = CarPartFactory::makeFrom(carType);
carParts->addPart(userInput);
ICarFeatures* carFeatures = CarFeaturesFactory::makeFrom(carType);
carFeatures->addFeature(userInput);
ICarMaker* carMaker = CarMakerFactory::makeFrom(carType);
ICar* car = carMaker->makeCar(carFeatures, carParts);
UserInput repairSpecs = getUserInput();
ICarParts* replacementParts = CarPartFactory::makeFrom(carType);
replacementParts->addPart(repairSpecs);
ICarFixer* carFixer = CarFixerFactory::makeFrom(carType);
carFixer->fixCar(car, replacementParts);
Perhaps now you all have a better understanding of what I'm trying to do and perhaps where I can improve.
I'm trying to use pointers of base classes to represent derived (ie Ford) classes, but the derived classes contain specific objects (ie FordPartsImpl) which are required by the other derived classes (ie FordCarFixer needs a FordCar and FordPartsImpl object). This requires me to use dynamic casting to downcast a pointer from the base to its respective derived class so I can access these specific Ford objects.
My question is: am I abusing inheritance here? I'm trying to have a many-to-many relationship between the workers and objects. I feel like I'm doing something wrong by having an Object family of class which literally do nothing but hold data and making the ObjectWorker class have to dynamic_cast the object to access the insides.
That is not abusing inheritance... This is abusing inheritance
class CSNode:public CNode, public IMvcSubject, public CBaseLink,
public CBaseVarObserver,public CBaseDataExchange, public CBaseVarOwner
Of which those who have a C prefix have huge implementations
Not only that... the Header is over 300 lines of declarations.
So no... you are not abusing inheritance right now.
But this class I just showed you is the product of erosion. I'm sure the Node as it began it was a shinning beacon of light and polymorphism, able to switch smartly between behavior and nodes.
Now it has become a Kraken, a Megamoth, Cthulu itself trying to chew my insides with only a vision of it.
Heed this free man, heed my counsel, beware of what your polymorphism may become.
Otherwise it is fine, a fine use of inheritance of something I suppose is an Architecture in diapers.
What other alternatives do I have if I want to only have a single work() method?
Single Work Method... You could try:
Policy Based Design, where a policy has the implementation of your model
A Function "work" that it is used by every single class
A Functor! Instantiated in every class that it will be used
But your inheritance seems right, a single method that everyone will be using.
One more thing....I'm just gonna leave this wiki link right here
Or maybe just copy paste the wiki C++ code... which is very similar to yours:
#include <iostream>
#include <string>
template <typename OutputPolicy, typename LanguagePolicy>
class HelloWorld : private OutputPolicy, private LanguagePolicy
{
using OutputPolicy::print;
using LanguagePolicy::message;
public:
// Behaviour method
void run() const
{
// Two policy methods
print(message());
}
};
class OutputPolicyWriteToCout
{
protected:
template<typename MessageType>
void print(MessageType const &message) const
{
std::cout << message << std::endl;
}
};
class LanguagePolicyEnglish
{
protected:
std::string message() const
{
return "Hello, World!";
}
};
class LanguagePolicyGerman
{
protected:
std::string message() const
{
return "Hallo Welt!";
}
};
int main()
{
/* Example 1 */
typedef HelloWorld<OutputPolicyWriteToCout, LanguagePolicyEnglish> HelloWorldEnglish;
HelloWorldEnglish hello_world;
hello_world.run(); // prints "Hello, World!"
/* Example 2
* Does the same, but uses another language policy */
typedef HelloWorld<OutputPolicyWriteToCout, LanguagePolicyGerman> HelloWorldGerman;
HelloWorldGerman hello_world2;
hello_world2.run(); // prints "Hallo Welt!"
}
More important questions are
How are you going to use an Int Object with your StringWorker?
You current implementation won't be able to handle that
With policies it is possible.
What are the possible objects?
Helps you define if you need this kind of behavior
And remember, don't kill a chicken with a shotgun
Maybe your model will never really change overtime.
You have committed a design error, but it is not "abuse of inheritance". Your error is that you are trying to be too generic. Meditate upon the principle of You Aren't Gonna Need It. Then, think about what you actually have. You don't have Objects, you have Dogs, Cats, and Horses. Or perhaps you have Squares, Polygons, and Lines. Or TextInEnglish and TextInArabic. Or ... the point is, you probably have a relatively small number of concrete things and they probably all go in the same superordinate category. Similarly, you do not have Workers. On the assumption that what you have is Dogs, Cats, and Horses, then you probably also have an Exerciser and a Groomer and a Veterinarian.
Think about your concrete problem in concrete terms. Implement only the classes and only the relationships that you actually need.
The point is that you're not accessing the specific functionality through the interfaces. The whole reason for using interfaces is that you want all Cars to be made, fixed and featured ... If you're not going to use them in that way, don't use interfaces (and inheritance) at all, but simply check at user input time which car was chosen and instantiate the correct specialized objects.
I've changed your code a bit so that only at "car making" time there will be an upward dynamic_cast. I would have to know all the things you want to do exactly to create interfaces I would be really happy with.
class ICar {
public:
ICar(void) {}
virtual ~ICar(void) {}
virtual void specialFeatureMethod(ICarFeatures *specialFeatures);
virtual void specialPartsMethod(ICarParts *specialParts);
virtual void specialRepairMethod(ICarParts *specialParts);
};
class FordCar : public ICar {
public:
FordCar(void) {}
~FordCar(void) {}
void specialFeatureMethod(ICarFeatures *specialFeatures) {
//Access the specialFeatures through the interface
//Do your specific Ford stuff
}
void specialPartsMethod(ICarParts *specialParts) {
//Access the specialParts through the interface
//Do your specific Ford stuff
}
void specialRepairMethod(ICarParts *specialParts) {
//Access the specialParts through the interface
//Do your specific Ford stuff
}
};
class ICarFeatures {
public:
ICarFeatures(void) {}
virtual ~ICarFeatures(void) {}
virtual void addFeature(UserInput feature) = 0;
};
class FordCarFeatures : public ICarFeatures{
public:
FordCarFeatures(void) {}
~FordCarFeatures(void) {}
void addFeature(UserInput feature){
//extract useful information out of feature, ie:
std::string name = feature.name;
int value = feature.value;
_fordFeature->specialAddFeatureMethod(name, value);
}
FordFeatureImpl* _fordFeature;
};
class ICarParts {
public:
ICarParts(void) {}
virtual ~ICarParts(void) {}
virtual void addPart(UserInput part) = 0;
};
class FordCarParts :public ICarParts{
public:
FordCarParts(void) {}
~FordCarParts(void) {}
void addPart(UserInput part) {
//extract useful information out of part, ie:
std::string name = part.name;
std::string dimensions = part.dimensions;
_fordParts->specialAddPartMethod(name, dimensions);
}
FordPartsImpl* _fordParts;
};
class ICarMaker {
public:
ICarMaker(void) {}
virtual ~ICarMaker(void) {}
virtual ICar* makeCar(ICarFeatures* features, ICarParts* parts) = 0;
};
class FordCarMaker {
public:
FordCarMaker(void) {}
~FordCarMaker(void) {}
ICar* makeCar(ICarFeatures* features, ICarParts* parts){
return customFordMakerFunction(features, parts);
}
ICar* customFordMakerFunction(ICarFeatures* features, ICarParts* parts) {
FordCar* fordCar = new FordCar;
fordCar->specialFeatureMethod(features);
fordCar->specialPartsMethod(parts);
return dynamic_cast<ICar*>(fordCar);
}
};
class ICarFixer {
public:
ICarFixer(void) {}
virtual ~ICarFixer(void) {}
virtual void fixCar(ICar* car, ICarParts* parts) = 0;
};
class FordCarFixer {
public:
FordCarFixer(void) {}
~FordCarFixer(void) {}
void fixCar(ICar* car, ICarParts* parts) {
customFordFixerFunction(car, parts);
}
void customFordFixerFunction(ICar* fordCar, ICarParts *fordParts){
fordCar->specialRepairMethod(fordParts);
}
};
One can do better (for certain values of "better"), with increased complexity.
What is actually being done here? Let's look point by point:
There's some object type, unknown statically, determined at run time from a string
There's some worker type, also unknown statically, determined at run time from another string
Hopefully the object type and the worker type will match
We can try to turn "hopefully" into "certainly" with some template code.
ObjectWorkerDispatcher* owd =
myDispatcherFactory->create("someWorker", "someObject");
owd->dispatch();
Obviously both object and worker are hidden in the dispatcher, which is completely generic:
class ObjectWorkerDispatcher {
ObjectWorkerDispatcher(string objectType, string workerType) { ... }
virtual void dispatch() = 0;
}
template <typename ObjectType>
class ConcreteObjectWorkerDispatcher : public ObjectWorkerDispatcher {
void dispatch () {
ObjectFactory<ObjectType>* of = findObjectFactory(objectTypeString);
WorkerFactory<ObjectType>* wf = findWorkerFactory(workerTypeString);
ObjectType* obj = of->create();
Worker<ObjectType>* wrk = wf->create();
wrk->doWork(obj);
}
map<string, ObjectFactory<ObjectType>*> objectFactories;
map<string, WorkerFactory<ObjectType>*> workerFactories;
ObjectFactory<ObjectType>* findObjectFactory(string) { .. use map }
WorkerFactory<ObjectType>* findWorkerFactory(string) { .. use map }
}
We have different unrelated types of Object. No common Object class, but we can have e.g. several subtypes of StringObject, all compatible with all kinds of StringWorker.
We have an abstract Worker<ObjectType> class template and concrete MyStringWorker : public Worker<StringObject> , OtherStringWorker : public Worker<StringObject> ... classes.
Both kinds of factories are inheritance-free. Different types of factories are kept completely separate (in different dispatchers) and never mix.
There's still some amount of blanks to fill in, but hopefully it all should be more or less clear.
No casts are used in making of this design. You decide whether this property alone is worth such an increase in complexity.
I think you have the right solution per your needs. One thing I see that can be improved is removing the use of carType from the function that deals with the objects at the base class level.
ICar* FordCarFixer::getFixedCar(UserInput& userInput)
{
FordCarParts* carParts = new FordPartFactory;
carParts->addPart(userInput);
FordCarFeatures* carFeatures = new FordCarFeatures;
carFeatures->addFeature(userInput);
FordCarMaker* carMaker = new FordCarMaker;
FordCar* car = carMaker->makeCar(carFeatures, carParts);
UserInput repairSpecs = getUserInput();
ForCarParts* replacementParts = new ForCarParts;
replacementParts->addPart(repairSpecs);
FordCarFixer* carFixer = new FordCarFixer;
carFixer->fixCar(car, replacementParts);
return car;
}
UserInput userInput = getUserInput();
ICar* car = CarFixerFactory::getFixedCar(userInput);
With this approach, most of the objects at FordCarFixer level are Ford-specific.
I have this code, but I don't see where I went wrong here. It seem to compile OK but I cannot access Computer or Appliance functions. Can someone please help me understand how can I make an array that holds different objects on this code example I have here?
#include <iostream>
using namespace std;
class Technics
{
private:
int price, warranty;
static int objCount;
double pvn;
char *name, *manufacturer;
public:
Technics()
{
this->objCount++;
};
Technics(int price)
{
this->objCount++;
this->price = price;
}
~Technics(){
this->objCount = this->objCount - 2;
};
static int getObjCount()
{
return objCount;
}
void setPrice(int price)
{
this->price = price;
}
int getPrice()
{
return this->price;
}
void resetCount()
{
this->objCount = 0;
}
};
int Technics::objCount = 0;
class Computer : public Technics
{
private:
int cpu, ram, psu, hdd;
public:
Computer() {}
Computer(int price)
{
this->setPrice(price);
}
void setCpu(int cpu)
{
this->cpu = cpu;
}
int getCpu()
{
return this->cpu;
}
};
class Appliance : public Technics
{
private:
int height;
int width;
char* color;
char* type;
public:
Appliance(){}
Appliance(int height, int width)
{
this->height = height;
this->width = width;
}
void setWidth(int width)
{
this->width = width;
}
int getWidth()
{
return this->width;
}
};
void main()
{
//Creating array
Technics *_t[100];
// Adding some objects
_t[0] = new Computer();
_t[1] = new Computer();
_t[2] = new Appliance();
// I can access only properties of Technics, not Computer or Appliance
_t[0]->
int x;
cin >> x;
}
The line:
_t[0] = new Computer();
Creates a computer object and stores it as a Technics base pointer in the array (i.e. for all intents and purposes while in that array, it is a Technics object).
You need to cast back to the derived class to access members that are more derived than those in Technics:
static_cast<Computer*>(_t[0])->Your_Member();
Use dyncamic cast if you don't know which derived type it is - it will return the casted pointer on success and NULL on fail so it's kind of a type-check - it has big runtime overhead though, so try to avoid it :)
EDIT in response to your closing comment:
//Calculate the length of your dynamic array.
//Allocate the dynamic array as a pointer to a pointer to Technics - this is like
//Making an array of pointers each holding some Technics heirarchy object.
Technics** baselist = new Technics*[some_length];
//Populate them the same way as before:
baselist[0] = new Computer();
baselist[1] = new Appliance();
PS: you could also use std::vector which is dynamically changeable as opposed to just created at run time - it's the best option if your allowed to use it. It saves you making your own resizable array code. Google it ;)
That's because _t is a pointer to Technics not Computer or Appliance.
Give Technics an "object type" parameter e.g. an enum that is TechnicsType.Computer for Computer and TechnicsType.Applicance for Appliance, check that and cast to the appropriate type to get the class methods.
The solution is very very simple :)
The super-class must have the virtual functions of the subclasses declared in the class definition.
For example: if the super-class computer have a sub-class called laptop that have a function int getBatteryLife();, so the computer class must have the definition virtual int getBatteryLife() to be called in the vector of pointers of the type computer.
Because _t is a Technics pointer array and, there is not possible to access to derived classes attributes. Use a Visitor Pattern like this or downcast your pointer:
// visitor pattern
class Visitor
{
void accept(Appliance &ref) { // access Appliance attributes };
void accept(Computer & ref) { // access Computer attributes };
};
class Technics
{
....
virtual void visit(Visitor &) = 0;
};
class Appliance
{
....
virtual void visit(Visitor &v) { v.accept(*this); }
};
class Computer
{
....
virtual void visit(Visitor &v) { v.accept(*this); }
};
Yes, you can only access the properties of Technics, since your variable has type Technics. You have to cast it to your Computer or Appliance class in order to execute your additional methods.
You really have to think about your design here. Is it really appropiate? Why do you have all of the objects inside the same container? Especially if you have different methods to call..this doesn't make sense..
If you really want to call different methods, you probably have to use a switch statement to decide what type you have, then call the appropiate methods (I guess you want to iterate through the whole container, or else it doesn't make sense to have a big container with different objects).
Suppose we have an abstract class Element from which classes Triangle and Quadrilateral are derived from.
Suppose yet that these classes are used in conjunction with interpolation methods that depend on the shape of the element. So, basically we create an abstract class InterpolationElement from which we derive InterpolationTriangle and InterpolationQuadrilateral.
Then, to include the interpolation functionality in the Triangle and Quadrilateral classes, we add a const-reference data member in class Element of type InterpolationElement, that is:
class Element
{
public:
Element(const InterpolationElement& interp);
const InterpolationElement& getInterpolation() const;
private:
const InterpolationElement& interpolation;
};
We then create a method (as described by Scott Meyers, Effective C++) that instanciate a local static object of class InterpolationTriangle as
const InterpolationTriangle& getInterpolationTriangle()
{
static InterpolationTriangle interpolationTriangle;
return interpolationTriangle;
}
So that class Triangle can be constructed like:
class Triangle : public Element
{
public:
Triangle() : Element( getInterpolationTriangle() ) {}
};
Here is my question: is this approach correct in order to incorporate interpolation methods on my class Element? Is this used in professional scenarios?
I could implement directly all the interpolation methods on class Element (as pure virtual) and the override them in the derived classes Triangle and Quadrilateral. However, this approach seems to me to be cumbersome, since every time I need to improve or implement new interpolation functionalities I would have to do that on these classes. Moreover, the classes get bigger and bigger (many methods) using this approach.
I would like to hear from you some tips and comments
Thanks in advance.
Additional details:
class InterpolationElement
{
public:
InterpolationElement();
virtual double interpolationMethod1(...) = 0;
:
virtual double interpolationMethodN(...) = 0;
}
class InterpolationTriangle : public InterpolationElement
{
public:
InterpolationTriangle () {}
virtual double interpolationMethod1(...) { // interpolation for triangle }
:
virtual double interpolationMethodN(...) { // interpolation for triangle }
}
class InterpolationQuadrilateral : public InterpolationElement
{
public:
InterpolationTriangle () {}
virtual double interpolationMethod1(...) { // interpolation for quadrilateral}
:
virtual double interpolationMethod1(...) { // interpolation for quadrilateral}
}
The classes are used in conjunction with interpolation methods. Why do those methods need to be in a singleton object? The singleton here looks very problematic.
class Element
{
public:
virtual double interpolationMethod1(...) = 0;
:
virtual double interpolationMethodN(...) = 0;
};
class Triangle : public Element
{
public:
virtual double interpolationMethod1(...) { // interpolation for triangle }
:
virtual double interpolationMethodN(...) { // interpolation for triangle }
}
Also, welcome to SO!
This is reminiscent of a question that I had answered here. The same idea about the separation of data containers and the strategies.
There is one little issue with your proposal: you have added an interpolation related method to your base class and you've changed the constructor...
So first of all, if you wish to do it this way, here is how you should do it:
class Element
{
public:
private:
// similar signature to a `clone` method
virtual InterpolationElement* interpolation() const = 0;
};
class Triangle
{
public:
private:
virtual InterpolationTriangle* interpolation() const
{
return new InterpolationTriangle();
}
};
There are 2 advantages here:
It's no longer necessary to change the constructor of each of the derived objects
The strategy object is no longer const, which allows it to maintain state during the computation... like a reference to the current object being interpolated.
However, this still requires to change the Element class, and each of its derived classes. Doesn't it bother you ;) ?
Well, it's time (for once) to call upon a Design Pattern: Visitor.
It's a little different from the strategy idea, relying on double dispatch to work properly. However it allows you to tweak the hierarchy of Elements ONCE (with an accept method) and then to add as many operations as you wish. And that is great.
You can always mess a little bit with templates.
First we have a top class.
class Element {
public:
virtual void calculate() const = 0;
};
... but then we also have a class in the middle of the hierarchy which is actually a template. Template can't be the top level class, as templates with different parameters are different classes. The idea is that we give an interpolation class as a type parameter to the element.
template <typename Interpolation>
class Element_Impl : public Element {
protected:
Interpolation m_interpolation;
};
And interpolation classes. Notice, they aren't siblings, because they don't need to.
class InterpolationTriangle {
public:
double interpolate(double a, double b) const {
std::cout << "interpolation triangle" << std::endl;
}
};
class InterpolationQuadrilateral {
public:
double interpolate(double a, double b) const {
std::cout << "interpolation quadrilateral" << std::endl;
}
};
And finally the real elements and the small main procedure.
class Triangle : public Element_Impl<InterpolationTriangle> {
public:
void calculate() const {
m_interpolation.interpolate(1.0, 2.0);
}
};
class Quadrilateral : public Element_Impl<InterpolationQuadrilateral> {
public:
void calculate() const {
m_interpolation.interpolate(2.0, 3.0);
}
};
int main() {
const Element &a = Triangle();
const Element &b = Quadrilateral();
a.calculate();
b.calculate();
}
Summary:
you can easily switch interpolation class for each element if needed.
there aren't double vtable access (first for Element's calculate and then for InterpolationElement's intepolate methods) as in the Matthieu's example. Each element knows at compile time which interpolation class it is using.
Element_Impl is an ugly bit, but it saves us from copypasta. You can expand it even further by implementing interpolation method wrappers
http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
One way is to use static methods, and defining a wrapper in Element_Impl - still only in one place.
class Element {
public:
virtual void calculate() const = 0;
};
template <typename Interpolation>
class Element_Impl : public Element {
protected:
void interpolate(double, double) const {
Interpolation::interpolate(1, 1);
}
};
class InterpolationTriangle {
public:
static double interpolate(double a, double b) {
std::cout << "interpolation triangle" << std::endl;
}
};
class InterpolationQuadrilateral {
public:
static double interpolate(double a, double b) {
std::cout << "interpolation quadrilateral" << std::endl;
}
};
class Triangle : public Element_Impl<InterpolationTriangle> {
public:
void calculate() const {
interpolate(1.0, 2.0);
}
};
class Quadrilateral : public Element_Impl<InterpolationQuadrilateral> {
public:
void calculate() const {
interpolate(2.0, 3.0);
}
};
int main() {
const Element &a = Triangle();
const Element &b = Quadrilateral();
a.calculate();
b.calculate();
}
What first comes to my mind is the GoF Design Pattern Visitor
From what I understand of your problem, this pattern is conceived to exactly solve this issue.
Each Visitor object defines an interpolation technique, or an algorithm to apply to your object.
Thus the Element class doesn't grow at all with each new functionnality. Once in place, the Visitor pattern enables to enrich functionnality without touching to the Base class definition.
OK, I have a somewhat complicated system in C++. In a nutshell, I need to add a method to a third party abstract base class. The third party also provides a ton of derived classes that also need the new functionality.
I'm using a library that provides a standard Shape interface, as well as some common shapes.
class Shape
{
public:
Shape(position);
virtual ~Shape();
virtual position GetPosition() const;
virtual void SetPosition(position);
virtual double GetPerimeter() const = 0;
private: ...
};
class Square : public Shape
{
public:
Square(position, side_length);
...
};
class Circle, Rectangle, Hexagon, etc
Now, here's my problem. I want the Shape class to also include a GetArea() function. So it seems like I should just do a:
class ImprovedShape : public virtual Shape
{
virtual double GetArea() const = 0;
};
class ImprovedSquare : public Square, public ImprovedShape
{
...
}
And then I go and make an ImprovedSquare that inherits from ImprovedShape and Square. Well, as you can see, I have now created the dreaded diamond inheritance problem. This would easily be fixed if the third party library used virtual inheritance for their Square, Circle, etc. However, getting them to do that isn't a reasonable option.
So, what do you do when you need to add a little functionality to an interface defined in a library? Is there a good answer?
Thanks!
Why does this class need to derive from shape?
class ImprovedShape : public virtual Shape
{
virtual double GetArea() const = 0;
};
Why not just have
class ThingWithArea
{
virtual double GetArea() const = 0;
};
ImprovedSquare is a Shape and is a ThingWithArea
We had a very similar problem in a project and we solved it by just NOT deriving ImprovedShape from Shape. If you need Shape functionality in ImprovedShape you can dynamic_cast, knowing that your cast will always work. And the rest is just like in your example.
I suppose the facade pattern should do the trick.
Wrap the 3rd party interface into an interface of your own, and your application's code works with the wrapper interface rather than the 3rd party interface. That way you've nicely insulated changes in the uncontrolled 3rd party interface as well.
Perhaps you should read up on proper inheritance, and conclude that ImprovedShape does not need to inherit from Shape but instead can use Shape for its drawing functionality, similar to the discussion in point 21.12 on that FAQ on how a SortedList doesn't have to inherit from List even if it wants to provide the same functionality, it can simply use a List.
In a similar fashion, ImprovedShape can use a Shape to do it's Shape things.
Possibly a use for the decorator pattern? [http://en.wikipedia.org/wiki/Decorator_pattern][1]
Is it possible to do a completely different approach - using templates and meta-programming techniques? If you're not constrained to not using templates, this could provide an elegant solution. Only ImprovedShape and ImprovedSquare change:
template <typename ShapePolicy>
class ImprovedShape : public ShapePolicy
{
public:
virtual double GetArea();
ImprovedShape(void);
virtual ~ImprovedShape(void);
protected:
ShapePolicy shape;
//...
};
and the ImprovedSquare becomes:
class ImprovedSquare : public ImprovedShape<Square>
{
public:
ImprovedSquare(void);
~ImprovedSquare(void);
// ...
};
You'll avoid the diamond inheritance, getting both the inheritance from your original Shape (through the policy class) as well as the added functionality you want.
Another take on meta-programming/mixin, this time a bit influenced by traits.
It assumes that calculating area is something you want to add based on exposed properties; you could do something which kept with encapsulation, it that is a goal, rather than modularisation. But then you have to write a GetArea for every sub-type, rather than using a polymorphic one where possible. Whether that's worthwhile depends on how committed you are to encapsulation, and whether there are base classes in your library you could exploit common behaviour of, like RectangularShape below
#import <iostream>
using namespace std;
// base types
class Shape {
public:
Shape () {}
virtual ~Shape () { }
virtual void DoShapyStuff () const = 0;
};
class RectangularShape : public Shape {
public:
RectangularShape () { }
virtual double GetHeight () const = 0 ;
virtual double GetWidth () const = 0 ;
};
class Square : public RectangularShape {
public:
Square () { }
virtual void DoShapyStuff () const
{
cout << "I\'m a square." << endl;
}
virtual double GetHeight () const { return 10.0; }
virtual double GetWidth () const { return 10.0; }
};
class Rect : public RectangularShape {
public:
Rect () { }
virtual void DoShapyStuff () const
{
cout << "I\'m a rectangle." << endl;
}
virtual double GetHeight () const { return 9.0; }
virtual double GetWidth () const { return 16.0; }
};
// extension has a cast to Shape rather than extending Shape
class HasArea {
public:
virtual double GetArea () const = 0;
virtual Shape& AsShape () = 0;
virtual const Shape& AsShape () const = 0;
operator Shape& ()
{
return AsShape();
}
operator const Shape& () const
{
return AsShape();
}
};
template<class S> struct AreaOf { };
// you have to have the declaration before the ShapeWithArea
// template if you want to use polymorphic behaviour, which
// is a bit clunky
static double GetArea (const RectangularShape& shape)
{
return shape.GetWidth() * shape.GetHeight();
}
template <class S>
class ShapeWithArea : public S, public HasArea {
public:
virtual double GetArea () const
{
return ::GetArea(*this);
}
virtual Shape& AsShape () { return *this; }
virtual const Shape& AsShape () const { return *this; }
};
// don't have to write two implementations of GetArea
// as we use the GetArea for the super type
typedef ShapeWithArea<Square> ImprovedSquare;
typedef ShapeWithArea<Rect> ImprovedRect;
void Demo (const HasArea& hasArea)
{
const Shape& shape(hasArea);
shape.DoShapyStuff();
cout << "Area = " << hasArea.GetArea() << endl;
}
int main ()
{
ImprovedSquare square;
ImprovedRect rect;
Demo(square);
Demo(rect);
return 0;
}
Dave Hillier's approach is the right one. Separate GetArea() into its own interface:
class ThingWithArea
{
public:
virtual double GetArea() const = 0;
};
If the designers of Shape had done the right thing and made it a pure interface,
and the public interfaces of the concrete classes were powerful enough, you could
have instances of concrete classes as members. This is how you get SquareWithArea
(ImprovedSquare is a poor name) being a Shape and a ThingWithArea:
class SquareWithArea : public Shape, public ThingWithArea
{
public:
double GetPerimeter() const { return square.GetPerimeter(); }
double GetArea() const { /* do stuff with square */ }
private:
Square square;
};
Unfortunately, the Shape designers put some implementation into Shape, and you
would end up carrying two copies of it per SquareWithArea, just like in
the diamond you originally proposed.
This pretty much forces you into the most tightly coupled, and therefore least
desirable, solution:
class SquareWithArea : public Square, public ThingWithArea
{
};
These days, it's considered bad form to derive from concrete classes in C++.
It's hard to find a really good explanation why you shouldn't. Usually, people
cite Meyers's More Effective C++ Item 33, which points out the impossibility
of writing a decent operator=() among other things. Probably, then, you should
never do it for classes with value semantics. Another pitfall is where the
concrete class doesn't have a virtual destructor (this is why you should
never publicly derive from STL containers). Neither applies here. The poster
who condescendingly sent you to the C++ faq to learn about inheritance is
wrong - adding GetArea() does not violate Liskov substitutability. About
the only risk I can see comes from overriding virtual functions in the
concrete classes, when the implementer later changes the name and silently breaks
your code.
In summary, I think you can derive from Square with a clear conscience.
(As a consolation, you won't have to write all the forwarding functions for
the Shape interface).
Now for the problem of functions which need both interfaces. I don't like
unnecessary dynamic_casts. Instead, make the function take references to
both interfaces and pass references to the same object for both at the call site:
void PrintPerimeterAndArea(const Shape& s, const ThingWithArea& a)
{
cout << s.GetPerimeter() << endl;
cout << a.GetArea() << endl;
}
// ...
SquareWithArea swa;
PrintPerimeterAndArea(swa, swa);
All PrintPerimeterAndArea() needs to do its job is a source of perimeter and a
source of area. It is not its concern that these happen to be implemented
as member functions on the same object instance. Conceivably, the area could
be supplied by some numerical integration engine between it and the Shape.
This gets us to the only case where I would consider passing in one reference
and getting the other by dynamic_cast - where it's important that the two
references are to the same object instance. Here's a very contrived example:
void hardcopy(const Shape& s, const ThingWithArea& a)
{
Printer p;
if (p.HasEnoughInk(a.GetArea()))
{
s.print(p);
}
}
Even then, I would probably prefer to send in two references rather than
dynamic_cast. I would rely on a sane overall system design to eliminate the
possibility of bits of two different instances being fed to functions like this.
GetArea() need not be a member. It could be templated function, so that you can invoke it for any Shape.
Something like:
template <class ShapeType, class AreaFunctor>
int GetArea(const ShapeType& shape, AreaFunctor func);
The STL min, max functions can be thought of as an analogy for your case. You can find a min and max for an array/vector of objects given a comparator function. Like wise, you can derive the area of any given shape provided the function to compute the area.
There exists a solution to your problem, as I understood the question. Use the addapter-pattern. The adapter pattern is used to add functionality to a specific class or to exchange particular behaviour (i.e. methods). Considering the scenario you painted:
class ShapeWithArea : public Shape
{
protected:
Shape* shape_;
public:
virtual ~ShapeWithArea();
virtual position GetPosition() const { return shape_->GetPosition(); }
virtual void SetPosition(position) { shape_->SetPosition(); }
virtual double GetPerimeter() const { return shape_->GetPerimeter(); }
ShapeWithArea (Shape* shape) : shape_(shape) {}
virtual double getArea (void) const = 0;
};
The Adapter-Pattern is meant to adapt the behaviour or functionality of a class. You can use it to
change the behaviour of a class, by not forwarding but reimplementing methods.
add behaviour to a class, by adding methods.
How does it change behaviour? When you supply an object of type base to a method, you can also supply the adapted class. The object will behave as you instructed it to, the actor on the object will only care about the interface of the base class. You can apply this adaptor to any derivate of Shape.