DEV Community

Cover image for Design Patterns Simplified in Dotnet(With Realtime Examples) - Part 1
varma36a
varma36a

Posted on • Updated on

Design Patterns Simplified in Dotnet(With Realtime Examples) - Part 1

Creational Design Patterns

Design Patterns with CSharp
Design patterns are mostly used and important in our daily lives, for interviews as well as day-to-day work.

One of the most popular books for learning design patterns is “Design Patterns: Elements of Reusable Object-Oriented Software,” which was authored in 1994 by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Due to the four authors, it has become known as the Gang of Four (GoF) design patterns book and is often abbreviated as “GoF Design Patterns.”

Creational Design Patterns

Deals with the process of object creation in such a way that they can be decoupled from their implementing system.

Provides more flexibility in deciding which objects need to be created for a given use case or scenario.

There are five design patterns in this category, which are:

  1. Singleton
  2. Prototype
  3. Factory Method
  4. Abstract Factory
  5. Builder

Singleton Pattern

This pattern ensures that a class has only one instance and provides a global point of access to it.

  • The singleton class includes:
  • static private instance
  • Private Constructor
  • Public Instance Property or GetInstance() method

Image description

Singleton Pattern Examples

You need a single instance of an object throughout the application.

  • Exception logging
  • Database Manager
  • Business Layer Manager

private static ExceptionHandler _instance = null;
        private static StreamWriter _streamWriter;
        private static object _lock = new object();
        private ExceptionHandler()
        {
            _streamWriter = new StreamWriter(@"C:\Error.txt");
        }

        public static ExceptionHandler Instance
        {
            get
            {
                lock (_lock)
                {
                    if (_instance == null)
                        _instance = new ExceptionHandler();

                    return _instance;
                }
            }
        }

        public void WriteLog(string message)
        {
            _streamWriter.WriteLine(message);
            _streamWriter.Flush();
        }
    }




        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }


        public void Add()
        {
            try
            {
                // TO DO:

            }
            catch (Exception ex)
            {
               // var obj = new ExceptionHandler();
                ExceptionHandler.Instance.WriteLog(ex.Message);
            }
        }

Enter fullscreen mode Exit fullscreen mode

Prototype Pattern

  • Used to create a duplicate object or clone of the current object to enhance performance.
  • Used when the creation of an object is costly or complex.

Image description

Prototype Pattern Examples

App needs copies of objects, where objects

Are not simple value types.

Have other objects as properties like Customer has Address object property


