Showing posts with label Design Pattern. Show all posts
Showing posts with label Design Pattern. Show all posts

Friday 3 March 2023

Observer Pattern with example

The Observer Pattern is used to maintain a list of dependents, called observers, and notify them automatically of any changes to the observed object. This is useful when you want to implement a publish-subscribe mechanism where multiple objects are interested in changes to a single object.

Here's an example implementation of the Observer pattern in C#:

public interface ISubject
{
    void Register(IObserver observer);
    void Unregister(IObserver observer);
    void NotifyObservers();
}

public interface IObserver
{
    void Update(ISubject subject);
}

public class Subject : ISubject
{
    private List<IObserver> observers = new List<IObserver>();
    private int state;

    public int State
    {
        get => state;
        set
        {
            state = value;
            NotifyObservers();
        }
    }

    public void Register(IObserver observer)
    {
        observers.Add(observer);
    }

    public void Unregister(IObserver observer)
    {
        observers.Remove(observer);
    }

    public void NotifyObservers()
    {
        foreach (var observer in observers)
        {
            observer.Update(this);
        }
    }
}

public class Observer : IObserver
{
    public void Update(ISubject subject)
    {
        if (subject is Subject s)
        {
            Console.WriteLine($"Observer updated: {s.State}");
        }
    }
}

In this implementation, we have an ISubject interface that defines methods for registering, unregistering, and notifying observers, and an IObserver interface that defines the Update method.

We also have a Subject class that implements the ISubject interface and maintains a list of observers, and an Observer class that implements the IObserver interface and updates its state when notified by the Subject instance.

To use the observer, you can simply create instances of the Subject and Observer classes, and register the observer with the subject, like this:

Subject subject = new Subject();
Observer observer = new Observer();

subject.Register(observer);
subject.State = 1; // Output: Observer updated: 1

subject.Unregister(observer);
subject.State = 2; // No output

In this example, we create an instance of the Subject and Observer classes, and register the observer with the subject by calling the Register method on the Subject instance with the Observer instance as an argument.

We then update the state of the Subject instance by setting its State property, which in turn calls the NotifyObservers method to notify all registered observers.

The Observer instance is notified and updates its state by calling the Update method, which outputs the new state.

Finally, we unregister the observer by calling the Unregister method on the Subject instance with the Observer instance as an argument, and update the state of the Subject instance again, which doesn't output anything as the observer is no longer registered.

This example demonstrates how the Observer pattern can be used to maintain a list of observers and notify them automatically of any changes to the observed object, allowing multiple objects to be notified of changes to a single object.

Thursday 2 March 2023

Adapter Pattern with example

The Adapter Pattern is used to convert the interface of one class into another interface that is expected by the client. This is useful when you have two incompatible interfaces that need to work together.

Here's an example implementation of the Adapter pattern in C#:

public interface ITarget
{
    void Request();
}

public class Adaptee
{
    public void SpecificRequest()
    {
        Console.WriteLine("Specific request.");
    }
}

public class Adapter : ITarget
{
    private readonly Adaptee adaptee;

    public Adapter(Adaptee adaptee)
    {
        this.adaptee = adaptee;
    }

    public void Request()
    {
        adaptee.SpecificRequest();
    }
}

In this implementation, we have an interface ITarget that defines the Request method, an Adaptee class that has a SpecificRequest method, and an Adapter class that implements the ITarget interface and adapts the Adaptee class to work with the ITarget interface.

The Adapter class takes an instance of the Adaptee class in its constructor, and implements the Request method by calling the SpecificRequest method of the Adaptee class.

To use the adapter, you can simply create an instance of the Adapter class with an instance of the Adaptee class, like this:


Adaptee adaptee = new Adaptee();
ITarget target = new Adapter(adaptee);

target.Request(); // Output: Specific request.


In this example, we create an instance of the Adaptee class, and use it to create an instance of the Adapter class by passing it to the Adapter constructor. We then call the Request method on the ITarget instance, which in turn calls the SpecificRequest method of the Adaptee instance.

This example demonstrates how the Adapter pattern can be used to adapt an incompatible interface to a compatible one, allowing the two interfaces to work together seamlessly.

Factory Pattern with example

The Factory pattern is used to create objects without specifying the exact class of object that will be created. This is useful when you want to create objects in a flexible and extensible way.

Here's an example implementation of the Factory pattern in C#:


public interface IAnimal
{
    void Speak();
}

