Where to put member variables in my interface? [closed] - c++

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 months ago.
Improve this question
The c++ guidelines say not to put any data in the base class and also not to use trivial getter and setter methods, but rather just member variables, but where do I put the member variables in my data base access implementation? If I put it in one of the derived classes, I get the compiler error that my class db_interface does not have such a member.
#include <boost/core/noncopyable.hpp>
#include <postgresql/libpq-fe.h>
#include <sqlite3.h>
#include <iostream>
#include <string>
class db_interface : boost::noncopyable{
public:
void connect() const {connect_to_DB();}
virtual ~db_interface(){};
private:
virtual void connect_to_DB() const = 0;
};
class postgreSQL : public db_interface{
private:
void connect_to_DB() const { std::cout << "THIS IS POSTGRESQL"<< std::endl; }
};
class liteSQL : public db_interface{
public:
std::string dbName;
private:
void connect_to_DB() const { std::cout << "THIS IS LIGHTSQL"<< std::endl; }
};
class DBFactory {
public:
virtual db_interface *createDB(std::string) = 0;
};
class Factory: public DBFactory {
public:
db_interface *createDB(std::string type) {
if(type == "LiteSQL") {
return new liteSQL;
}
else if(type == "PostgreSQL") {
return new postgreSQL;
}
return nullptr;
}
};

It's worth pointing out the guidelines are just that. Some of them even contradict each other. It's up to you to be the engineer and decide what guidelines make sense for you and your project.
However, these guidelines do not really contradict each other.
The first one you linked to is: C.133: Avoid protected data
The other guideline linked in that one is: C.121: If a base class is used as an interface, make it a pure abstract class
The other guideline linked is the one you seem to be skipping and subsequently tripping yourself over: C.9: Minimize exposure of members
C.133 links to last one with the text Prefer private data.
The ideas being presented do not conflict, but they do take the separation of concerns to a level that is less common in C++ land.
Mainly, your interface should just be an interface. In that regard, putting data in your interface makes no sense.
You can then derive from that interface to create your 'base' class. When here, don't make your data protected. Make it private. Your base class is then the only class responsible for that data. Protected functions like getters and setters do make sense here for your derived classes to make changes which your base class alone is responsible for handling/validating.
On top of that, I actually don't like the idea of the interface being all public pure virtual functions and instead I prefer NVI or Non-Virtual Interfaces. Here's a short example:
#include <iostream>
#include <memory>
#include <string>
#include <vector>
// Interface
class PetInterface {
public:
virtual ~PetInterface() = default;
std::string speak() const { return speak_v(); }
std::string name() const { return name_v(); }
private:
virtual std::string speak_v() const = 0;
virtual std::string name_v() const = 0;
};
// Base class
class Pet : public PetInterface {
public:
Pet() = default;
Pet(std::string name, std::string vocal) : m_name(name), m_vocal(vocal) {}
virtual ~Pet() = default;
private:
std::string m_name;
std::string m_vocal;
protected:
std::string name() const { return m_name; }
std::string vocal() const { return m_vocal; }
};
class Dog final : public Pet {
public:
Dog() = default;
Dog(std::string name) : Pet(name, "Woof") {}
private:
std::string speak_v() const override { return vocal(); }
std::string name_v() const override { return name(); }
};
class Cat final : public Pet {
public:
Cat() = default;
Cat(std::string name) : Pet(name, "Meow") {}
private:
std::string speak_v() const override { return vocal(); }
std::string name_v() const override { return name(); }
};
int main() {
std::vector<std::unique_ptr<PetInterface>> pets;
pets.emplace_back(new Dog("Fido"));
pets.emplace_back(new Cat("Dame Whiskers"));
for (const auto& i : pets) {
std::cout << i->name() << " says \"" << i->speak() << "\"\n";
}
}
Output:
❯ ./a.out
Fido says "Woof"
Dame Whiskers says "Meow"
Pet is still an abstract class, as only 'leaf' classes should be concrete.
One final note is that you don't need boost to make a thing non-copyable. Just = delete the copy constructor and copy assignment operator.

You should use use trivial getter and setter methods. Pure virtual in the base class, and implemented in derived classes.
From C++ Core Guidelines:
We do not suffer the delusion that every one of these rules can be effectively applied to every code base... Consider these rules ideals for new code, opportunities to exploit when working on older code, and try to approximate these ideals as closely as feasible.
You should not follow Avoid trivial getters and setters because you are implementing a Database interface, and the express purpose of a database is "setting" and "getting".

Related

My prof is asking me to create objects but both classes are abstract

