We are using Abstract Factory
design pattern in our project, as the project became complex, most of the time the concrete class functionality need to separate to multiple class.
As the following code snippet, the render are supported by Renderer
and Canvas
, so there need static_cast
in the concrete implementation as following code snippet.
So is it bad design smell for typecasting in Abstract Factory
design pattern? If yes, how to improve the design in following snippet?
#include <iostream>
class Renderer
{
public:
virtual ~Renderer() {};
virtual void RenderIt(Canvas* canvas) = 0;
};
class OpenGLRenderer : public Renderer
{
void RenderIt(Canvas* canvas) {
// <----How can we avoid to cast here.
OpenGLCanvas* canvas = static_cast<OpenGLRenderer>(canvas);
// Do something with opengl.
}
};
class DirectXRenderer : public Renderer
{
void RenderIt(Canvas* canvas) {
// <---How can we avoid to cast here.
DirectXCanvas* canvas = static_cast<DirectXCanvas>(canvas);
// do something with directx
}
};
#include <string>
class RendererFactory
{
public:
Canvas * createCanvas(const std::string& type) {
if(type == "opengl")
return new OpenGLCanvas();
else if(type == "directx")
return new DirectXCanvas();
else return NULL;
}
Renderer *createRenderer(const std::string& type)
{
if(type == "opengl")
return new OpenGLRenderer();
else if(type == "directx")
return new DirectXRenderer();
else return NULL;
}
};
Best Answer
(First a general note: that is not an abstract factory. The pattern gets its name from having an abstract class or interface for the factory. You're then supposed to have a different factory class for each family of types, instead of choosing the type from looking at a string. However, you might choose a concrete factory class depending on a string. There is such an example at the end of this answer.)
This problem is in fact discussed in the Design Patterns book:
So if the decision between OpenGL and DirectX needs to be a runtime decision, then C++ can only give you that with a loss of type safety. Many design patterns feel much more comfortable in more dynamic languages, but those don't guarantee comparable type safety in the first place. E.g. you probably wouldn't think about this problem at all in Python, but the necessary cast is very visible in C++. So the
dynamic_cast
isn't really such a bad thing.(Note that you should use a
dynamic_cast
here to assert that the runtime type of the object really matches. Astatic_cast
would perform an unchecked downcast and is not type-safe.)What you can do is use templates instead of inheritance. All code using the renderers/canvasses would then be templated over the API type and would effectively be compiled twice. For example:
In the above example, the API struct takes over the role of your factory. Here using type aliases is sufficient, in a more complex example these API types could also provide methods.
Depending on how the client code is structured, it is sometimes possible to introduce a higher-level interface that abstracts over all concrete types. The concrete subclasses of the interface then internally now about the specific Canvas and Renderer types. This is known as type erasure.
Note that in languages with generics (as opposed to templates), it is possible to write this kind of code without casts. The fundamental difference is that template arguments are placeholders that must be filled in with concrete types, whereas generics have type variables that are sufficient for checking type safety, but don't specify a concrete run-time type. We'd then have (using Java):
The difference to C++ here is that we do in fact use Renderer and Factory interfaces, and that the client code only needs to be compiled once, not again for each API. However, we still have to carry all necessary type variables (here: the Canvas) around. In Java that is especially cumbersome since the language does not have type aliases.