Creational Design Patterns

 
1. Builder Design Pattern
       
       Builder is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.

There are many problems with using a constructor.

·       Many Constructor, More code
·       Passing null while creating an object.
·       Unreadable code
·       Object obj is a mutable object.

Object created by builder design pattern is always immutable.

public class Program
{
    public static void Main()
    {
		var emp = new EmployeeBuilder()
						.SetAge(26)
						.SetName("Arun")
						.build();
			emp.DoOpertion();
						
    }
} 

public class EmployeeBuilder
{
	private int iAge {get; set;}
	private string strName {get; set;}
	
	public EmployeeBuilder SetAge(int age)
	{
		iAge = age;
		return this;
	}
	
	public EmployeeBuilder SetName(string name)
	{
		strName = name;
		return this;
	}
	
	public Employee build()
	{
		return new Employee(iAge, strName);
	}
}

public class Employee
{
	private int iAge;
	private string Name;
	public Employee(int age, string strName)
	{
		iAge = age;
		Name = strName;
	}
	
	public void DoOpertion()
	{
		Console.WriteLine(Name + " Age Of "+ iAge +" Painting the house");
	}
}


 
Class Design Diagram




2. Factory Design Pattern


Factory Method is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.

Problem
Imagine that you’re creating a logistics management application. The first version of your app can only handle transportation by trucks, so the bulk of your code lives inside the Truck class.


After a while, your app becomes pretty popular. Each day you receive dozens of requests from sea transportation companies to incorporate sea logistics into the app.

Solution
The Factory Method pattern suggests that you replace direct object construction calls (using the new operator) with calls to a special factory method. Don’t worry: the objects are still created via the new operator, but it’s being called from within the factory method. Objects returned by a factory method are often referred to as products.

At first glance, this change may look pointless: we just moved the constructor call from one part of the program to another. However, consider this: now you can override the factory method in a subclass and change the class of products being created by the method.


There’s a slight limitation though: subclasses may return different types of products only if these products have a common base class or interface. Also, the factory method in the base class should have its return type declared as this interface.

For example, both Truck and Ship classes should implement the Transport interface, which declares a method called deliver. Each class implements this method differently: trucks deliver cargo by land, ships deliver cargo by sea. The factory method in the RoadLogistics class returns truck objects, whereas the factory method in the SeaLogistics class returns ships.


The code that uses the factory method (often called the client code) doesn’t see a difference between the actual products returned by various subclasses. The client treats all the products as abstract Transport. The client knows that all transport objects are supposed to have the deliver method, but exactly how it works isn’t important to the client.

Class Diagram
Code:

In C#, the Factory Design Pattern can be implemented as follows:

1. Define an interface or an abstract class that will serve as the product of the factory:

public interface IProduct
{
    void Operation();
}

 
2. Create concrete implementations of the interface:

public class ConcreteProductA : IProduct
{
    public void Operation()
    {
        //implementation for ConcreteProductA
    }
}

public class ConcreteProductB : IProduct
{
    public void Operation()
    {
        //implementation for ConcreteProductB
    }
}


3. Create the factory class that will create the objects based on some input:

public class ProductFactory
{
    public static IProduct CreateProduct(string type)
    {
        switch (type)
        {
            case "A":
                return new ConcreteProductA();
            case "B":
                return new ConcreteProductB();
            default:
                throw new ArgumentException("Invalid type", "type");
        }
    }
}

 
4. Use the factory to create the objects:

IProduct productA = ProductFactory.CreateProduct("A");
IProduct productB = ProductFactory.CreateProduct("B");

 
In this example, the ProductFactory creates the objects based on the input string, which could come from user input or some configuration file. The factory returns an instance of the concrete product that was created, which can then be used by the client code.

The Factory Design Pattern is useful in situations where the creation of an object involves complex logic or there are multiple types of objects that need to be created. By encapsulating the creation logic in a factory class, the client code can remain simple and decoupled from the implementation details of the product classes.


3. Prototype Design Pattern

Prototype is a creational design pattern that lets you copy existing objects without making your code dependent on their classes.

To simplify the above definition, we can say that, the Prototype Design Pattern gives us a way to create new objects from the existing instance of the object. That means it clone the existing object with its data into a new object. If we do any changes to the cloned object (i.e. new object) then it does not affect the original object.

Problem in Cloning:

In C#, when we try to copy one object to another object using the assignment (=) operator, then both the objects will share the same memory address. And the reason is the assignment operator (=) copies the reference, not the object except when there is a value type field. This operator will always copy the reference, not the actual object. 
  
using System;
namespace PrototypeDesignPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            Employee emp1 = new Employee();
            emp1.Name = "Anurag";
            emp1.Department = "IT";
            Employee emp2 = emp1;
            emp2.Name = "Pranaya";
            Console.WriteLine("Employee 1: ");
            Console.WriteLine("Name: " + emp1.Name + ", Department: " + emp1.Department);
            Console.WriteLine("Employee 2: ");
            Console.WriteLine("Name: " + emp2.Name + ", Department: " + emp2.Department);
            Console.Read();
        }
    }

    public class Employee
    {
        public string Name { get; set; }
        public string Department { get; set; }
    }
}

