HiveBrain v1.2.0
Get Started
← Back to all entries
snippetcsharpModerate

Abstract Factory Pattern C# Example

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
factoryexampleabstractpattern

Problem

I am working through more tutorials on Design Patterns and came across the Abstract Factory Pattern. I extended it out a bit (the example that is) and want your opinion:

```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DoFactory.GangOfFour.Abstract.RealWorld
{
class MainApp
{
///
/// Entry point into console application.
///
public static void Main()
{
// Create and run the African animal world
ContinentFactory africa = new AfricaFactory();
AnimalWorld world = new AnimalWorld(africa);
world.RunFoodChain();

// Create and run the American animal world
ContinentFactory america = new AmericaFactory();
world = new AnimalWorld(america);
world.RunFoodChain();

// Wait for user input
Console.ReadKey();
}
}

///
/// The 'AbstractFactory' abstract class
///
abstract class ContinentFactory
{
public abstract Herbivore CreateHerbivore();
public abstract Carnivore CreateCarnivore();
public abstract Problem CreateProblem();
public abstract Giant CreateGiant();
public abstract Giant CreateSmallGiant();
}

///
/// The 'ConcreteFactory1' class
///
class AfricaFactory : ContinentFactory
{
public override Herbivore CreateHerbivore()
{
return new Wildebeest();
}
public override Carnivore CreateCarnivore()
{
return new Lion();
}
public override Problem CreateProblem()
{
return new Trouble();
}

public override Giant CreateGiant()
{
return new Elephant();
}

public override Giant CreateSmallGiant()
{
return new SmallElephant();
}
}

///
/// The 'ConcreteFactory2' class
///

Solution

Single Inheritance

By using abstract classes like this...

abstract class ContinentFactory
{
    public abstract Herbivore CreateHerbivore();
    public abstract Carnivore CreateCarnivore();
    public abstract Problem CreateProblem();
    public abstract Giant CreateGiant();
    public abstract Giant CreateSmallGiant();
}


Really boils down to this:

interface ContinentFactory
{
    Herbivore CreateHerbivore();
    Carnivore CreateCarnivore();
    Problem CreateProblem();
    Giant CreateGiant();
    Giant CreateSmallGiant();
}


You should always prefer interface over abstract class, especially when the abstract class doesn't have anything virtual or protected for the derived types to override or consume.

The reason for this, is because a class in C# can only ever be derived from one single base class, but can implement as many interfaces as you wish. By deriving from a base class you're locking yourself into an inheritance issue.. especially when the base class is used as a marker interface that brings nothing to the table:

abstract class Herbivore
{
}


And I find it's a code smell that every class you derive from Herbivore also has nothing to offer:

class Wildebeest : Herbivore
{
}

class Bison : Herbivore
{
}


Interface Segregation

Your design won't scale very well. In my opinion, the "ideal" abstract factory interface looks something like this:

interface IAbstractFactory
{
    IFactoryProduct Create();
}


In this case I think you need to trim your factory interface / ContinentFactory - all these methods really do this:

interface ICreatureFactory
{
    ICreature Create();
}


This means you would need an ElephantFactory, a LionFactory, a WolfFactory, and so on and so forth. Annoying.

On the other hand, it turns this:

private Herbivore _herbivore;
private Carnivore _carnivore;
private Problem _problem;
private Giant _giant;
private Giant _giant2;


Into that:

private readonly IList _creatures;


Abstract Factory Galore

I think you don't need a factory to create the creatures. Your world could be constructor-injected with any IEnumerable and walk away happy.

Alternatively, the world could be constructor-injected with some IFaunaFactory implementation whose role would be to provide the world with an IEnumerable:

class AfricanFaunaFactory : IFaunaFactory
{
    public IEnumerable Create()
    {
        var result = new List
            { 
                new Lion(),
                new Elephant(),
                new Gorilla(),
                new Hyena()
                // ...
            };

        return result;
    }
}

class AmericanFaunaFactory : IFaunaFactory
{
    public IEnumerable Create()
    {
        var result = new List
            {
                new Wolf(),
                new Bison(),
                new Beaver(),
                new BlackBear()
                // ...
            };

        return result;
    }
}


This design is much easier to extend, and has much less confusing names too. I'd suggest to use simpler interfaces and avoid abstract classes until you identify something that's common to all creatures, that you can write in a virtual method in that base class (which derived types are free to override, or not).

interface ICreature
{
    void MakeSound();
}

interface ICarnivore : ICreature
{
    void Eat(ICreature victim);
}

interface IHerbivore : ICreature
{
    void Eat();
}

interface IGiant : IHerbivore
{
    void Stomp(ICreature victim);
}

Code Snippets

abstract class ContinentFactory
{
    public abstract Herbivore CreateHerbivore();
    public abstract Carnivore CreateCarnivore();
    public abstract Problem CreateProblem();
    public abstract Giant CreateGiant();
    public abstract Giant CreateSmallGiant();
}
interface ContinentFactory
{
    Herbivore CreateHerbivore();
    Carnivore CreateCarnivore();
    Problem CreateProblem();
    Giant CreateGiant();
    Giant CreateSmallGiant();
}
abstract class Herbivore
{
}
class Wildebeest : Herbivore
{
}

class Bison : Herbivore
{
}
interface IAbstractFactory
{
    IFactoryProduct Create();
}

Context

StackExchange Code Review Q#55062, answer score: 14

Revisions (0)

No revisions yet.