• 4 hours
  • Easy

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 3/28/24

Create Objects With Creational Design Patterns

Creational Design Patterns

In the last chapter, we discussed what design patterns are and their value. Moving forward, we are going to delve into some of the design patterns in each category: creational, structural, and behavioral.

Creational design patterns deal with object creation: trying to create objects in a way that fits the given situation.

Let’s begin our design pattern journey by looking at four creation design patterns:

  • Factory method

  • Builder

  • Prototype

  • Singleton

Factory Method Pattern

As with any factory, the factory method pattern builds and produces things.

Like a car factory, each car is made up of many components (i.e., brakes, seating, engine, tires). A person working on the assembly line for an engine knows they’re building a car, but might not know for which car model.

Therefore, you can apply the factory method pattern to those situations where you have a set of component classes, but you won’t know which one to use at runtime. You create an object without exposing the creation logic. With that, you use an interface for creating the object, but let a subclass decide which class to instantiate.

How do I know when to use a factory method pattern? 

Here are some clues:

  • Constructors have too much code. Are you noticing that you’re doing more than just assigning a few variables? 

  • A constructor in a class has an argument, and later you have to make a code change where the constructor takes more arguments. You then have to make changes throughout your application wherever this class was created. 

  • Take for example:  TableLight light = new TableLight(Switch switch, BulbType bulbType);
    When the object is created, the client is aware about classes and the object process. You should avoid exposing such object creation details to the client application. 

Let’s take a look at the UML class diagram below that shows the structure of the factory method pattern:

The Structure of the Factory Method
Structure of the factory method pattern
  • IProduct: The interface for creating the object.

  • ConcreteProduct: A class that implements the product interface.

  • Creator: An abstract class and declares the factory method, which returns an object of type  Product.

  • ConcreteCreator: A class that implements the creator class and overrides the factory method to return an instance of a  ConcreteProduct

What does this look like in code?

Let’s take the above UML diagram and implement it in C#:

public interface IProduct
{
}
 
public class ConcreteProductA : IProduct
{
}
 
public class ConcreteProductB : IProduct
{
}
 
public abstract class Creator
{
    public abstract IProduct FactoryMethod(string type);
}
 
public class ConcreteCreator : Creator
{
    public override IProduct FactoryMethod(string type)
    {
        switch(type)
        {
            case "A":
                return new ConcreteProductA();
                break;
 
            case "B":
                return new ConcreteProductB();
                break;
 
            default:
                throw new ArgumentException("Invalid type", type);
        }
    }
}

Now take the structure from above and create an example of creating a factory product.

First, create an interface of  IProduct:

public interface IProduct
{
    string GetName();
    string SetPrice(double price);
}

By using an interface, you can have the subclasses decide with class to instantiate. For example:

public class Phone : IProduct {}
public class Tablet : IProduct {}

Build out the phone class:

public class Phone : IProduct
{
    private double _price;
 
    public string GetName()
    {
        return "IPad";
    }
 
    public string SetPrice(double price)
    {
        this._price = price;
        return "success";
    }
}

Create an abstract class needed to be the product making factory:

public abstract class ProductFactory
{
    public abstract IProduct CreateProduct();
 
    public IProduct GetProduct()
    {
        return this.CreateProduct();
    }
}

With the product factory, you can now create product type factories that inherit the product factory.

public class PhoneFactory : ProductFactory
{
    public override IProduct CreateProduct()
    {
        IProduct product = new Phone();
        product.SetPrice(200.50);
        return product;
    }
}

So, whenever you need to create a phone object:

IProductFactory phoneFactory = new PhoneFactory();
IProduct phone = phoneFactory.CreateProduct();

But, what if we want to create a tablet? Simple:

IProductFactory tabletFactory = new TabletFactory();
IProduct tablet = tabletFactory.CreateProduct();

What Are the Advantages?

  • Did you notice anything familiar in the above example? If you remember the SOLID principles, you noticed that the factory method pattern followed the open/closed principle. When a new requirement appeared, like creating a tablet, we did not make changes to existing code but created an additional factory. 

  • To support additional products, do not modify existing code, but rather add one new factory class.

  • The client application calls CreateProduct without knowing anything about how and what was created.

Builder Pattern

The next design pattern is the builder. The definition from Design Patterns is:

Separate the construction of a complex object from its representation so that the same construction process can create different representations.

When will I need this?

Simply put, whenever you need to build something from smaller pieces.

Think of an assembly line for a car where it’s a step-by-step process of adding smaller pieces to build the whole car.

For an application, this is useful when you need to create several complex objects that follow a similar construction procedure. 

This sounds like the factory pattern. Why wouldn’t I use that?

Great question! It comes down to complexity. Use the builder pattern when you need to do a lot of things to build an object step by step. A factory emphasizes a family of product objects. The builder returns the product as a final step, while the factory returns a finished product in one step.

Let’s take a look at the UML class diagram below that shows the structure of the builder pattern:

Structure of the Builder Pattern
Structure of the builder design pattern
  • Director: This class doesn’t create and assemble the object directly. Instead, it refers to the builder interface for building the parts of a complex object.

  • Builder: The abstract interface for creating objects (product).

  • ConcreteBuilder: This class implements the builder, which makes it able to construct other objects. 

  • Product: The class that defines the type of the complex object that is to be generated.

Let’s see an example of building a car step by step.

First, create a class which represents the product object:

public class Car 
{
    public string Make { get; set; }
    public string Model { get; set; }
    public int NumDoors { get; set; }
    public string Color { get; set; }

    public Car(string make, string model, string color, int numDoors)
    {
        Make = make;
        Model = model;
        Color = color;
        NumDoors = numDoors;
    }
}

Next, create a builder interface:

public interface ICarBuilder
{
    string Color { get; set; }
    int NumDoors { get; set; }

    Car GetResult();
}

Then implement a concrete builder of a car:

public class HondaBuilder : ICarBuilder
{
    public string Color { get; set; }
    public int NumDoors { get; set; }

    public Car GetResult() 
    {
        return NumDoors == 2 ? new Car("Honda", "Civic", Color, NumDoors) : null;
    }
}

Next is the director class, which contains the flow or algorithm to construct the car using the car builder.

public class CarBuildDirector
{
    private ICarBuilder _builder;

    public CarBuildDirector(ICarBuilder builder)
    {
        _builder = builder;
    }

    public void Construct()
    {
        _builder.Color = "Blue";
        _builder.NumDoors = 2;
    }
}

Finally, let's build a Honda in the client application:

public class Client
{
    public void GoingToBuildCars()
    {
        HondaBuilder builder = new HondaBuilder();
        CarBuildDirector director = new CarBuildDirector(builder);

        director.Construct();
        Car myHonda = builder.GetResult();
    }
}

The director assembles a car instance and delegates the construction to a separate builder object that has been given to the director by the client .

What Are the Advantages?

  • The ConcreteBuilder gathers all components it needs in the same scope. The client of the builder doesn’t see anything about the car (product).

  • It follows the dependency inversion principle. The builder interface plays a key role in the builder pattern because both the director and ConcreteBuilder follow the same rule to build a car. 

  • One build process can build many different kinds of cars. 

  • The parameters to the constructor are reduced.

  • The object is always instantiated in a complete state (i.e.,  builder.GetResult();).

Prototype Pattern

Prototype is the next design pattern. The definition from Design Patterns is:

Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype.

It's similar to how cells in your body create new ones: when a cell splits, it creates an exact duplicate.

In software terms, you can clone an object instantly rather than creating another one from scratch. In the previous chapters, you learned to create an instance of a class from a builder or factory pattern. Now, you can take that builder or factory and clone it.

The new clone (copy) of an object is completely independent of the original and can be used for any purpose without affecting the original. Also, there’s no limit for copying; any existing object can be cloned.

In C#, use the  ICloneable  interface to make your class cloneable. 

There are two types of cloning: shallow copy and deep copy.  

The shallow copy creates a new object from the existing one and then copies the value type fields of the current object to the new one. But, in the case of reference types, it only copies the reference. Thus, the original and the clone refer to the same object in cases of reference types. 

In the following example, we’re going to clone a simple  Employee  class.

public class Employee : ICloneable
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Department { get; set; }
    public Address EmpAddress { get; set; }

    public object Clone() 
    {
        return this.MemberwiseClone();
    }
}

public class Address
{
    public string Street { get; set; }
}

Let’s see how they are used in the console client application:

public static void Main(string[] args)
{
    Employee emp1 = new Employee();
    emp1.FirstName = "Joe";
    emp1.LastName = "Anderson";
    emp1.Department = “IT”;
    emp1.EmpAddress = new Address() { Street = "1234 Lane South" };
    
    Employee emp2 = (Employee)emp1.Clone();
    emp2.FirstName = "Bob";
    emp2.EmpAddress.Street = "989 Sunset Lane";
}

Notice that the  Clone()  method contains the .NET  MemberwiseClone()  method. This is how you make a shallow copy.

With the example above:

  1. I created an object for  emp1  and set some values. 

  2. Then, I created the second object,  emp2, using the  Clone()  method. 

  3. Because I used the  MemberwiseClone(), I created a shallow copy, which copied the value type fields:  FirstName,  LastName, and  Department

  4. But,  EmpAddress  is a reference type, and if I change the  Street  in  emp1, it will change the  Street  in  emp2

In this case, you probably don’t want the  EmpAddress  changing values between cloned objects. To get around this, do a deep copy, which will create the new object from the existing one. The value type fields and the reference types will be copied. 

So, let’s modify the example above to perform a deep copy. Change the  Address  class:

public class Address : ICloneable
{
    public string Street { get; set; }

    public object Clone() 
    {
        return this.MemberwiseClone();
    }
}

Then, one small change to the  Clone()  method in the  Employee  class:

public object Clone() 
{
    this.EmpAddress = (Address)EmpAddress.Clone();
    return this.MemberwiseClone();
}

Now, if you change the  Street  in  emp1.EmpAddress, it will not affect the  Street  in  emp2.EmpAdress.

What Are the Advantages?

  • It eliminates the overhead of initializing an object, which can sometimes be expensive.

  • It simplifies and can optimize instances where multiple objects of the same type have mostly the same data. 

  • It enables adding and removing objects at run time. 

  • It enables configuring application classes dynamically.

Singleton Pattern

The last creational design pattern we’ll look at is the singleton pattern, which is also the simplest. The singleton pattern restricts the instantiation of a class to a single instance.

The implementation of a singleton class should have the following:

  1. It should only have one instance.

  2. The instance should be globally available. 

It is also the most controversial pattern because it’s considered an anti-pattern.

Why?

  • It is frequently used in situations that are not beneficial.

  • It breaks the “S” in SOLID; the single responsibility principle. The job of a  Singleton  class is to create an instance of itself. But, this issue can be solved by other means. 

  • It can’t be sub-classed. 

  • It can hide dependencies. 

  • It is difficult to unit test.

So, if they’re so bad, why use them?

They come in handy for logging, communicating, configuring, caching, and other resources that require a global access point. 

Let’s take a look at a simple example of the singleton pattern:

public sealed class MySingleton
{
    private static MySingleton _instance;

    private MySingleton()
    {
    }

    public static MySingleton Instance()
    {
        if(_instance == null)
        {
            _instance = new MySingleton();
        }
        return _instance;
    }
}

What’s going on with this class?

  • The first thing you’ll notice is that I  sealed  the class. By doing so, I ensure that the class can’t be inherited and instantiation is restricted in the derived class.

  • Secondly, the private constructor,  MySingleton(), ensures that the class is not going to be instantiated from outside. 

  • Finally, the public property  Instance  is used to return only one instance of the class by checking the value of the private variable instance. 

Now, let’s take a look at the client application that will use the singleton class.

static void Main(string[] args)
{
    MySingleton single1 = MySingleton.Instance();
    MySingleton single2 = MySingleton.Instance();

    if(single1 == single2)
    {
        Console.WriteLine("These two objects are the same.");
    }
}

Output:

These two objects are the same.

As you can see above, the singleton pattern assures only a single instance of the class can be created.

Let’s Recap!

  • Creational design patterns deal with object creation.

    • The factory method pattern offers great flexibility in creating different objects.

    • The builder pattern creates complex objects in a step-by-step fashion.

    • The prototype pattern creates copies of pre-existing objects of the same class.

    • The singleton pattern assures only a single instance of the class can be created.

Now that we’ve covered some creational design patterns, let’s look at some examples in the second group: structural design patterns. 

Example of certificate of achievement
Example of certificate of achievement