Given following problem:
class Instrument {
};
class Guitar : public Instrument {
public:
void doGuitar() const;
};
class Piano : public Instrument {
public:
void doPiano() const;
};
I got a list of pointers to Instrument
list<shared_ptr<Instrument>> instruments;
in which i add instruments via (for example)
Guitar myGuitar;
instruments.push_back(make_shared<Guitar>(myGuitar));
Now, I want to iterate over the list instruments and call doPiano() iff the current instrument is a piano and doGuitar() iff it is a guitar. These two functions differ a lot and thus, cannot be made abstract in class Instrument.
The problem is that C++ won't be able to identify the type of the Instrument by runtime, isn't it (due to single dispatch)? How can I achieve that it calls the piano- or the guitar-function depending on the current type the iterator is pointing at.
I would be happy if I could implement sth. working like this pseudo-code:
list<shared_ptr<Instrument>>::const_iterator it;
if ("current type == Guitar")
(*it)->doGuitar();
else if ("current type == Piano")
(*it)->doPiano();
Result
Actually, I ran into several problems with my approach. I did much refactoring using this post: How does one downcast a std::shared_ptr? . Thanks to all for your help :)
The design can probably be improved to eliminate this problem, but working within the existing design you can add a virtual member function Instrument::play_it that takes a Player as polymorphic argument. In Player have two functions play_guitar (taking guitar argument) and play_piano (taking piano argument). In guitar class override play_it to call Player::play_guitar with self as argument. In piano class override play_it to call Player::play_piano with self as argument. Look ma no casts.
This isn't exactly multiple dispatch, it's known as the visitor pattern. However it's perhaps best to not focus too much on that, lest you start to name things visitor or such non-descriptive folly.
A double dispatch works like this (pseudocode, important but trivial stuff omitted):
struct InstrumentVisitor{
// knows all instruments
virtual void doGuitar(Guitar*) = 0;
virtual void doPiano(Piano*) = 0;
};
class Instrument {
virtual void doInstrument(InstrumentVisitor*) = 0;
...
};
class Piano : public Instrument {
void doInstrument (InstrumentVisitor* v) {
v->doPiano(this);
};
class Guitar : public Instrument {
void doInstrument (InstrumentVisitor* v) {
v->doGuitar(this);
};
Now we can devise concrete visitors.
struct Player : InstrumentVisitor {
// does vastly different things for guitar and piano
void doGuitar (Guitar* g) {
g->Strum(pick, A6);
}
void doPiano (Piano* p) {
p->Scale (Am, ascending);
};
Type erasure is another option:
std::vector<std::function<void()>> playInstrument;
playInstrument.emplace_back([g = Guitar{}]() { return g.doGuitar(); });
playInstrument.emplace_back([p = Piano{} ]() { return p.doPiano(); });
playInstrument[0]();
For this you even don't need a common base class.
One way to identify classes at runtime is to use dynamic_cast. But to use that, you need to have atleast one virtual method in your class. An empty virtual method can be added to the instrument class for that purpose.
class Instrument {
private:
virtual void emptyMethod_doNotCall() {} // Add this method.
};
class Guitar : public Instrument {
public:
void doGuitar() const;
};
class Piano : public Instrument {
public:
void doPiano() const;
};
The object type can be checked by doing a dynamic_cast to the target class pointer. dynamic_cast returns NULL if the object cannot be cast to the desired target class.
list<shared_ptr<Instrument>>::const_iterator it;
if (dynamic_cast<Guitar*>(it) != NULL)
(*it)->doGuitar();
else if (dynamic_cast<Piano*>(it) != NULL)
(*it)->doPiano();
Related
Consider I have a Plant class that has derived Fruit and Vegetable classes, and Fruit class has some more derived classes, like Orange and Apple, while Vegetable has derived Potato and Tomato. Assume, Plant has Plant::onConsume()=0; method:
class Plant
{
public:
virtual void onConsume(void)=0;
};
class Fruit:public Plant
{
};
class Orange:public Fruit
{
void onConsume(void)
{
// Do something specific here
}
};
class Apple:public Fruit
{
void onConsume(void)
{
// Do something specific here
}
};
class Vegetable:public Plant
{
};
class Potato:public Vegetable
{
void onConsume(void)
{
// Do something specific here
}
};
class Tomato:public Vegetable
{
void onConsume(void)
{
// Do something specific here
}
};
class Consumer
{
public:
void consume(Plant &p)
{
p.onConsume();
// Specific actions depending on actual p type here
// like send REST command to the remote host for Orange
// or draw a red square on the screen for Tomato
}
};
Suppose, I have a Consumer class with Consumer::consume(Plant) method. This "consume" method should perform different actions for different "Plants" instances/types, among calling Plant::onConsume() for any of "Plants". These action ain't directly related to the Plant class, require a lot of different additional actions and parameters, could literally be completely arbitrary, so cannot be implemented inside onConsume method.
What is the preferred method to implement this? As I understand, it is possible to implement some "Plant::getPlantType()=0" method, that would return plant type, but in this case I'm not sure what should it return. In case the returned value would be an enum, I'd need to change this enum each time I add a new derived class. And in any case, there's no control that multiple derived classes could return the same value.
Also, I'm aware there's a dynamic_cast conversion that returns nullptr if conversion could not be made, and typeid() operator that returns std::typeinfo (even with typeinfo::name()), which could be used in the switch() (it's just great for my case). But I'm afraid it could significally slow down the execution and make code heavier.
So, my question is, what is the preferred way in C++ to do that? maybe I just forgot about some simpler way to implement that?
A little update. Thank you for your explanations about inheritance, encapsulation etc! I supposed it's clear from my question, but it is not, I am sorry about that. So, please think about it, like I don't have an access to the whole Plant sources hierarchy, just need to implement this Consumer::onConsume(Plant). So I cannot add new specific methods in it. Or, also, it could be considered as a Plants library, that I have to write once, and make it usable for other devs. So, I could divide use cases/functionality into two parts: one that implemented "per class" in the Plant::onConsume() method, and second that is unknown yet and will differ depending on usage.
One option would be the visitor pattern, but this requires one function per type in some class. Basically you create a base class PlantVisitor with one Visit function per object type and pass add a virtual method to Plant that receives a PlantVisitor object and calls the corresponding function of the visitor passing itself as parameter:
class PlantVisitor
{
public:
virtual void Visit(Orange& orange) = 0;
virtual void Visit(Tomato& tomato) = 0;
...
};
class Plant
{
public:
virtual void Accept(PlantVisitor& visitor) = 0;
};
class Orange : public Plant
{
public:
void Accept(PlantVisitor& visitor) override
{
visitor.Visit(*this);
}
};
class Tomato : public Plant
{
public:
void Accept(PlantVisitor& visitor) override
{
visitor.Visit(*this);
}
};
This would allow you to do something like this:
class TypePrintVisitor : public PlantVisitor
{
public:
void Visit(Orange& orange) override
{
std::cout << "Orange\n";
}
void Visit(Tomato& tomato) override
{
std::cout << "Tomato\n";
}
};
std::vector<std::unique_ptr<Plant>> plants;
plants.emplace_back(std::make_unique<Orange>());
plants.emplace_back(std::make_unique<Tomato>());
TypePrintVisitor visitor;
for (size_t i = 0; i != plants.size(); ++i)
{
std::cout << "plant " << (i+1) << " is a ";
plants[i]->Accept(visitor);
}
Not sure the need for this does not indicate a design inefficiency though.
Btw: If you've got multiple visitors and do not necessarily want to implement logic for every single type in all of them, you could add default implementations in PlantVisitor that call the function for the supertype instead of specifying pure virtual functions.
Polymorphism is all about not having to know about a specific type. Usually your design is flawed if you discover having to detect a specific type explicitly.
At very first:
void Consumer::consume(Plant p)
does not work as intended! The Plant object is accepted by value, i. e. its bytes are copied one by one; however, only those of the Plant type, any others (those of derived types) are ignored and get lost within consume function – this is called object slicing.
Polymorphism only works with references or pointers.
Now assume you want to do something like the following (incomplete code!):
void Consumer::consume(Plant& p) // must be reference or pointer!
{
p.onConsume();
generalCode1();
if(/* p is apple */)
{
appleSpecific();
}
else if(/* p is orange */)
{
orangeSpecific();
}
generalCode2();
}
You don't want to decide yourself upon type, you let the Plant class do the stuff for you, which means you extend its interface appropriately:
class Plant
{
public:
virtual void onConsume() = 0;
virtual void specific() = 0;
};
The code of the consume function will now be changed to:
void Consumer::consume(Plant const& p) // must be reference or pointer!
{
p.onConsume();
generalCode1();
p.specific();
generalCode2();
}
You'll do so at any place you need specific behaviour (and specific is just a demo name, chose one that describes nicely what the function actually is intended to do).
p.onConsume();
generalCode1();
p.specific1();
generalCode2();
p.specific2();
generalCode3();
p.specific3();
generalCode4();
// ...
Of course you need now to provide appropriate implementations in your derived classes:
class Orange:public Fruit
{
void onConsume() override
{ }
void specific() override
{
orangeSpecific();
}
};
class Apple:public Fruit
{
void onConsume() override
{ }
void specific() override
{
appleSpecific();
}
};
Note the addition of override keyword, which protects you from accidentally creating overloaded functions instead actually overwriting in case of signature mismatch. It helps you, too, to locate all places of necessary changes if you discover having to change the function signature in the base class.
I'm trying to work with my own little framework(only for interest actually:) ) recently. I'd like to abstract an interface to describe a process with an input and an output. So I defined a class BaseInput and a class Baseoutput. And here is the interface.
class IProcess
{
public:
virtual void Proc(BaseInput &input) = 0;
};
And here is my problem. The classes derived from IProcess have to make BaseInput as its argument's type according to the standard of C++. I expect the subclasses could be like this(I know it's wrong):
class ProcessA : public IProcess
{
public:
void Proc(MyInput &input) override;
};
I know this couldn't compile correctly. And I also know It could convert to a MyInput pointer in ProcessA::Proc. I considered Dependency Injection but I don't know whether it could solve my problem.
How do you guys solve such kinds of problems in the real project?
P.S. I actually found a similar situation here
Edit 1:
Well, I'm extremely sorry for my wrong expression. And thanks for all you guys' helping. MyInput actually brings some data like this:
class MyInput : public BaseInput
{
public:
//... some functions
std::vector<int> m_Data;
};
AKA the argument's type should be MyInput& if I need to access m_Data from proc without any pointer converting. What I want to achieve is architecture just like .NET Core MVC. An input worker class accept different input(from files, internet or serial ports...), pack them into classes derived from BaseInput and give it to some process classes derived from IProcess (maybe there also could be some middleware) and finally return a result packaged by output classes derived from BaseOutput.
It might be a really dumb architecture. I'm also wondering how to make it better. I had also considered not to pack those input. But I don't know how :-x
Thanks to all you guys again.🙏
Actually, MyInput must inherits BaseInput. Then, ProcessA::Proc must have the same protoype than the IProcess::proc that it is supposed to override (So it must take a BaseInput as parameter too).
By using polymorphism, you will be able to pass a MyInput when calling the function since you passed a reference and that MyInput inherits BaseInput.
Here is an example:
.h:
class BaseInput
{
public:
virtual ~BaseInput();
virtual void display();
};
class MyInput : public BaseInput
{
public:
void display() override;
};
class IProcess
{
public:
virtual ~IProcess();
virtual void proc(BaseInput & input) = 0;
};
class ProcessA : public IProcess
{
public:
void proc(BaseInput & input) override;
};
.cpp:
BaseInput::~BaseInput()
{}
void BaseInput::display()
{
std::cout << "BaseInput::display()" << std::endl;
}
void MyInput::display()
{
std::cout << "MyInput::display()" << std::endl;
}
IProcess::~IProcess()
{}
void ProcessA::proc(BaseInput & input)
{
input.display();
}
main:
int main()
{
MyInput mi;
ProcessA pa;
pa.proc(mi); // Pass a MyInput
return 0;
}
The output is (as expected):
MyInput::display()
EDIT (answer to question's edit 1):
You have two solutions.
Either you defines in BaseInput the required methods to be implemented by every input type (as I did with display()). In your case, it could be a getData() member for example.
Or you will have to dynamic_cast your given BaseInput & into a MyInput &.
Keep in mind that if dynamic_cast fails with pointers, it returns a nullptr, but if it fails with references, it will throw a std::bad_cast exception.
As you use references, you will have to catch the exception in case of potential fail (if another type of input is given).
I'm relatively new to C++ and I'm right now facing a point in my design where I cannot seem to avoid downcasting. I know this is usually a sign of bad design, so I would like to know what would be a better way to do this.
I have a class Frame that represents geometrical frame trees and allows geometrical transformations between them:
class Frame
{
private:
Frame *_parent;
std::vector<Frame*> _children;
public:
Frame* getParent() const;
std::vector<Frame*> getChildren() const;
... (extra methods for geometrical transformations)
}
I want now to create a new Frame subclass, MechanicalFrame, that adds some functionality to deal with dynamical properties.
class MechanicalFrame
{
private:
double mass;
...
public:
void compute();
}
My problem is that, the "compute" method needs to implement some recursive logic, so it would contain something like this:
MechanicalFrame::compute()
{
for element in getChildren():
element.compute();
}
However, since getChildren returns a vector of Frame* and not MechanicalFrame*, I would need to make a static_cast at this point. I've given the problem a lot of thought, but none of the solutions I've found are fully satisfying to me:
Solution 1) Static cast: somehow it indicates bad design
Solution 2) Add the compute method to the base class (Frame) with a dummy implementation, i.e., throwing an exception: it seems unnatural to force the implementation of the parent class based on the derived class.
Solution 3) Split totally MechanicalFrame from Frame: this would mean reimplementing many of the functionalities already available in Frame.
Any help would be very appreciated.
Many thanks in advance :)
Use polymorphic behaviour, use your Solution 2)
You can follow below pattern (Interface -> Base class -> Derived class)
class IFrame
{
public:
virtual void compute()=0;
}
class Frame:public IFrame
{
public:
virtual void compute() {/*nothing to do*/}
}
class MechanicalFrame:public Frame
{
public:
virtual void compute() {/*your implementation with mass*/}
}
If you are sure that all the Frame* pointers in MechanicalFrame::getChildren() are pointing to MechanicalFrame instances, I don't see any problem with static_cast. Make sure you use dynamic_cast + assert in debug builds to catch mistakes.
void MechanicalFrame::compute()
{
for(auto frame_ptr : getChildren())
{
downcast<MechanicalFrame*>(frame_ptr)->compute();
}
}
Where downcast is something like:
template <typename TOut, typename T>
auto downcast(T* ptr)
{
static_assert(std::is_base_of<T, TOut>{});
assert(ptr != nullptr);
assert(dynamic_cast<TOut>(ptr) == ptr);
return static_cast<TOut>(ptr);
}
(For a more thorough implementation of downcast see my Meeting C++ 2015 lightning talk "Meaningful casts" or my current implementation in vrm_core.)
Notice that there's a performance advantage here, as you avoid virtual dispatch. Play around with this snippet on gcc.godbolt.org to see differences in the generated assembly.
Another option is to use the Visitor pattern:
class Frame;
class MechanicalFrame;
class FrameVisitor
{
public:
virtual ~FrameVisitor() = default;
virtual void visit(Frame&) = 0;
virtual void visit(MechanicalFrame&) = 0;
};
class Frame
{
public:
virtual void accept(FrameVisitor& visitor)
{
visitor.visit(*this);
}
void acceptRecursive(FrameVisitor& visitor)
{
accept(visitor);
for (Frame* child : getChildren())
{
child->acceptRecursive(visitor);
}
}
...
};
class MechanicalFrame : public Frame
{
public:
virtual void accept(FrameVisitor& visitor) override
{
visitor.visit(*this);
}
...
};
Then the client code will be:
class ConcreteVisitor : public FrameVisitor
{
public:
virtual void visit(Frame& frame) override
{
// Deal with Frame (not a subclass) object.
}
virtual void visit(MechanicalFrame& frame) override
{
// Deal with MechanicalFrame object.
}
};
Frame root = ...;
ConcreteVisitor visitor;
root.acceptRecursive(visitor);
In general, the Visitor pattern allows you to traverse a hierarchy of heterogeneous objects and perform operations on them without type casting. It's most useful when the number of operations is expected to grow while your type hierarchy is more or less stable.
Since you're asking for new ideas, I will not explain in detail anything you written about in solutions 1-3.
You could add extra functionality to the MechanicalFrame class, splitting its children of MechanicalFrame class and all other classes, like this:
class Frame {
public:
std::vector<Frame*> getChildren(); // returns children
void addChild(Frame* child); // adds child to children
private:
std::vector<Frame*> children;
}
class MechanicalFrame : public Frame {
public:
void compute();
std::vector<MechanicalFrame*> getMechanicalChildren(); // returns mechanical_children
void addChild(MechanicalFrame* child); // adds child to mechanical_children
private:
std::vector<MechanicalFrame*> mechanical_children;
}
One possible implementation of compute is the following:
void MechanicalFrame::compute() {
...
for (auto* child : getMechanicalChildren()) {
child->compute();
}
}
UP: As far as I understand, one of the problems with casts is that the code starts behaving very differently depending on the actual class of the object, and we cannot substitute the parent class object with child class (see Liskov principle). The approach described in this answer actually changes the principle of using the "mechanicity" of your Frames, allowing adding MechanicalFrame children in such a way that they're ignored in compute method.
Let's say I have a parent class, Arbitrary, and two child classes, Foo and Bar. I'm trying to implement a function to insert any Arbitrary object into a database, however, since the child classes contain data specific to those classes, I need to perform slightly different operations depending on the type.
Coming into C++ from Java/C#, my first instinct was to have a function that takes the parent as the parameter use something like instanceof and some if statements to handle child-class-specific behavior.
Pseudocode:
void someClass(Arbitrary obj){
obj.doSomething(); //a member function from the parent class
//more operations based on parent class
if(obj instanceof Foo){
//do Foo specific stuff
}
if(obj instanceof Bar){
//do Bar specific stuff
}
}
However, after looking into how to implement this in C++, the general consensus seemed to be that this is poor design.
If you have to use instanceof, there is, in most cases, something wrong with your design. – mslot
I considered the possibility of overloading the function with each type, but that would seemingly lead to code duplication. And, I would still end up needing to handle the child-specific behavior in the parent class, so that wouldn't solve the problem anyway.
So, my question is, what's the better way of performing operations that where all parent and child classes should be accepted as input, but in which behavior is dictated by the object type?
First, you want to take your Arbitrary by pointer or reference, otherwise you will slice off the derived class. Next, sounds like a case of a virtual method.
void someClass(Arbitrary* obj) {
obj->insertIntoDB();
}
where:
class Arbitrary {
public:
virtual ~Arbitrary();
virtual void insertIntoDB() = 0;
};
So that the subclasses can provide specific overrides:
class Foo : public Arbitrary {
public:
void insertIntoDB() override
// ^^^ if C++11
{
// do Foo-specific insertion here
}
};
Now there might be some common functionality in this insertion between Foo and Bar... so you should put that as a protected method in Arbitrary. protected so that both Foo and Bar have access to it but someClass() doesn't.
In my opinion, if at any place you need to write
if( is_instance_of(Derived1) )
//do something
else if ( is_instance_of(Derived2) )
//do somthing else
...
then it's as sign of bad design. First and most straight forward issue is that of "Maintainence". You have to take care in case further derivation happens. However, sometimes it's necessary. for e.g if your all classes are part of some library. In other cases you should avoid this coding as far as possible.
Most often you can remove the need to check for specific instance by introducing some new classes in the hierarchy. For e.g :-
class BankAccount {};
class SavingAccount : public BankAccount { void creditInterest(); };
class CheckingAccount : public BankAccount { void creditInterest(): };
In this case, there seems to be a need for if/else statement to check for actual object as there is no corresponsing creditInterest() in BanAccount class. However, indroducing a new class could obviate the need for that checking.
class BankAccount {};
class InterestBearingAccount : public BankAccount { void creditInterest(): } {};
class SavingAccount : public InterestBearingAccount { void creditInterest(): };
class CheckingAccount : public InterestBearingAccount { void creditInterest(): };
The issue here is that this will arguably violate SOLID design principles, given that any extension in the number of mapped classes would require new branches in the if statement, otherwise the existing dispatch method will fail (it won't work with any subclass, just those it knows about).
What you are describing looks well suited to inheritance polymorphicism - each of Arbitrary (base), Foo and Bar can take on the concerns of its own fields.
There is likely to be some common database plumbing which can be DRY'd up the base method.
class Arbitrary { // Your base class
protected:
virtual void mapFields(DbCommand& dbCommand) {
// Map the base fields here
}
public:
void saveToDatabase() { // External caller invokes this on any subclass
openConnection();
DbCommand& command = createDbCommand();
mapFields(command); // Polymorphic call
executeDbTransaction(command);
}
}
class Foo : public Arbitrary {
protected: // Hide implementation external parties
virtual void mapFields(DbCommand& dbCommand) {
Arbitrary::mapFields();
// Map Foo specific fields here
}
}
class Bar : public Arbitrary {
protected:
virtual void mapFields(DbCommand& dbCommand) {
Arbitrary::mapFields();
// Map Bar specific fields here
}
}
If the base class, Arbitrary itself cannot exist in isolation, it should also be marked as abstract.
As StuartLC pointed out, the current design violates the SOLID principles. However, both his answer and Barry's answer has strong coupling with the database, which I do not like (should Arbitrary really need to know about the database?). I would suggest that you make some additional abstraction, and make the database operations independent of the the data types.
One possible implementation may be like:
class Arbitrary {
public:
virtual std::string serialize();
static Arbitrary* deserialize();
};
Your database-related would be like (please notice that the parameter form Arbitrary obj is wrong and can truncate the object):
void someMethod(const Arbitrary& obj)
{
// ...
db.insert(obj.serialize());
}
You can retrieve the string from the database later and deserialize into a suitable object.
So, my question is, what's the better way of performing operations
that where all parent and child classes should be accepted as input,
but in which behavior is dictated by the object type?
You can use Visitor pattern.
#include <iostream>
using namespace std;
class Arbitrary;
class Foo;
class Bar;
class ArbitraryVisitor
{
public:
virtual void visitParent(Arbitrary& m) {};
virtual void visitFoo(Foo& vm) {};
virtual void visitBar(Bar& vm) {};
};
class Arbitrary
{
public:
virtual void DoSomething()
{
cout<<"do Parent specific stuff"<<endl;
}
virtual void accept(ArbitraryVisitor& v)
{
v.visitParent(*this);
}
};
class Foo: public Arbitrary
{
public:
virtual void DoSomething()
{
cout<<"do Foo specific stuff"<<endl;
}
virtual void accept(ArbitraryVisitor& v)
{
v.visitFoo(*this);
}
};
class Bar: public Arbitrary
{
public:
virtual void DoSomething()
{
cout<<"do Bar specific stuff"<<endl;
}
virtual void accept(ArbitraryVisitor& v)
{
v.visitBar(*this);
}
};
class SetArbitaryVisitor : public ArbitraryVisitor
{
void visitParent(Arbitrary& vm)
{
vm.DoSomething();
}
void visitFoo(Foo& vm)
{
vm.DoSomething();
}
void visitBar(Bar& vm)
{
vm.DoSomething();
}
};
int main()
{
Arbitrary *arb = new Foo();
SetArbitaryVisitor scv;
arb->accept(scv);
}
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.