Design patterns in C# with realtime examples.



Design patterns are general reusable solutions to commonly occurring problems in software design. They provide developers with a way to solve recurring problems in a standardised way, leading to more efficient and maintainable code.

Singleton Pattern - Ensures that only one instance of a class is created and provides a global point of access to it. 

Example - The Singleton pattern is used when we want to ensure that only one instance of a class exists and provide a global point of access to it. 

 


  public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static Singleton Instance {
        get {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
}
In this implementation, the constructor of the Singleton class is private, which means that it                cannot be called from outside the class. The only way to get an instance of the Singleton                        class is by calling the static Instance property.

The Instance property checks if an instance of the Singleton class has already been created. If it has, it returns that instance. If it hasn't, it creates a new instance and returns it.
  
    // Please paste your code here and press Beautify button
    
Singleton instance1 = Singleton.Instance;
Singleton instance2 = Singleton.Instance;

// instance1 and instance2 refer to the same object
if (instance1 == instance2) {
    Console.WriteLine("Only one instance exists.");
}
    
  

}

 

In this example, we create two instances of the Singleton class by calling the Instance property twice. Since the Instance property ensures that only one instance of the Singleton class exists, instance1 and instance2 refer to the same object.


The Singleton pattern is used when we want to ensure that only one instance of a class exists and provide a global point of access to it.

Let's say we have a class DatabaseConnection that establishes a connection to a database. We want to ensure that only one connection to the database is created and shared across the entire application. To achieve this, we can use the Singleton pattern.

First, we create the DatabaseConnection class:
  
public class DatabaseConnection {
    private static DatabaseConnection _instance;
    private readonly string _connectionString;
    private DatabaseConnection() {
        _connectionString = "connection string here";
        // code to establish database connection here
    }
    public static DatabaseConnection GetInstance() {
        if (_instance == null) {
            _instance = new DatabaseConnection();
        }
        return _instance;
    }
    public void ExecuteQuery(string query) {
        // code to execute query here
    }
}
  
In this implementation, the constructor of the DatabaseConnection class is private, which means that it cannot be called from outside the class. The only way to get an instance of the DatabaseConnection class is by calling the static GetInstance() method.

The GetInstance() method checks if an instance of the DatabaseConnection class has already been created. If it has, it returns that instance. If it hasn't, it creates a new instance and returns it.

The ExecuteQuery() method can be used to execute SQL queries on the database.

Now, let's say we want to use the DatabaseConnection class in our application:
 
public class Program {
    static void Main(string[] args) {
        DatabaseConnection dbConn1 = DatabaseConnection.GetInstance();
        DatabaseConnection dbConn2 = DatabaseConnection.GetInstance();
        dbConn1.ExecuteQuery("SELECT * FROM users");
        dbConn2.ExecuteQuery("SELECT * FROM orders");
        if (dbConn1 == dbConn2) {
            Console.WriteLine("Only one database connection exists.");
        }
    }
}
   
In this example, we create two instances of the DatabaseConnection class by calling the GetInstance() method twice. Since the GetInstance() method ensures that only one instance of the DatabaseConnection class exists, dbConn1 and dbConn2 refer to the same object.

By using the Singleton pattern, we ensure that only one connection to the database is created and shared across the entire application, leading to more efficient and maintainable code.
---------------------------------------------------------------------------------------------------------------------


Let's say we have a class ConfigurationManager that reads configuration settings from a file. We want to ensure that only one instance of the ConfigurationManager class exists and that all instances of the class share the same configuration settings.

We can use the Singleton pattern to achieve this:
 
public class ConfigurationManager {
    private static ConfigurationManager _instance;
    private readonly Dictionary < string, string > _settings;
    private ConfigurationManager() {
        // code to read configuration settings from file
        _settings = new Dictionary < string, string > {
            {
                "setting1",
                "value1"
            },
            {
                "setting2",
                "value2"
            },
            {
                "setting3",
                "value3"
            }
        };
    }
    public static ConfigurationManager GetInstance() {
        if (_instance == null) {
            _instance = new ConfigurationManager();
        }
        return _instance;
    }
    public string GetSetting(string key) {
        if (_settings.ContainsKey(key)) {
            return _settings[key];
        }
        return null;
    }
}
  
In this implementation, the constructor of the ConfigurationManager class is private, which means that it cannot be called from outside the class. The only way to get an instance of the ConfigurationManager class is by calling the static GetInstance() method.