public class Dog : IAnimal
{
    public void Speak()
    {
        Console.WriteLine("Woof!");
    }
}

public class Cat : IAnimal
{
    public void Speak()
    {
        Console.WriteLine("Meow!");
    }
}

public class AnimalFactory
{
    public IAnimal CreateAnimal(string type)
    {
        switch (type)
        {
            case "dog":
                return new Dog();
            case "cat":
                return new Cat();
            default:
                throw new ArgumentException($"Invalid animal type: {type}");
        }
    }
}

In this implementation, we have an interface IAnimal that defines the Speak method, and two classes Dog and Cat that implement the IAnimal interface.

We also have a AnimalFactory class that has a CreateAnimal method which takes a string type as input and returns an instance of an IAnimal implementation based on the type.

To use the factory, you can simply call the CreateAnimal method on the AnimalFactory class, like this:

AnimalFactory factory = new AnimalFactory();
IAnimal dog = factory.CreateAnimal("dog");
IAnimal cat = factory.CreateAnimal("cat");

dog.Speak(); // Output: Woof!
cat.Speak(); // Output: Meow!


In this example, we create an instance of the AnimalFactory class, and use it to create instances of the Dog and Cat classes by calling the CreateAnimal method with the appropriate type. The Speak method of each animal is then called to output the appropriate sound.

This example demonstrates how the Factory pattern can be used to create objects in a flexible and extensible way, without knowing the exact implementation details of each object.

Singleton pattern with example

The Singleton pattern is used to ensure that only one instance of a class is created throughout the lifetime of the application. This is useful when you want to control access to a shared resource, such as a database connection, or when you want to limit the number of instances of a class to one.

Here's an example implementation of the Singleton pattern in C#:

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 class Singleton has a private constructor to prevent external instantiation, and a private static field instance that holds the single instance of the class. The Instance property is a public static property that returns the single instance of the class. The property first checks if the instance field is null, and if so, it creates a new instance of the Singleton class.

To use the Singleton, you can simply call the Instance property on the class, like this:


Singleton s1 = Singleton.Instance;
Singleton s2 = Singleton.Instance;

if (s1 == s2)
{
    Console.WriteLine("s1 and s2 are the same instance.");
}


In this example, s1 and s2 are both instances of the Singleton class, but because the class is implemented as a Singleton, they both refer to the same instance. The output of the above code will be "s1 and s2 are the same instance.".

Note that in multi-threaded environments, additional measures must be taken to ensure thread-safety when implementing the Singleton pattern.

What is Design Pattern? Types of Design Patterns.

Design patterns are general reusable solutions to common problems that arise during software development. In .NET, there are several design patterns that are commonly used. Here are some explanations of design patterns in .NET without specific examples:

1. Singleton Pattern:

The Singleton pattern is used when you want to ensure that only one instance of a class is created. This pattern is useful when you want to control the creation of objects to ensure that there is only one instance of a particular object in the system. This pattern is commonly used for database connections, logging, and caching.

2. Factory Pattern:

The Factory pattern is used when you want to create objects without specifying the exact class of object that will be created. This pattern is useful when you want to create objects in a flexible and extensible way. The Factory pattern encapsulates the object creation process and allows for the creation of objects without knowing the exact implementation details.

3. Adapter Pattern:

The Adapter pattern is used when you want to adapt an existing class to meet the requirements of a new interface. This pattern is useful when you have an existing class that does not match the requirements of a new interface. The Adapter pattern is a bridge between the existing class and the new interface. The Adapter class takes the existing class as input and adapts it to the new interface.

4. Observer Pattern:

The Observer pattern is used when you want to notify multiple objects of changes in a single object. This pattern is useful when you have an object that is changing and you want to notify other objects of these changes. The Observer pattern defines a one-to-many relationship between objects so that when one object changes state, all its dependent objects are notified and updated automatically.

5. Decorator Pattern:

The Decorator pattern is used when you want to add functionality to an existing object without modifying its structure. This pattern is useful when you have an existing object that you want to enhance with additional features or behaviors. The Decorator pattern uses a wrapper class to add new functionality to an object dynamically.

6. Dependency Injection Pattern:

The Dependency Injection pattern is used when you want to inject dependencies into a class rather than creating them inside the class. This pattern is useful for decoupling components and promoting testability. The Dependency Injection pattern allows for flexibility in the construction of objects by separating the creation of objects from their use.



There are many other design patterns used in .NET and software development in general. It is important to understand these patterns and when to use them in order to write clean, maintainable, and extensible code.