public class Address
    {
        public string Address1 { get; set; }
        public string Address2 { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Zip { get; set; }

        public Address Clone()
        {
            return this.MemberwiseClone() as Address;
        }
    }



public abstract class CustomerReport
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public Address PermanentAddress { get; set; }
        public Address CurrentAddress { get; set; }

        public abstract object Clone();
        public abstract CustomerReport DeepClone();
    }

  public class GoldCustomerReport : CustomerReport
    {
        public override object Clone()
        {
            return this.MemberwiseClone() as CustomerReport;
        }

        public override CustomerReport DeepClone()
        {
            CustomerReport customer =  this.MemberwiseClone() as CustomerReport;

            customer.CurrentAddress = this.CurrentAddress.Clone();`
            return customer;
        }
    }

static void Main(string[] args)
        {
            GoldCustomerReport goldCustomer = new GoldCustomerReport();

            goldCustomer.Id = 1;

            goldCustomer.CurrentAddress = new Address
            {
                Address1 = "Lagpat Nager",
                Address2 = "Near Delhi Metro",
                State = "Delhi",
                City = "Delhi",
                Zip = "110091"
            };



            //deep copy

         CustomerReport clone = (CustomerReport)goldCustomer.DeepClone();

//parent copy
            Console.WriteLine($"Address1: {clone.CurrentAddress.Address1}");
//child copy
            Console.WriteLine($"Address1: {goldCustomer.CurrentAddress.Address1}");

            clone.CurrentAddress.Address1 = "Delhi";

//parent copy
            Console.WriteLine($"Address1: {clone.CurrentAddress.Address1}");
//child copy
            Console.WriteLine($"Address1: {goldCustomer.CurrentAddress.Address1}");
        }

Enter fullscreen mode Exit fullscreen mode

Shallow:

Breadth vs Depth; think in terms of a tree of references with your object as the root node.

Shallow:

Image description

The variables A and B refer to different areas of memory, when B is assigned to A the two variables refer to the same area of memory. Later modifications to the contents of either are instantly reflected in the contents of the other, as they share contents.

Deep:

Image description

The variables A and B refer to different areas of memory, when B is assigned to A the values in the memory area which A points to are copied into the memory area to which B points. Later modifications to the contents of either remain unique to A or B; the contents are not shared.

Factory Method

Allow us to create object without exposing the creation logic.

An interface is used for creating an object, but subclass decide which class to instantiate.
Factory Method Real Life Example

Image description

Image description

Factory Method Examples

  • Need to create different logger types
  • Console Logger
  • Database Logger
  • File Logger etc.
  • Need to create different report types
  • PDF
  • Word etc.

static void Main(string[] args)
        {
            ILogger logger = LoggerFactory.CreateLogger(LoggerType.Console);
            Client client = new Client(logger);
            client.Add();
        }

public static ILogger CreateLogger(LoggerType type)
        {
            switch (type)
            {
                case LoggerType.File:
                    return new FileLogger();
                case LoggerType.Console:
                default:
                    return new ConsoleLogger();
            }
        }

  public class ConsoleLogger : ILogger
    {
        public void LogError(string error)
        {
            Console.WriteLine(error);
        }
    }


  public class FileLogger : ILogger
    {
        public void LogError(string error)
        {
            File.WriteAllText(@"c:\Error.txt", error);
        }
    }


 public interface ILogger
    {
        void LogError(string error);
    }


Enter fullscreen mode Exit fullscreen mode

Abstract Factory

Image description

Abstract Factory patterns acts a super- factory which creates other factories. This pattern is also called as Factory of factories.
In Abstract Factory pattern an interface is responsible for creating a factory of related objects, or dependent objects without specifying their concrete classes.

Image description

Abstract Factory Examples

• Need to support multiple database types
• SQL Server
• Oracle
• MySQL etc.


static void Main(string[] args)
        {
            SqlServerFactory sqlFactory = new SqlServerFactory();
            Client client = new Client(sqlFactory);

            client.Add();
        }

public abstract class DbFactory
    {
        public abstract DbConnection GetConnection();
        public abstract DbCommand GetCommand();
    }


 public class MySqlFactory : DbFactory
    {
        private MySqlConnection _mySqlConnection;
        public override DbCommand GetCommand()
        {
            MySqlCommand command = new MySqlCommand();
            command.Connection = _mySqlConnection;
            return command;
        }

        public override DbConnection GetConnection()
        {
            string strCon = @"server=localhost;user id=root;password=root;database=MyDB";
            _mySqlConnection = new MySqlConnection(strCon);
            return _mySqlConnection;
        }
    }




public class SqlServerFactory : DbFactory
    {
        private SqlConnection _sqlConnection;
        public override DbCommand GetCommand()
        {
            SqlCommand command = new SqlCommand();
            command.Connection = _sqlConnection;
            return command;
        }

        public override DbConnection GetConnection()
        {
            string strCon = @"data source=Shailendra\SqlExpress; initial catalog=MyDB;persist security info=True;user id=sa;password=dotnettricks;";
            _sqlConnection = new SqlConnection(strCon);
            return _sqlConnection;
        }
    }



Enter fullscreen mode Exit fullscreen mode

Factory Pattern vs. Abstract Factory Pattern

• Use inheritance and relies on concrete class to create object.
• Produce only one product.
• Hides the creation process of single object
• Abstract Factory may use Singleton design pattern for creating objects.

• Use composition to delegate object creation responsibility to another class
• Produce a family of related products
• Hides the creation process of a family of related objects
• Abstract Factory may use Factory pattern or Builder design pattern or prototype design pattern for creating objects. It completely depends upon your implementation.

Builder Pattern

• Used to build a complex object by using a step by step approach.
• Builder interface defines the steps to build the final object.
• A class that is known as Director, controls the object creation process.
• Describes a way to separate an object from its construction. The same construction method can create different representation of the object.

Image description

Image description

Builder Pattern Examples

• Construct Report/Email Builder Class
• Set Header
• Set Body
• Set Footer



 static void Main(string[] args)
        {
            var products = new List<Product>
                {
                    new Product { Name = "Monitor", Price = 200.50 },
                    new Product { Name = "Mouse", Price = 20.41 },
                    new Product { Name = "Keyboard", Price = 30.15}
                };
            var builder = new ReportBuilder(products);
            var director = new ReportDirector(builder);
            director.BuildStockReport();

            var report = builder.GetReport();
            Console.WriteLine(report);
        }



public class ReportDirector
    {
        private IReportBuilder _reportBuilder;
        public ReportDirector(IReportBuilder reportBuilder)
        {
            _reportBuilder = reportBuilder;
        }

        public void BuildStockReport()
        {
            _reportBuilder.BuildHeader();
            _reportBuilder.BuildBody();
            _reportBuilder.BuildFooter();
        }
    }




 public class ReportBuilder : IReportBuilder
    {
        private Report _report;
        private IEnumerable<Product> _products;
        public ReportBuilder(IEnumerable<Product> products)
        {
            _products = products;
            _report = new Report();
        }
        public void BuildBody()
        {
            _report.Body = "";
            //_report.Body = string.Join(Environment.NewLine, _products.Select(p => $"Product name: {p.Name}, product price: {p.Price}"));
            foreach (var item in _products)
            {
                _report.Body += $"{Environment.NewLine}Product name: {item.Name}, product price: {item.Price}";
            }
        }

        public void BuildFooter()
        {
            _report.Footer = $"******** Report Footer *********";
        }

        public void BuildHeader()
        {
            _report.Header = $"******** Report Header *********";
        }

        public Report GetReport()
        {
            var report = _report;
            Clear();
            return report;
        }

        private void Clear()
        {
            _report = new Report();
        }
    }



 public class Report
    {
        public string Header { get; set; }
        public string Body { get; set; }
        public string Footer { get; set; }

        public override string ToString()
        {
            return new StringBuilder()
                .AppendLine(Header)
                .AppendLine(Body)
                .AppendLine(Footer)
                .ToString();
        }
    }


  public interface IReportBuilder
    {
        void BuildHeader();
        void BuildBody();
        void BuildFooter();

        public Report GetReport();
    }

public class Product
    {
        public string Name { get; set; }
        public double Price { get; set; }
    }

Enter fullscreen mode Exit fullscreen mode

Abstract Factory Pattern vs. Builder Pattern

• Abstract Factory Pattern will return the Instance directly.
• Does not keep the track of its created Object.

• This pattern creates the object in several steps.
• Keeps the reference of its created Object.

Top comments (0)