The GetInstance() method checks if an instance of the ConfigurationManager class has already been created. If it has, it returns that instance. If it hasn't, it creates a new instance and returns it.

The GetSetting() method can be used to retrieve a configuration setting based on a key.

Now, let's say we want to use the ConfigurationManager class in our application:
 
public class Program {
    static void Main(string[] args) {
        ConfigurationManager config1 = ConfigurationManager.GetInstance();
        ConfigurationManager config2 = ConfigurationManager.GetInstance();
        string setting1 = config1.GetSetting("setting1");
        string setting2 = config2.GetSetting("setting2");
        if (config1 == config2) {
            Console.WriteLine("Only one configuration manager exists.");
        }
    }
}
  
In this example, we create two instances of the ConfigurationManager class by calling the GetInstance() method twice. Since the GetInstance() method ensures that only one instance of the ConfigurationManager class exists, config1 and config2 refer to the same object.

By using the Singleton pattern, we ensure that only one set of configuration settings is read from the file and shared across the entire application, leading to more efficient and consistent behavior.

---------------------------------------------------------------------------------------------------------------------
Provided a more detailed explanation of the Singleton pattern and an example.

The Singleton pattern is a creational design pattern that ensures that only one instance of a class is created and provides a global point of access to it. This pattern is useful in situations where we want to ensure that only one object of a certain type exists in our application. This can be important for many reasons, such as ensuring efficient use of resources or preventing multiple objects from interfering with each other.

The Singleton pattern consists of three components:

A private constructor - this prevents the class from being instantiated from outside the class.
A static property or method to retrieve the single instance of the class - this ensures that there is only one instance of the class and provides a global point of access to it.
A static field to hold the single instance of the class - this stores the single instance of the class that is created by the static property or method.
Let's consider a simple example to illustrate the use of the Singleton pattern. Suppose we have a class called Logger that logs messages to a file. We want to ensure that there is only one Logger object in our application, so we will use the Singleton pattern to achieve this.

Here is an implementation of the Logger class using the Singleton pattern in C#:



public class Logger {
    private static Logger _instance;
    private static readonly object _lock = new object();
    private readonly string _logFileName;
    private Logger() {
        _logFileName = "log.txt";
    }
    public static Logger GetInstance() {
        lock(_lock) {
            if (_instance == null) {
                _instance = new Logger();
            }
        }
        return _instance;
    }
    public void Log(string message) {
        using(StreamWriter writer = new StreamWriter(_logFileName, true)) {
            writer.WriteLine($ "{DateTime.Now}: {message}");
        }
    }
}
In this implementation, the Logger class has a private constructor, which prevents it from being instantiated from outside the class. The only way to get an instance of the Logger class is by calling the static GetInstance() method.

The GetInstance() method checks if an instance of the Logger class has already been created. If it has, it returns that instance. If it hasn't, it creates a new instance of the Logger class and returns it. The method uses a lock to ensure that only one thread can access the method at a time.

The Log() method can be used to log messages to a file.

Now, let's consider an example of how we might use the Logger class in our application:


public class Program {
    static void Main(string[] args) {
        Logger logger1 = Logger.GetInstance();
        Logger logger2 = Logger.GetInstance();
        logger1.Log("Hello, world!");
        logger2.Log("Goodbye, world!");
        if (logger1 == logger2) {
            Console.WriteLine("Only one logger instance exists.");
        }
    }
}
In this example, we create two instances of the Logger class by calling the GetInstance() method twice. Since the GetInstance() method ensures that only one instance of the Logger class exists, logger1 and logger2 refer to the same object. We then use the Log() method to log messages to a file.

By using the Singleton pattern, we ensure that only one Logger object is created and shared across the entire application, leading to more efficient and consistent logging behavior.

I hope this explanation and example help clarify the Singleton pattern for you!
 
 
Factory Pattern - Provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.

Observer Pattern - Defines a one-to-many dependency between objects, so that when one              object changes state, all of its dependents are notified and updated automatically.

Decorator Pattern - Allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class.

Adapter Pattern - Converts the interface of a class into another interface that clients expect. Adapters allow classes to work together that wouldn't otherwise because of incompatible interfaces.
 
Strategy Pattern - Defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
 
Command Pattern - Encapsulates a request as an object, thereby allowing for the parameterisation of clients with different requests, and for the queuing or logging of requests.
 
Iterator Pattern - Provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
 
Template Method Pattern - Defines the skeleton of an algorithm in a method, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.

 Facade Pattern - Provides a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.

Post a Comment

0 Comments