14 minute read

10 Design Patterns that every developer should know

How to write reusable and extensible object-oriented software - (Part One)

design patterns

What are design patterns?

Programs need to be flexible, reusable and maintainable, they need to be elegant. Design patterns are elegant solutions to repeating problems in software design. They describe a problem, a solution to the problem, where to apply the solution and it’s consequences. When a developer masters the design patterns he/she can reuse already established designs and avoids common mistakes from the painful experience of others. Mastering design patterns will also equip you with the understanding of common vocabulary and help you communicate with other developers at a more abstract level during analysis and development of software. Design patterns are programming language independent strategies, the patterns represent an idea and not a particular implementation.

Example

You are required to create a class from which only a single object should be created which can be used by other classes.

Solution

The Singleton Design Pattern is the best solution to this problem. We are going to review the pattern later in this article.

History

In 1994, four authors, popularly known as the Gang of Four(GoF) published a book known as Design Patterns - Elements of Reusable software which gave birth to the concept of Design patterns in Software development. The GoF came up with 23 design patterns which can be categorized into 3: Creational, Structural and Behavioural Patterns.

i) Creational Patterns

Creational patterns focuses on the creation of objects i.e how to create objects in a manner that is suitable to the situation. Creation pattern is further divided into five categories:

a) Singleton Pattern

This pattern involves a single class which is responsible for creating an object while making sure only one object gets created. The pattern restricts the instantiation of a class to only one instance. You can use the instance when you need in different parts of your application.

Real life Example

A country can have only one official government regardless of the identities that forms the government.

Computer World Example

In the development of software logging frameworks use the Singleton Pattern a lot. You can also create a single configuration file or database connection string which will be used anywhere in the application when required.Let’s look at a program example where we create an application configuration settings.


//configuration class
    public class ConfigurationManager
    {
        //Store our settings in dictionary - key value pair
        Dictionary<string, string> configs = new Dictionary<string, string>();

        //we need two methods for setting and getting our app settings
        public void setConfigSettings(string key, string value)
        {
            configs.Add(key, value);
        }

        public string getConfigSettings(string key)
        {
            return configs[key];
        }

    }

Let’s create a new setting in the main method.

   class Program
    {

        static void Main(string[] args)
        {
            //Create a new instance of ConfigurationManager
            ConfigurationManager configManager = new ConfigurationManager();
            //Create a new setting
            configManager.setConfigSettings("setting1", "value1");

            //Read the vlaue of setting1 using the configManager instance
            Console.WriteLine(configManager.getConfigSettings("setting1"));

            //Create another instance of ConfigurationManager
            ConfigurationManager configManager2 = new ConfigurationManager();

            //Read the value of setting2 using the configManager2 instance
            Console.WriteLine(configManager2.getConfigSettings("setting1"));

            Console.ReadKey();
        }
    }

In the example above, We have created a new instance of ConfigurationManager and created a new setting. The setting can be accessed anywhere in the application using the configManager instance. When we create another ConfigurationManager instance and try to access setting1, we get a exception saying:

The given key ‘setting1’ was not present in the dictionary.

This can be avoided by simply having only one instance of the ConfigurationManager class which will accessed anywhere in the application when needed. The singleton pattern observes the following rules:

  1. A single private field that holds the instance of the class, it ensures a global point of access. Under the hood, this method calls the private constructor to create an object and saves it in a static field. The sealed keyword ensures that a subclass cannot misuse it.
  2. A private Constructor - You cannot create a new instance of the class outside the class.
  3. Define an operation(method) to return the single instance.
//Configuration class
    public sealed class ConfigurationManager
    {
        //1. single private field that holds the instance of the class 
        private static readonly ConfigurationManager instance = new ConfigurationManager();

        //2. make the constructor private
        private ConfigurationManager() { }

        //3. define an operation to return the single instance
        public static ConfigurationManager Instance
        {
            get
            {
                return instance;
            }

        }

        //Store our settings in dictionary
        Dictionary<string, string> configs = new Dictionary<string, string>();

        //we need two methods for setting and getting our app settings
        public void setConfigSettings(string key, string value)
        {
            configs.Add(key, value);
        }

        public string getConfigSettings(string key)
        {
            return configs[key];
            
        }
    }

The Main Method:

 static void Main(string[] args)
        {
            //get the instance of ConfigurationManager
            ConfigurationManager configManager = ConfigurationManager.Instance;
            //Create a new setting
            configManager.setConfigSettings("setting1", "value1");

            //Read the value of setting1 using the configManager instance
            Console.WriteLine(configManager.getConfigSettings("setting1"));

            Console.ReadKey();
        }

