A low level (core) library, Library A, has the following base class:
class Shape
{
public:
virtual void draw() const = 0;
};
A high level (application) library, Library B, contains the derived classes, for example:
class Circle : public Shape
{
public:
void draw() const
{
std::cout << "Circle" << std::endl;
}
};
A mid level (GUI) library, Library C, needs to instantiate derived classes, for example:
std::shared_ptr<Shape> ShapeEditor::create()
{
if (someCondition)
{
return std::make_shared<Circle>();
}
}
However, this is not possible due to the relationships between libraries. Library C is aware of (links with) Library A, but not Library B and therefore cannot instantiate derived class types.
To work around this, the factory pattern is deployed. Library A contains a simple ShapeFactory:
class ShapeFactory
{
public:
typedef std::function<std::shared_ptr<Shape>()> ShapeBuilder;
static std::shared_ptr<Shape> create(const std::string &name)
{
auto it = registry().find(name);
if (it != registry().end())
{
return (it->second)();
}
return nullptr;
}
static std::map<std::string, ShapeBuilder>& registry()
{
static std::map<std::string, ShapeBuilder> impl;
return impl;
}
};
template<typename T>
struct ShapeFactoryRegister
{
ShapeFactoryRegister(std::string name)
{
ShapeFactory::registry()[name] = []() {return std::make_shared<T>();};
std::cout << "Registering class '" << name << "'\n";
}
};
Derived classes in Library B self-register themselves with the factory via a static ShapeFactoryRegister member variable:
class Circle : public Shape
{
public:
void draw() const
{
std::cout << "Circle" << std::endl;
}
private:
static ShapeFactoryRegister<Circle> AddToFactory_;
};
ShapeFactoryRegister<Circle> Circle::AddToFactory_("Circle");
The function from Library C then instantiates derived classes via the factory:
std::shared_ptr<Shape> ShapeEditor::create()
{
if (someCondition)
{
return ShapeFactory::create("Circle"); // Coupling between Library B and Library C
}
}
The use of a hard coded string as a handle to the derived types effectively couples Library B and Library C together. This method is no better than simply moving the derived classes into Library B.
The questions I have on this scenario is:
- What are alternatives to using a string as a handle to derived classes?
- How do I further decouple Library B and Library C?
- What are alternatives to the factory pattern to solve this problem?
- Is the factory design pattern the right hammer for this problem?
This problem has had me stumped for a while. Many thanks!
Aucun commentaire:
Enregistrer un commentaire