Reputation: 69
I am studying an example of a Bridge Pattern from "Designed Patterns Explained". The example I am looking at is Example 10.3 which can be found at
The specific confusion I have is with the Shape class and its derived classes.
#pragma once
#include "Drawing.h"
class Shape
{
public:
Shape(Drawing *aDrawing);
virtual void draw()= 0;
protected:
Drawing *myDrawing;
void drawLine( double, double, double, double);
void drawCircle( double, double, double);
public:
~Shape(void);
};
In the Circle class we have
#pragma once
#include "Shape.h"
class Circle : public Shape
{
public:
Circle(Drawing*, double, double, double);
virtual void draw();
virtual void drawCircle(double, double, double)=0;
public:
~Circle(void);
protected:
double _x, _y, _r;
};
So the question I have is:
why can drawCircle
be pure virtual in the inherited class given that the method is in fact implemented in the base class?
Upvotes: 1
Views: 4391
Reputation: 35891
Imagine you're building a module to draw shapes using different API's (windows GDI, some smartphone API, OpenGL, anything). Having a typical hierarchy abstract Shape
<---
concrete Circle
and abstract Shape
<---
concrete Rectangle
you would have to recompile and redeploy Circle
and Rectangle
each time you add a new framework and each time something changes in the existing framework. Such changes may even involve modifying the constructors of these classes so users of your module would have to also change their code.
Example: you have a working first version of your module, with the following interface for Circle
:
class Circle : public Shape
{
public:
Circle(int x, int y, int radius);
void draw(...);
};
Then, it happens that optimization reasons for one of the platforms force you to know the DPI
resolution of the current platform in advance (before actually drawing the circle). So, you would have to change the constructor:
class Circle : public Shape
{
public:
Circle(int x, int y, int radius, int dpi);
void draw(...);
};
and clients of your code would have to recompile their applications. There would be of course some hacks possible to avoid this (like introducing CircleWithDpi
), but they would lead to a highly coupled and hard to maintain code. If you use the bridge pattern you can have your clear design left intact and still express your domain (in general, concept of a "circle" shouldn't know anything about a thing called "dpi resolution").
So having:
class Circle : public Shape
{
public:
Circle(int x, int y, int radius);
virtual void draw(...) = 0;
};
and
class CircleImpl : public Circle
{
public:
CircleImpl(int x, int y, int radius, int dpi);
//perform some calculations before drawing for optimization
void draw(...);
//draw using appropriate API
};
and
class ShapeFactory
{
public:
virtual Circle* CreateCircle(int x, int y, int radius) = 0;
};
Of course you would have many CircleImpl
s - each for different platform your module supports (so, CircleImplGDI
, CircleImplTk
, CircleImplOpenGL
etc.).
In the implementation of the ShapeFactory
you'd create a particular CircleImpl
appropriately and the client of your module doesn't have to know anything about it. This example is the simplified version of the one you've given link to. Note that now, when one of possible CircleImpl
s is used as Circle
no abstract classes are being instantiated, so this should also clear out your issue about abstract derived class.
The main idea behind this pattern is to have two levels of abstraction: Shape
is an abstract geometrical concept, Circle
and Rectangle
are more concrete than the Shape
but in the context of many technical possibilities for drawing them they are still quite abstract. Concrete representations of particular shapes exist when you know the context: for example - drawing on a raster or using vector graphics.
Another level of abstraction gives you possibility to defer some more decisions about your code - at first we defer the decision about what shapes we have. Then, having Circle
and Rectangle
we defer the decision about how to draw them. And deferred decisions give us decoupled, flexible code (as demonstrated with the "added DPI" example).
Upvotes: 4
Reputation: 409136
Pure virtual methods are allowed in any class, as long as you don't try to create an instance of that class.
Upvotes: 1