emza0114
emza0114

Reputation: 69

Bridge pattern example from "Designed Patterns Explained"

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

http://www.netobjectives.com/resources/books/design-patterns-explained/cpp-code-examples/chapter10#10-3

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

Answers (2)

BartoszKP
BartoszKP

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 CircleImpls - 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 CircleImpls 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

Some programmer dude
Some programmer dude

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

Related Questions