i have this OOP lab assignment , and i was asked to creat 2 classes that inherit from a parent class, both of these classes have pure virtual functions "virtual void merge(Book *ptr) = 0;".
then later in the assignment i was asked to create objects in the driver class, but both classes that i should be using to create the objects are abstract!
is there something i could do that creates the objects?
or is it just a mistake by the prof?
The error i get
I asked the prof and he said that no, nothing is wrong here, so i'm kinda lost.
Likely your mentor wants you to override all pure-virtual functions to make these classes concrete. After that, you can instantiate them without any issues:
#include <string>
struct Book {
virtual std::string genre() const = 0;
virtual ~Book() = default;
};
struct FictionBook : Book {
std::string genre() const override { return "Fiction"; }
};
struct NonFictionBook : Book {
std::string genre() const override { return "NonFiction"; }
};
int main() {
FictionBook fiction;
NonFictionBook nonFiction;
}

How to check if object is castable?

I have following classes:
class ATemperatureDevice{};
class AHumidityDevice{};
class BluetoothLeDevice{};
class Sensor1 : BluetoothLeDevice, ATemperatureDevice, AHumidityDevice {};
class Sensor2 : BluetoothLeDevice, AHumidityDevice {};
I have a vector<BluetoothLeDevice*>, where all devices are stored.
The Classes ATemperatureDevice, AHumidityDevice and BluetoothLeDevice have virtual functions.
When I pick one, I have a BluetoothLeDevice. Now I want to check if it derives from ATemperatureDevice and/or AHumidityDevice.
I tried dynamic_cast, when its not castable, I should get null but, it says "'dynamic_cast' not permitted with -fno-rtti" although they have virtual functions.
What's the best way to check and cast?
As people already commented, storing polymorphic objects T inside a std::vector<T> will cause object slicing. Your code makes sense in C#, but in C++ people tend to use composition over inheritance whenever possible to avoid such issues. In your case this could look like: https://godbolt.org/z/xna1vjnWh
If dynamic_cast is not permitted, then store tag to identify derived class.
// Test sample
#include <iostream>
using namespace std;
class BluetoothLeDevice
{
public:
enum class DerivedClassMetaInfo
{
Sensor1Tag,
Sensor2Tag
};
virtual ~BluetoothLeDevice()
{
}
virtual DerivedClassMetaInfo tag(void) const = 0;
template <typename TargetType>
TargetType& to()
{
if (TargetType::static_tag != tag())
throw "Convertion failed";
return static_cast<TargetType&>(*this);
}
};
class Sensor1 : public BluetoothLeDevice
{
public:
static const BluetoothLeDevice::DerivedClassMetaInfo static_tag = BluetoothLeDevice::DerivedClassMetaInfo::Sensor1Tag;
void show()
{
cout << "Sensor1 message" << endl;
}
virtual DerivedClassMetaInfo tag(void) const override
{
return static_tag;
}
};
int main()
{
BluetoothLeDevice* temp = new Sensor1();
Sensor1& sens = temp->to<Sensor1>();
sens.show();
delete temp;
return 0;
}
Note: use this code with cautions, because it does not handle tricky inheritance trees. However you can easy (I guess) improve it

Subclass as argument in superclass's member function, C++

I'm new to OOP and I'm working on a C++ project. I isolated my problem to make answering easy but here's the real scenario:
I have a superclass member function, that modifies values inside the object that called it. The modification is based on a value coming from another object of the same class. This object is given to the function as the only parameter. Such as:
void BaseClass::function(BaseClass x) {}
However, I created a subclass. And if the parameter is a subclass type, I want to modify its unique attribute, too.
void BaseClass::function(DerivedClass x) {}
The problem is that the subclass is obviously defined later in the code.
I don't want it as two separate methods, because the calculation algorithm is already written inside, and also the solution I search for doesn't require to change the code at the places where the function is already in use. Besides, every other possibility that comes to mind (e.g. using typeid()) looks silly.
#include <iostream>
#include <string>
class Base
{
protected:
//common attribute
const std::string name;
public:
//constructor for common attribute
Base(const std::string nameString) : name(nameString) {}
//getter
std::string getName() { return name; }
//superclass as parameter
void test1(Base &example) { std::cout << example.getName(); }
//subclass as parameter (I'd want the line below to work)
//void test2(Derived &example) { std::cout << example.getNumber(); }
};
class Derived : private Base
{
protected:
//unique attribute
const std::string number;
public:
//constructor
Derived(const std::string nameString, const std::string numberString) : Base(nameString),
number(numberString) {}
//getter for unique attribute
std::string getNumber() { return number; }
};
int main ()
{
Base object = Base("whatever");
Base baseParameter = Base("base");
Derived derivedParameter = Derived("derived", "12");
object.test1(baseParameter);
//object.test2(derivedParameter);
return 0;
}
What is the standard way of doing it?
You could make test2 a template, and ensure that it's only used with types derived from Base:
template<typename Derived>
void test2(Derived &example)
{
static_assert(std::is_base_of_v<Base, Derived>);
std::cout << example.getNumber();
}
Here's a demo.

How to initialize grandparent's (const) property in grandchild in C++?