Pros
  1. Ensures that a class has only one instance.
  2. The singleton object is initialized only when it’s requested for the first time.
Cons
  1. Violation of SRP - The pattern violates the Single Responsibily Principle - they control their own creation and lifecycle.
  2. The pattern requires a special treatment in a multithreaded environment. This prevents multiple threads from creating a singleton object multiple times.
  3. Increases tight coupling - Changes to one component in your application is difficult as it would affect all other components that are connected to it.
  4. Unit Testing becomes difficult - Increased coupling makes creating mocks during unit testing difficult. The Singleton Pattern uses Static Method which makes unit testing difficult since you cannot mock or stub.
Will I ever use this pattern?

Absolutely. Singletons are objects of which there can only ever be exactly one instance. The singleton pattern is easy to understand and implement, which makes it a favorite tool of people afflicted with golden hammer syndrome(When the only tool you have is a hammer, everything looks like a nail). it’s common to find Singletons in use where global variables should be used instead.

b) Factory Pattern

According to the gang of four “A Factory is an object that is used for the creation of other objects”. This simply means that a factory is a class with a method which creates and returns different types of objects based on the input parameter. This is used when we have to return the object of one child class when we have a Parent Class and n number of Child Classes. The Factory Pattern let a class differ instatiation to a child class. The pattern creates an object without exposing the object creation logic to the client.

Real World Example

A factory is a place where products are created. Based on the order, a car factory can create different models of a car based on requirement. The same case applies in the Factory Pattern. A factory(a class), will create products(objects) based on incoming parameters.

Example

Let’s understand the Factory Design Pattern using an example. We are going to develop a program that will show credit card details based by the user input.

The following diagram shows three credit cards: Gold Visa, Platinum Visa and MoneyBack MasterCard. The credit cards can be child classes to a CreditCard super class or a super interface. The CreditCard super class can have 3 methods: GetCardType, GetCreditCardLimit and GetCreditCardAnnualCharge. Our program requirement is to ask the user to select a credit card, we will use that input to display the details the card.

Code Implementation without using the Factory Design Pattern

namespace FactoryDesignPattern
{
    //CreditCard Interface
    public interface CreditCard
    {
        string GetCardType();
        decimal GetCreditCardLimit();
        decimal GetCreditCardAnnualCharge();
    }
    //Gold Visa Class
    class GoldVisa : CreditCard
    {
        public string GetCardType()
        {
            return "Gold Visa";
        }

        public decimal GetCreditCardAnnualCharge()
        {
            return 400;
        }

        public decimal GetCreditCardLimit()
        {
            return 400000;
        }
    }
    //Platimun Visa Class
    class PlatinumVisa : CreditCard
    {
        public string GetCardType()
        {
            return "Platimun Visa";
        }

        public decimal GetCreditCardAnnualCharge()
        {
            return 500;
        }

        public decimal GetCreditCardLimit()
        {
            return 500000;
        }
    }

    //MoneyBackMasterCard Class
    class MoneyBackMasterCard : CreditCard
    {
        public string GetCardType()
        {
            return "MoneyBack Mastercard";
        }

        public decimal GetCreditCardAnnualCharge()
        {
            return 600;
        }

        public decimal GetCreditCardLimit()
        {
            return 600000;
        }
    }
}

The client code

In the client code(Program.cs) we will ask the user to select a credit card. We will then create an instance of the selected credit card and print out it’s details in the console.

 class Program
    {

        static void Main(string[] args)
        {

            Console.WriteLine("Select a credit card");
            Console.WriteLine("1.Gold Visa  2. Platinum Visa 3.MoneyBack Mastercard");
            var selectedCreditCard = Convert.ToInt32(Console.ReadLine());

            //Based of the CreditCard Type we are creating the
            //appropriate type instance using if else condition
            CreditCard cardDetails = null;
            if (selectedCreditCard == 1)
            {
                cardDetails = new GoldVisa();
            }
            else if (selectedCreditCard == 2)
            {
                cardDetails = new PlatinumVisa();
            }
            else if (selectedCreditCard == 3)
            {
                cardDetails = new MoneyBackMasterCard();
            }

            if (cardDetails != null)
            {
                Console.WriteLine("selectedCreditCard : " + cardDetails.GetCardType());
                Console.WriteLine("CreditLimit : " + cardDetails.GetCreditCardLimit());
                Console.WriteLine("AnnualCharge :" + cardDetails.GetCreditCardAnnualCharge());
            }

            else
            {
                Console.Write("Invalid Card Type");
            }

            Console.ReadLine();
        }

    }

When we run our program and select a credit card, an object is created and the details of the card are displayed in the console. For case 1, the output is as follows:

