Click here to Skip to main content
15,867,834 members
Articles / Programming Languages / C#
Article

Illustrated GOF Design Patterns in C# Part I: Creational

Rate me:
Please Sign up or sign in to vote.
4.82/5 (52 votes)
5 Nov 2002CPOL7 min read 379.1K   1.6K   362   21
Part I of a series of articles illustrating GOF Design Patterns in C#

Abstract

Design Patterns, Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides [also known as the Gang of Four (GOF)] has been a de facto reference for any Object-Oriented software developer. This article is Part I of a series of articles illustrating the GOF design patterns in C#. We will discuss the Abstract Factory, Factory Method, and Builder patterns. The Singleton pattern is already widely known and is not discussed here. It is assumed the reader is familiar with basic C# syntax and conventions, and not necessarily details of the .NET Framework.

Background

In Design Patterns, each pattern is described with its name (and other well-known names); the motivation behind the pattern; its applicability; the structure of the pattern; class/object participants; participant collaborations; pattern consequences; implementation; sample code; known uses; and related patterns. This article will only give a brief overview of the pattern, and an illustration of the pattern in C#.

A design pattern is not code, per se, but a "plan of attack" for solving a common software development problem. The GOF had distilled the design patterns in their book into three main subject areas: Creational, Structural, and Behavioral. This article deals with the Creational design patterns, or how objects get created.

This article is meant to illustrate the design patterns as a supplement to their material. It is recommended that you are familiar with the various terms and object diagram methods used to describe the design patterns as used by Gamma et al. If you're not familiar with the diagrams, they should be somewhat self-explanatory once viewed. The most important terms to get your head around are abstract and concrete. The former is a description and not an implementation, while the latter is the actual implementation. In C#, this means an abstract class is an interface, and the concrete class implements that interface.

Creational Patterns

To quote Gamma et al., "Creational design patterns abstract the instantiation process. They help make a system independent of how its objects are created, composed, and represented. A class creational pattern uses inheritance to vary the class that's instantiated, whereas an object creational pattern will delegate instantiation to another object." We separate the object from how it's directly created. As we delve into the patterns the reasons for doing so should become clear.

Abstract Factory

The intent of the pattern, as described in Design Patterns, is to "Provide an interface for creating families of related or dependent objects without specifying their concrete classes." The pattern structure is shown here:

Abstract Factory structure

This pattern is quite useful in systems where we want to expose a library of products to be used by a client, independent of their implementations. It is worthwhile to note that the Factories used by the client are typically Singletons, and the Abstract Factory is only an interface, the concrete implementation of the factory actually creates the products. For this, the interface defines a Factory Method (another creational design pattern) for each product, which is in turn implemented in the concrete factory.

In our C# illustration, we wish to create similar airplanes from two different manufacturers. Please view the sample source code file abstract_factory.cs for this example.