I know this might look like a trivial question, but I haven't found really an elegant C++ solution to the following problem.
I want to represent a complex (tree-like) hierarchy of a "world" of objects. Let's say Animals. Every animal has some basic const properties.
Like for example a name. Then it also has some methods, but they are not significant for this problem.
class Animal {
public:
const char *GetName() const;
protected:
const char *name;
};
class Insect : public Animal {
...
};
class Butterfly : public Insect {
...
};
In this hierarchy I would like to initialize the name in every derived (grand)child. What is an elegant solution to this?
It is also important to say that in this "world" there be only instances of the tree leaves. That is, there will be no objects "Animal" or "Insect". But there will be objects "Butterfly", "Bee" or "Mosquito".
I know the "standard" way to do this is to put name into constructor:
Animal::Animal(const char *name) : name(name) {}
Insect::Insect(const char *name) : Animal(name) {}
Butterfly::Butterfly() : Insect("Butterfly") {}
But if there are more of these properties, the derived classes need also some initialization and the hierarchy has more levels it can become quite a mess:
Animal::Animal(const char *name) : name(name) {}
Vertebrate::Vertebrate(const char *name) : Animal(name) {}
Mammals::Mammals(const char *name) : Vertebrate(name) {}
Ungulate::Ungulate(const char *name) : Mammals(name) {}
Horse::Horse() : Ungulate("Horse") {}
Another option I can see is to drop the const and assign directly in the grandchild's constructor:
class Animal {
public:
const char *GetName() const;
protected:
std::string name;
};
Horse::Horse() {this->name = "Horse";}
But that is also not optimal, because the const is lost and it is more prone to errors (the initialization can be forgotten).
Is there some better way to do this?
Hm - hope that I get not locked out from SO for that answer, but you could use a virtual base class that implements the name-property. Thereby, you will not have to propagate initialization in a base class all way through the hierarchy but could directly address the "very base" constructor with the name-property. Furthermore, you will actually be enforced to call it in any "Grandchild"-class, so you can't forget it by accident:
class NamedItem {
public:
NamedItem(const char* _name) : name(_name) {}
const char *GetName() const;
protected:
const char *name;
};
class Animal : public virtual NamedItem {
public:
Animal(int mySpecificOne) : NamedItem("") {}
};
class Insect : public Animal {
public:
Insect(int mySpecificOne) : Animal(mySpecificOne), NamedItem("") {}
};
class Butterfly : public Insect {
};
The elegant solution is to pass arguments through initialisation. For example, if the "name" variable was the name of the Butterfly (such as "sally" or "david") then it would be obvious it has to be done through initialisation. If you are finding that is ugly, as it is here, it may indicate that your data decomposition/class heirarchy are at fault. In your example every Butterfly object would have an identical set of properties that really refer to their class rather than each instance, ie they are class variables not instance variables. This implies that the "Butterfly" class should have a static pointer to a common "Insect_Impl" object (which might have a pointer to a single "Animal_Impl" object etc) or a set of overridden virtual functions. (Below I only show one level of heirarchy but you should be able to work out more levels)
// Make virtual inherited functionality pure virtual
class Animal {
private:
std::string objName; // Per object instance data
public:
virtual ~Animal(std::string n): objName(n) {}
virtual std::string const& getName() = 0; // Per sub-class data access
virtual std::string const& getOrder() = 0; // Per sub-class data access
std::string const& getObjName() { return this->objName; }
};
// Put common data into a non-inherited class
class Animal_Impl{
private:
std::string name;
public:
Animal_Impl(std::string n): name(n);
std::string const& getName() const { return this->name; }
};
// Inherit for per-instance functionality, containment for per-class data.
class Butterfly: public Animal{
private:
static std::unique< Animal_Impl > insect; // sub-class data
public:
Butterfly(std::string n): Animal(n) {}
virtual ~Butterfly() {}
virtual std::string const& getName() override {
return this->insect->getName(); }
virtual std::string const& getOrder() override {
static std::string order( "Lepidoptera" );
return order; }
};
// Class specific data is now initialised once in an implementation file.
std::unique< Animal_Impl > Butterfly::insect( new Animal_Impl("Butterfly") );
Now using the Butterfly class only needs per-instance data.
Butterfly b( "sally" );
std::cout << b.getName() << " (Order " << b.getOrder()
<< ") is called " << b.getObjName() << "\n";
The issue with your alternative, or any alternative leaving name non-const and protected, is that there is no guarantee that this property is going to be setup properly by the subclasses.
What does the following class give you ?
class Animal {
public:
Animal(const char* something)
const char *GetName() const;
private:
const char *name;
};
The guarantee of the immutability of the Animal interface, which can be a big plus when doing multithreading. If an object is immutable, multiple threads can use it without being a critical resource.
I know the "standard" way to do this is to put name into constructor:
... But if there are more of these properties, the derived classes
need also some initialisation and the hierarchy has more levels it can
become quite a mess
It is not messy at all. Given that there is only one place where the members of object A are being initialised, and it is within the constructor of their subclasses.

Am I Abusing Inheritance Here? What's A Best-Practice Alternative/Pattern?

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.