selectedCreditCard : Gold Visa
CreditLimit : 400000
AnnualCharge :400
What’s the problem with this implementation?

The code implementation above introduces the following problems:

  1. Tight coupling between the Client class(Program.cs) and the Product class(Gold, Platinum and Money Back).
  2. We will need to modify the Main method in the client class when we create another credit card which violates the open closed principle Open Closed Princle.
Program implemetation using the Factory Design Pattern.

We described the Factory Pattern as a pattern that creates an object without exposing the object creation logic to the client. We can modify the previous implementations as follows to add more flexibility to our program.

  1. Create a CreditCardFactory class(A factory creates products i.e credit card objects)
   public class CreditCardFactory
    {
        public static CreditCard CreateCreditCard(int Creditcard)
        {
            CreditCard cardDetails = null;
            switch (Creditcard)
            {
                case 1:
                    cardDetails = new GoldVisa();
                    break;
                case 2:
                    cardDetails = new PlatinumVisa();
                    break;
                case 3:
                    cardDetails = new MoneyBackMasterCard();
                    break;
                default:
                    throw new ApplicationException("Unknown Credit Card Cannot be created");
            }
            return cardDetails;
        
        }
    }
  1. Modify the Client Class as follows
 class Program
    {

        static void Main(string[] args)
        {

            Console.WriteLine("Select a credit card");
            Console.WriteLine("1.Gold Visa  2. Platinum Visa 3.MoneyBack Mastercard");
            var selectedCreditCard = Convert.ToInt32(Console.ReadLine());

            //Based of the CreditCard Type we are creating the
            //appropriate type the Credit Card Factory Class

            CreditCard cardDetails = CreditCardFactory.CreateCreditCard(selectedCreditCard);
          
            if (cardDetails != null)
            {
                Console.WriteLine("selectedCreditCard : " + cardDetails.GetCardType());
                Console.WriteLine("CreditLimit : " + cardDetails.GetCreditCardLimit());
                Console.WriteLine("AnnualCharge :" + cardDetails.GetCreditCardAnnualCharge());
            }

            else
            {
                Console.Write("Invalid Card Type");
            }

            Console.ReadLine();
        }

    }

The output when we select 1:

selectedCreditCard : Gold Visa
CreditLimit : 400000
AnnualCharge : 400
When to use the Factory Design Pattern

Use the Factory design pattern when you don’t know all the types before hand and the product implementation is going to change over time. The Factory Method separates product construction code from the code that actually uses the product(Client). Therefore it’s easier to extend the product construction code independently from the rest of the code.

Pros
  • You avoid tight coupling between the Client and the Concrete Products.
  • Supports Single Responsibilty Priniciple
  • Supports the Open/Closed Principle. You can introduce new products without breaking the existing code.
Cons

The pattern increases code complexity as one need to introduce a lot of new subclasses.

Will I ever use this pattern?

Absolutely. Any time you need to create groups of related objects, Factory Method is one of the cleanest ways to do so.

C) Builder Design Pattern

Builder is a creational design pattern that enables you to construct complex objects step by step by separating the construction of a complex object from it’s representation. By doing so, a class(the same construction process) can create different representations. The builder pattern is useful when creating objects that have multiple parts. The creation process should be independent of the different parts i.e it should not care how the parts are assembled. This enables us to use the same process to create other representations.

Real World Example

A laptop is an example of a complex object. In order to build a laptop, we have to use small objects such as display, Hard Drive, RAM, Battery, USB ports etc. SO we have to assemble these small objects to build a laptop which is complex object. Depending on a customer request, different objects(hardware parts) can be assembled. A customer can opt for 1TB hard drive with a core i7 processor and another can opt for a 500GB hard drive with a AMD Processor.

Example

Let’s understand the Builder pattern using another example.Suppose we want to develop an application for displaying reports. The reports need to display either in Excel or PDF format depending on the user preference. The two formats are what we call the representations. The structure below illustrates the high level of our requirements.

Here, we are generating reports either in PDF or Excel format. The construction of the reports involves several steps such setting the report type, setting the header, the content and the footer. The construction process of the report representations is mostly similar.

Understanding the structure of the Builder Pattern

]In order to separate the construction process from it’s representation, the builder pattern involves four components.

The four components are:

  • Builder - This is an interface that defines the steps used to make the concrete product.
  • Concrete Builder - This is a class that implements the builder interface and provides implementation to the abstract methods. The concrete builder constructs and assemble the individual parts of the product. It also defines and track the representation it creates.
  • Director - The director takes the individual processes from the builder and defines sequence to build the products.
  • Product - This is a class that defines different parts that will make a product.