Both manufacturers sell single-engine and multi-engine land-based airplanes, of similar design, but with differing flight characteristics. We define an interface for the Abstract Factory as well as the airplanes in their own namespace as follows (we could also create an IAirplane interface to base the Airplanes on, but we're not going to do that for this illustration):

C#
public interface ISingleEngineAirplane
{
    void Fly();
}

public interface IMultiEngineAirplane
{
    void Fly();
}

public interface IAirplaneFactory
{
    ISingleEngineAirplane CreateSingleEngine();
    IMultiEngineAirplane  CreateMultiEngine();
}

We implement ISingleEngineAirplane and IMultiEngineAirplane for the two manufacturers, Cessna and Piper as private classes, as well as the public concrete factories for them in their own namespace. When a client needs an aircraft, they just import the interface library, the concrete library, and use the appropriate concrete factory:

C#
public class TheClient
{
    static CessnaFactory m_oCF = new CessnaFactory();
    static PiperFactory  m_oPF = new PiperFactory();

    public static void Main(string[] args)
    {
        ISingleEngineAirplane oSE;
        IMultiEngineAirplane  oME;

        Console.WriteLine("Creating Cessna SE, Piper ME...");

        oSE = m_oCF.CreateSingleEngine();
        oME = m_oPF.CreateMultiEngine();

        oSE.Fly();
        oME.Fly();

        Console.WriteLine("Creating Piper SE, Cessna ME...");

        oSE = m_oPF.CreateSingleEngine();
        oME = m_oCF.CreateMultiEngine();

        oSE.Fly();
        oME.Fly();
    }
}

It is worthwhile to note that it is possible to further use an AbstractFactory pattern to create the concrete factories as well!

Factory Method

One drawback to using an Abstract Factory is that to in order to support new products from your factories you have to extend the factories and all the subclasses. One way around this is to use the Factory Method pattern. Please view the sample source code file factory_method.cs for this example.

The Factory Method pattern is useful when (GOF):

  • a class can't anticipate the class of objects it must create.
  • a class wants its subclasses to specify the objects it creates.
  • classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate.

It's structure is shown here:

Factory Method structure

In our example, our concrete factories define numeric constants to what kind of objects it can create (you can use strings or other identifying types):

C#
public class CessnaFactory : IAirplaneFactory
{
    public const int SEL = 1;
    public const int MEL = 2;

    public virtual IAirplane Create(int t)
    {
        switch(t)
        {
            case SEL:
                return new CessnaSingleEngine();
            case MEL:
                return new CessnaMultiEngine();
            default:
                throw new Exception("Unknown Aircraft Type!");
        }
    }
}

The client, would use the factory thus:

C#
IAirplane o = m_oCF.Create(CessnaFactory.SEL);

If we wished to extend the factory to build other objects we could derive from the Factory, and override the Create method, of course calling the base class' Create method:

C#
public class BetterCessnaFactory : CessnaFactory
{
    public const int CITATION = MEL + 1;

    override public IAirplane Create(int t)
    {
        try
        {
            return base.Create(t);
        }
        catch
        {
            switch(t)
            {
            case CITATION:   
                return new CessnaCitation();
            default:
                throw new Exception("Unknown Aircraft Type!");
            }
        }
    }
}

The Factory Method pattern is widely used in COM. It's quite informative and educational to peruse the C++ ATL source code, and see it for yourself.

Builder

A Builder is a creational design pattern meant to "Separate the construction of a complex object from its representation so that the same construction process can create different representations." (GOF) Continuing with our airplane manufacturer analogy, building an airplane consists of assembling the sub-parts. Our airplane creation algorithm should be independent of the parts that make it up and how they are assembled. Please view the sample source code file builder.cs for this example.

The builder design pattern structure looks like this:

Builder structure

We begin with the parts, defining an abstract part builder, abstract airplane part, and abstract director (assembler) as interfaces which we will make concrete by implementing them later for our specific parts:

C#
public interface IAirplanePart
{
    string Manufacturer { get; }
}

public interface IPartBuilder
{
    bool BuildPart()
}

public interface IAirplane
{
    IAirplanePart Airframe { get; }
    IAirplanePart Engine { get; }
}

public interface IAirplaneAssembler
{
    bool Construct();
}

We then create the concrete implementations for our various parts, for example, a Continental engine:

C#
public class ContinentalEngine : IAirplanePart, IPartBuilder
{
    private const string MANUFACTURER = "Continental";

    public string Manufacturer { get { return MANUFACTURER; } }

    public bool BuildPart()
    {
        Console.WriteLine("Assembling a {0} engine...", 
            MANUFACTURER);

        return true;
    }
}

We've combined the GetResult() in the pattern structure into the actual BuildPart() for brevity. We further make a concrete director (assembler) for our Cessna 172:

C#
public class C172 : IAirplane, IAirplaneAssembler
{
    private C172Airframe m_oAirframe;
    private LycomingEngine m_oEngine;

    public IAirplanePart Airframe { get { return m_oAirframe; } }
    public IAirplanePart Engine { get { return m_oEngine; } }

    public bool Construct()
    {
        m_oAirframe = new C172Airframe();
        m_oEngine = new LycomingEngine();

        return m_oAirframe.BuildPart() && m_oEngine.BuildPart();
    }
}

In order to use these fun, new classes in a client, we import the interface library, the concrete library, and build a Cessna 172:

C#
public static void Main(string[] args)
{
    C172 o = new C172();

    Console.WriteLine("Constructing a Cessna 172...");

    if (o.Construct())
    {
        Console.WriteLine("Success!");

        Console.WriteLine("Airframe: {0}",
            o.Airframe.Manufacturer);

        Console.WriteLine("Engine: {0}", 
            o.Engine.Manufacturer);
    }
    else
        Console.WriteLine("Failed!");
}

If all goes well, we should see:

Constructing a Cessna 172...
Assembling a Cessna 172 airframe...
Assembling a Lycoming engine...
Success!
Airframe: Cessna 172
Engine: Lycoming

This example is highly simplified, but I hope it illustrates the pattern in a straightforward manner. In a real-world implementation, I probably would not combine the airplane part and the builder of that part, as building a part isn't usually the responsibility of the part itself. I would also not combine the "director" with the actual object being assembled for the same reason.

Conclusions

Creational design patterns allow for great flexibility in how your software solves the problem of creating objects:

Using an Abstract Factory allows you to control the kind of classes you create at runtime. It also encapsulates the functionality of object creation, isolating the implementation from clients, since clients only use the interfaces. As you can see from the example in this article, exchanging product families is simple.

Factory Methods. Design Patterns has this to say about the Factory Method pattern: "A potential disadvantage of factory methods is that clients might have to subclass the Creator class just to create a particular ConcreteProduct object. Subclassing is fine when the client has to subclass the Creator class anyway, but otherwise the client now must deal with another point of evolution." In the sample source code, you can see this statement in action.

The Builder creational pattern lets you vary a product's internal structure, as well as how it gets assembled. Because you construct the object through an abstract interface, you can define a new kind of builder for a product to assemble it from different structures. The client need not know anything about the construction process, nor the parts that make up a product. You get a high degree of control in the construction process.

Stay tuned for future articles...

Building the Samples

Unzip the source files to the folder of your choice. Start a shell (cmd.exe), navigate to the folder and type nmake. You may have to modify the Makefile to point to the correct folder where your .NET Framework libraries live.

History

2002-11-06 Updated builder.cs constants to private constants :)