Output

Employee 1: 
Name: Pranaya, Department: IT
Employee 2: 
Name: Pranaya, Department: IT

We have one Employee class. Then we create an instance of Employee class (i.e. emp1) and set its Name and Department properties. Then we create another employee instance (i.e. emp2) by assigning the existing employee object (i.e. emp1). When we change the Name of emp2 then it also changes the name of the emp1. 

Solution:

When we talk about object cloning it means it is all about the call by value. So, if we do any changes to one object then it will not affect the other object. Let us see how to clone the object to another object. To do so, C# provides one method i.e. MemberwiseClone which will create a new complete copy of the object.


using System;
namespace PrototypeDesignPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            Employee emp1 = new Employee();
            emp1.Name = "Anurag";
            emp1.Department = "IT";
            Employee emp2 = emp1.GetClone();
            emp2.Name = "Pranaya";
            
            Console.WriteLine("Employee 1: ");
            Console.WriteLine("Name: " + emp1.Name + ", Department: " + emp1.Department);
            Console.WriteLine("Employee 2: ");
            Console.WriteLine("Name: " + emp2.Name + ", Department: " + emp2.Department);
            Console.Read();
        }
    }
	
    public class Employee
    {
        public string Name { get; set; }
        public string Department { get; set; }
        public Employee GetClone()
        {
            return (Employee)this.MemberwiseClone();
        }
    }
}

 
Output

In Employee class, we created one method i.e. GetClone and as part of that method, we are returning a clone object using the MemberwiseClone method. Then from the client code, first we are creating a new instance of the Employee class and assigning the properties with some values. Next, we are creating the second object by calling the GetClone method which in turn returns a new complete copy of the emp1 object. Now, both the objects (i.e. emp1 and emp2) are independent and if we do any changes to any object, then it will not affect other objects.

Employee 1: 
Name: Anurag, Department: IT
Employee 2: 
Name: Pranaya, Department: IT

Points to Remember:
  1. The MemberwiseClone method is part of the System.Object class and creates a shallow copy of the given object.
  2. MemberwiseClone Method only copies the non-static fields of the object to the new object
  3. In the process of copying, if a field is a value type, a bit by bit copy of the field is performed. If a field is a reference type, the reference is copied but the referenced object is not.
Class Diagram:


4. Singleton Design Pattern

Singleton is a creational design pattern that lets you ensure that a class has only one instance, while providing a global access point to this instance.


Implementation Guidelines:
  1. Declare a constructor that should be private and parameterless.
  2. The class should be declared as sealed which will ensure that it cannot be inherited.
  3. You need to create a private static variable that is going to hold a reference to the single created instance of the class if any.
  4. You also need to create a public static property/method which will return the single-created instance of the singleton class.
Use:
  1. To reduce the overhead of instantiating a heavy object again and again.
  2. To share common data
  3. A single global point of access to a particular instance, so it is easy to maintain.
  4. It can be lazy-loaded and also has Static Initialization.

Disadvantages:
Unit testing is very difficult


Real-time scenarios:
  1. Create Database connections as Singleton which can improve the performance of the application.
  2. Create your Logger as Singleton then it will improve the performance of the I/O operation.
  3. Data sharing: If you have any constant values or configuration values then you can keep these values in Singleton So that these can be read by other components of the application.
  4. Caching: As we know fetching the data from a database is a time-consuming process.
Singleton Basic

namespace SingletonDemo
{
    public sealed class Singleton
    {
        private static int counter = 0;
        private static Singleton instance = null;
        public static Singleton GetInstance
        {
            get
            {
                if (instance == null)
                    instance = new Singleton();
                return instance;
            }
        }
        
        private Singleton()
        {
            counter++;
            Console.WriteLine("Counter Value " + counter.ToString());
        }

        public void PrintDetails(string message)
        {
            Console.WriteLine(message);
        }
    }
	
class Program
    {
        static void Main(string[] args)
        {
            Singleton fromTeachaer = Singleton.GetInstance;
            fromTeachaer.PrintDetails("From Teacher");
            Singleton fromStudent = Singleton.GetInstance;
            fromStudent.PrintDetails("From Student");

            Console.ReadLine();
        }
    }
}

Output:

Counter Value  1
From Teacher
From Student

Thread-safe : Problem

namespace SingletonDemo
{
    public sealed class Singleton
    {
        private static int counter = 0;     
        private static Singleton instance = null;  
        public static Singleton GetInstance
        {
            get
            {
                if (instance == null)
                    instance = new Singleton();
                return instance;
            }
        }  
        private Singleton()
        {
            counter++;
            Console.WriteLine("Counter Value " + counter.ToString());
        }    
        public void PrintDetails(string message)
        {
            Console.WriteLine(message);
        }
    }
	
class Program
    {
        static void Main(string[] args)
        {
            Parallel.Invoke(
                () => PrintTeacherDetails(),
                () => PrintStudentdetails()
                );
            Console.ReadLine();
        }
        private static void PrintTeacherDetails()
        {
            Singleton fromTeacher = Singleton.GetInstance;
            fromTeacher.PrintDetails("From Teacher");
        }
        private static void PrintStudentdetails()
        {
            Singleton fromStudent = Singleton.GetInstance;
            fromStudent.PrintDetails("From Student");
        }
    }
}