Code implementation of the Builder pattern
namespace BuilderPattern
{
    //Abstract Class or Interface to define the parts a product
    public abstract class ReportBuilder
    {
        public Report reportObject;
        public abstract void SetReportType();
        public abstract void SetReportHeader();
        public abstract void SetReportContent();
        public abstract void SetReportFooter();
        public void CreateNewReport()
        {
            reportObject = new Report();
        }
        public Report GetReport()
        {
            return reportObject;
        }
    }

    //Concrete implementation - Excel Report
    public class ExcelReport : ReportBuilder
    {
        public override void SetReportContent()
        {
            reportObject.ReportContent = "Excel Content Section";
        }
        public override void SetReportFooter()
        {
            reportObject.ReportFooter = "Excel Footer";
        }
        public override void SetReportHeader()
        {
            reportObject.ReportHeader = "Excel Header";
        }
        public override void SetReportType()
        {
            reportObject.ReportType = "Excel";
        }
    }

    //Concrete implementation - PDF Report
    public class PDFReport : ReportBuilder
    {
        public override void SetReportContent()
        {
            reportObject.ReportContent = "PDF Content Section";
        }
        public override void SetReportFooter()
        {
            reportObject.ReportFooter = "PDF Footer";
        }
        public override void SetReportHeader()
        {
            reportObject.ReportHeader = "PDF Header";
        }
        public override void SetReportType()
        {
            reportObject.ReportType = "PDF";
        }
    }

    //Product - Defines the different parts that will make a product
    public class Report
    {
        public string ReportType { get; set; }
        public string ReportHeader { get; set; }
        public string ReportFooter { get; set; }
        public string ReportContent { get; set; }
        public void DisplayReport()
        {
            Console.WriteLine("Report Type :" + ReportType);
            Console.WriteLine("Header :" + ReportHeader);
            Console.WriteLine("Content :" + ReportContent);
            Console.WriteLine("Footer :" + ReportFooter);
        }
    }

    //Director - Takes the individual process from the builder and defines a sequense to build products
    public class ReportDirector
    {
        public Report MakeReport(ReportBuilder reportBuilder)
        {
            reportBuilder.CreateNewReport();
            reportBuilder.SetReportType();
            reportBuilder.SetReportHeader();
            reportBuilder.SetReportContent();
            reportBuilder.SetReportFooter();
            return reportBuilder.GetReport();
        }
    }

    class Program
    {

        static void Main(string[] args)
        {
            // Client Code
            Report report;
            ReportDirector reportDirector = new ReportDirector();
            // Construct and display Reports
            PDFReport pdfReport = new PDFReport();
            report = reportDirector.MakeReport(pdfReport);
            report.DisplayReport();
            Console.WriteLine("-------------------");
            ExcelReport excelReport = new ExcelReport();
            report = reportDirector.MakeReport(excelReport);
            report.DisplayReport();

            Console.ReadKey();
        }

    }
}

Output
Report Type :PDF
Header :PDF Header
Content :PDF Content Section
Footer :PDF Footer
-------------------
Report Type :Excel
Header :Excel Header
Content :Excel Content Section
Footer :Excel Footer
When to use the Factory Design Pattern

Use builder pattern when you need to do lots of things to build an object.

Pros
  • You direct the builder to build a complex object step by step hide the implementation details of how it is construction process.
  • The pattern supports different products using the same construction process.
  • Supports Single Responsibilty Priniciple. You can isolate complex construction code from the business logic of the product.
Cons

The overall complexity of the code increases since the pattern requires creating multiple new classes.

Will I ever use this pattern?

Probably. Using the pattern involves a lot of work to build these supposedly related items in a reusable manner. The patterns depends on too many assumptions to rely on using this pattern in common projects. The Builder pattern has some uses, just not a lot of them.

Summary on Creational Design patterns

Creational design patterns deal with object creation mechanisms, it creates objects in a manner suitable to the situation. The basic form of object creation could result in design problems or in added complexity to the design. Creational design patterns solve this problem by somehow controlling this object creation. The most popular creational design pattern are:

  • Singleton Pattern, which ensures that a class only has one instance, and provides a global point of access to it.
  • Factory Pattern, which allows a class to defer instantiation to subclasses.
  • Builder Pattern, which separates the construction of a complex object from its representation so that the same construction process can create different representations.

Watch out for part II where we will talk about Structural Design patterns.

Thank You!

I’d love to keep in touch! Feel free to follow me on Twitter at @codewithfed. If you enjoyed this article please consider sponsoring me for more. Your feedback is welcome anytime. Thanks again!

Updated:

Leave a comment