Abstract Factory Design Pattern.

I've been reading Design Patterns: Elements of Reusable Object-Oriented Software and their example is really confusing me.

From what I understand, this design pattern involves an abstract factory class and abstract product classes. This code defines how the products will be created:

1
2
3
4
5
6
7
8
9
10
11
12
13
class MazeFactory {
public:
    MazeFactory();

    virtual Maze* MakeMazeO const
        { return new Maze; }
    virtual Wall* MakeWall() const
        { return new Wall; }
    virtual Room* MakeRoom(int n) const
        { return new Room(n); }
    virtual Door* MakeDoor(Room* rl, Room* r2) const
        { return new Door(rl, r2); }
};


But you can't create objects; or in this case allocate memory for objects of abstract classes. What am I missing here?

According to their diagram, MazeFactory class is suppose to be abstract as well, but they did mention the reason why MazeFactory isn't abstract is so it can act as both an AbstractFactory and ConcreteFactory as well as this being a common implementation of the Abstract Factory Design. Is that what is going on with the various Product classes? That they aren't actually abstract classes to allow the definitions above?
closed account (o1vk4iN6)
I can't really think of a reason why you would want to use a factory to make a maze. You need an abstract factory that doesn't define the actual implementation of the functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Factory
{
public:
    virtual Wall* MakeWall() = 0;
};

class MazeFactory
{
public:
    virtual Wall* MakeWall() override { return new MazeWall; }
};

// wall hierarchy

class Wall
{
public:
    virtual ~Wall() { }
};

class MazeWall : public Wall
{
public:
    /* implement Wall abstract functions */
};
But you can't create objects; or in this case allocate memory for objects of abstract classes. What am I missing here?


Just wondering whether you knew about a pointer to a derived class being a valid pointer to a base class.

For example if Wall is an abstract base class, we can declare a pure virtual function in Wall that returns a pointer to Wall, or accepts arguments that are pointers to Wall.

In the derived concrete class, we can have functions that take arguments that are pointers to the derived class, or assign the return value from a function to a derived class pointer.

This is a very handy & powerful thing: we can declare a small number of functions in the base class, and not have to declare functions for every derived class type. We just override the functions as required

So, in the book's example they were intending to subclass Walls, Rooms & Doors. As per the example of the Bombed Wall at the end of the Sample Code section.

Hopefully this was the info you were after?
Is that what is going on with the various Product classes? That they aren't actually abstract classes to allow the definitions above?

Yep. They're all concrete.

They've made the assumption that all (e.g.) wall classes will be derived from the same concrete base class Wall. This allows them to use this class to define the required interface (by exposing the appropriate virtual functions) instead of using a distinct interface/abstract base class.

But if you wanted the freedom to implement walls which do not derive from the Wall class, or any other concreate class, then you'd need to factor out the interface into an abstract class.

Andy
Last edited on
@xerzi

I can't really think of a reason why you would want to use a factory to make a maze.


What do you mean? As in there are better approaches to building mazes?

@TheIdeasMan

Are you talking about polymorphism? As how xerzi has shown in his code?

For example if Wall is an abstract base class, we can declare a pure virtual function in Wall that returns a pointer to Wall...


I don't get what you mean here.

1
2
3
4
5
class Wall
{
public:
    virtual Wall* getWall() const = 0;
};


Like that? I don't actually understand what this would accomplish. Could you please explain?

I get the bit where they derive from Wall/Room/Door to create something more specific. It's just the part where they do this (if Wall is an abstract class)

1
2
virtual Wall* MakeWall() const
        { return new Wall; }


rather than how xerzi has shown here

virtual Wall* MakeWall() override { return new MazeWall; }

that confuses me. Leaves me wondering if Wall is a concrete class rather than abstract (as how they have done with making MazeFactory a concrete class, differing from their flowchart) or maybe it's just a "generic filler" they use and it's left to the reader to fill it in.

Edit:

@andywestken

So if there is no actual abstract classes, what does abstract even mean in this case? As in just a general interface for other classes to derive from to create something more specific?

@TheIdeasMan

I assume you have the book as well, could you please shed some light on what you think is going on with their sample code?
Last edited on
closed account (o1vk4iN6)
Well there isn't any point to having a factory that just contains a bunch of one liner new operators. You could do something more like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

class Maze
{
public:

    Wall& AddWall( /* wall properties */ )
    {
         // allocate wall here
         // push onto walls
         // return wall
    }

private:

    std::vector<Wall*> walls;

};


As for the original code, f that's the code that was provided in the example than yes it probably isn't abstract otherwise you'll get the errors you are getting.
I just realised I was looking at the very similar Factory Method, which also has Maze type examples. However what I explained was still right though. Perhaps I shouldn't have said "pure virtual function" : but the same still applies for ordinary virtual functions.

The book explains how the base class isn't an abstract class, so that only the operations that need overriding need to change; not all of them as per a pure virtual function. Which is what Andy was saying I think.

xerzi's code does the same thing as what I was saying - the MakeWall function is making a derived class object, but it returns a pointer to the base class. If it didn't return a pointer to Wall, then we would have a separate function for each type of Wall - MazeWall, BombedWall, EnchantedWall etc; as opposed to just one virtual base class function.

So the base class is acting as a placeholder for the generic virtual function, the derived classes specialise it. Which is also what Andy said.

I am hoping I haven't explained something you already knew very well - it is just that your original comment didn't seem to indicate that.

The other thing is that the book present several scenarios - Abstract Factory has 3 different implementations, while Factory has 5. Also they can be used together.

Thanks guys that's very helpful in clearing things up. I thought there may have been some kind of funny business going on seeing as I'm a scrub.

@TheIdeasMan

I am hoping I haven't explained something you already knew very well - it is just that your original comment didn't seem to indicate that.


Nah I haven't really played with inheritance at all really. I barely know what it is or what it can do.
Last edited on
So if there is no actual abstract classes, what does abstract even mean in this case? As in just a general interface for other classes to derive from to create something more specific?

Well, I think the problem is that abstract in the sense of a pattern isn't as tightly defined as it is when applied to C++ classes.

In the C++ case, as I'm sure you know, an abstract class is one with one or more pure virtual methods, which therefore cannot be instantiated.

But in the pattern case, it's probabably more the case that it doesn't force you to use any specific concrete class; the pattern will work with any (C++, etc) class which implements the required interfaces, concrete or abstract.

Andy

PS I've just read the comment in Gamma et al, and I guess it's not esp. clear what's going on on first reading...
Last edited on
Topic archived. No new replies allowed.