Output:

Counter Value  2
From Student
Counter Value  1
From Teacher

Thread-safe : Solution

namespace SingletonDemo
{
    public sealed class Singleton
    {
        private static int counter = 0;
        private static readonly object Instancelock = new object();
        private Singleton()
        {
            counter++;
            Console.WriteLine("Counter Value " + counter.ToString());
        }
        private static Singleton instance = null;
       
        public static Singleton GetInstance
        {
            get
            {
                if (instance == null)
                {
                    lock (Instancelock)
                    {
                        if (instance == null)
                        {
                            instance = new Singleton();
                        }

                    }
                }
                return instance;
            }
        }

        public void PrintDetails(string message)
        {
            Console.WriteLine(message);
        }
    }
}

Output:

Counter Value  1
From Teacher
From Student

Singleton: lazyLoading

The Lazy or Deferred Loading is a design pattern or you can say it’s a concept that is commonly used to delay the initialization of an object until the point at which it is needed. So the main objective of Lazy loading is to load the object on-demand or you can say object when needed. 

The most important point that you need to remember is the Lazy<T> objects are by default thread-safe.

namespace SingletonDemo
{
    public sealed class Singleton
    {
        private static int counter = 0;

        private Singleton()
        {
            counter++;
            Console.WriteLine("Counter Value " + counter.ToString());
        }

        private static readonly Lazy<Singleton> Instancelock =
                    new Lazy<Singleton>(() => new Singleton());

        public static Singleton GetInstance
        {
            get
            {
                return Instancelock.Value;
            }
        }

        public void PrintDetails(string message)
        {
            Console.WriteLine(message);
        }
    }
}

Class Diagram


5. Abstract Factory Design Pattern

Abstract Factory is a creational design pattern that lets you produce families of related objects without specifying their concrete classes.

Code 

using System;

namespace DoFactory.GangOfFour.Abstract.RealWorld
{
    /// <summary>
    /// MainApp startup class for Real-World
    /// Abstract Factory Design Pattern.
    /// </summary>

    class MainApp
    {
        /// <summary>
        /// Entry point into console application.
        /// </summary>

        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();
        }
    }


    /// <summary>
    /// The 'AbstractFactory' abstract class
    /// </summary>

    abstract class ContinentFactory
    {
        public abstract Herbivore CreateHerbivore();
        public abstract Carnivore CreateCarnivore();
    }

    /// <summary>
    /// The 'ConcreteFactory1' class
    /// </summary>

    class AfricaFactory : ContinentFactory
    {
        public override Herbivore CreateHerbivore()
        {
            return new Wildebeest();
        }
        public override Carnivore CreateCarnivore()
        {
            return new Lion();
        }
    }

    /// <summary>
    /// The 'ConcreteFactory2' class
    /// </summary>

    class AmericaFactory : ContinentFactory
    {
        public override Herbivore CreateHerbivore()
        {
            return new Bison();
        }
        public override Carnivore CreateCarnivore()
        {
            return new Wolf();
        }
    }

    /// <summary>
    /// The 'AbstractProductA' abstract class
    /// </summary>

    abstract class Herbivore
    {
    }

    /// <summary>
    /// The 'AbstractProductB' abstract class
    /// </summary>

    abstract class Carnivore
    {
        public abstract void Eat(Herbivore h);
    }

    /// <summary>
    /// The 'ProductA1' class
    /// </summary>

    class Wildebeest : Herbivore
    {
    }

    /// <summary>
    /// The 'ProductB1' class
    /// </summary>

    class Lion : Carnivore
    {
        public override void Eat(Herbivore h)
        {
            // Eat Wildebeest

            Console.WriteLine(this.GetType().Name +
              " eats " + h.GetType().Name);
        }
    }

    /// <summary>
    /// The 'ProductA2' class
    /// </summary>

    class Bison : Herbivore
    {
    }

    /// <summary>
    /// The 'ProductB2' class
    /// </summary>

    class Wolf : Carnivore
    {
        public override void Eat(Herbivore h)
        {
            // Eat Bison

            Console.WriteLine(this.GetType().Name +
              " eats " + h.GetType().Name);
        }
    }

    /// <summary>
    /// The 'Client' class 
    /// </summary>

    class AnimalWorld
    {
        private Herbivore _herbivore;
        private Carnivore _carnivore;

        // Constructor

        public AnimalWorld(ContinentFactory factory)
        {
            _carnivore = factory.CreateCarnivore();
            _herbivore = factory.CreateHerbivore();
        }

        public void RunFoodChain()
        {
            _carnivore.Eat(_herbivore);
        }
    }
}

Output:

Lion eats Wildebeest
Wolf eats Bison

Class Diagram


No comments:

Post a Comment