2002-11-04 Slight revision

2002-11-03 Initial Revision

References

  • Design Patterns, Elements of Reusable Object-Oriented Software. Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Addison Wesley Longman, Inc. 1988. ISBN 0-201-63498-8.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer
United States United States
20+ years as a strategist at the intersection of business, design and technology.

Comments and Discussions

 
GeneralMy vote of 5 Pin
d2niraj24-Jan-11 23:44
d2niraj24-Jan-11 23:44 
GeneralAbout the Design Pattern Pin
analyzer_Dev7-Feb-07 0:13
analyzer_Dev7-Feb-07 0:13 
GeneralRe: About the Design Pattern Pin
C#.Net3.026-Mar-07 1:23
C#.Net3.026-Mar-07 1:23 
GeneralClient class could use IAirplaneFactory Pin
Fakher Halim8-Jan-04 16:45
Fakher Halim8-Jan-04 16:45 
GeneralRe: Client class could use IAirplaneFactory Pin
ian mariano9-Jan-04 3:07
ian mariano9-Jan-04 3:07 
GeneralRe: Client class could use IAirplaneFactory Pin
Ladrak30-Jan-15 12:22
Ladrak30-Jan-15 12:22 
GeneralGeneral question on patterns Pin
Anonymous29-Jun-03 19:16
Anonymous29-Jun-03 19:16 
GeneralRe: General question on patterns Pin
Anonymous18-Feb-04 23:19
Anonymous18-Feb-04 23:19 
GeneralPoor Pin
Anonymous7-Jun-03 14:10
Anonymous7-Jun-03 14:10 
GeneralRe: Poor PinPopular
ian mariano7-Jun-03 18:57
ian mariano7-Jun-03 18:57 
GeneralRe: Poor Pin
Anonymous12-Oct-03 19:30
Anonymous12-Oct-03 19:30 
GeneralRe: Poor Pin
Anonymous8-Mar-04 15:47
Anonymous8-Mar-04 15:47 
GeneralRe: Poor Pin
Josh Smith19-Jan-07 16:08
Josh Smith19-Jan-07 16:08 
GeneralI can't believe these are design patterns. Pin
Member 19148424-Apr-03 21:37
Member 19148424-Apr-03 21:37 
GeneralSmall point Pin
6-Nov-02 0:42
suss6-Nov-02 0:42 
GeneralRe: Small point Pin
ian mariano6-Nov-02 6:38
ian mariano6-Nov-02 6:38 
GeneralRe: Small point Pin
Anonymous4-Apr-03 1:43
Anonymous4-Apr-03 1:43 
GeneralGOF Patterns Pin
Blake Coverett3-Nov-02 21:14
Blake Coverett3-Nov-02 21:14 
GeneralRe: GOF Patterns Pin
ian mariano3-Nov-02 22:50
ian mariano3-Nov-02 22:50 
GeneralRe: GOF Patterns Pin
Anonymous4-Apr-03 1:43
Anonymous4-Apr-03 1:43 
GeneralMore on the Builder Pin
ian mariano3-Nov-02 13:43
ian mariano3-Nov-02 13:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.