Design patterns: Classification and examples in C#

Design patterns: Classification and examples in C#

As a software developer with over a decade of experience working in large companies, I've seen how design patterns have transformed the way we approach software construction. These patterns provide reusable, proven solutions to common software design problems, enhancing code efficiency and quality. In this article, we will explore the classification of design patterns, provide a brief description of each, and offer practical examples in C#.

Classification of Design Patterns

Design patterns are generally classified into three main categories:

1. Creational Patterns: These patterns focus on how objects are created, helping to make the system independent of how its objects are created, composed, and represented.

2. Structural Patterns: These patterns deal with the composition of classes or objects, forming larger and more complex structures.

3. Behavioral Patterns: These patterns focus on communication between objects and delegation of responsibilities.


Creational Patterns

1. Singleton: Ensures a class has only one instance and provides a global point of access to it.

Example in C#:

public class Singleton
{
    private static Singleton _instance;

    private Singleton() { }

    public static Singleton Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new Singleton();
            }
            return _instance;
        }
    }
}
        

2. Factory Method: Defines an interface for creating an object but allows subclasses to alter the type of objects that will be created.

Example in C#:

public abstract class Creator
{
    public abstract IProduct FactoryMethod();
}

public class ConcreteCreator : Creator
{
    public override IProduct FactoryMethod()
    {
        return new ConcreteProduct();
    }
}

public interface IProduct { }

public class ConcreteProduct : IProduct { }
        

3. Abstract Factory: Provides an interface for creating families of related or dependent objects without specifying their concrete classes.

Example in C#:

public interface IAbstractFactory
{
    IProductA CreateProductA();
    IProductB CreateProductB();
}

public class ConcreteFactory1 : IAbstractFactory
{
    public IProductA CreateProductA() => new ProductA1();
    public IProductB CreateProductB() => new ProductB1();
}

public interface IProductA { }
public interface IProductB { }

public class ProductA1 : IProductA { }
public class ProductB1 : IProductB { }
        

Structural Patterns

1. Adapter: Allows classes with incompatible interfaces to work together by converting the interface of a class into another interface the client expects.

Example in C#:

public interface ITarget
{
    void Request();
}

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

public class Adapter : ITarget
{
    private readonly Adaptee _adaptee;

    public Adapter(Adaptee adaptee)
    {
        _adaptee = adaptee;
    }

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

2. Decorator: Adds responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

Example in C#:

public abstract class Component
{
    public abstract void Operation();
}

public class ConcreteComponent : Component
{
    public override void Operation()
    {
        Console.WriteLine("ConcreteComponent.Operation()");
    }
}

public abstract class Decorator : Component
{
    protected Component _component;

    public void SetComponent(Component component)
    {
        _component = component;
    }

    public override void Operation()
    {
        _component?.Operation();
    }
}

public class ConcreteDecorator : Decorator
{
    public override void Operation()
    {
        base.Operation();
        AddedBehavior();
    }

    void AddedBehavior()
    {
        Console.WriteLine("ConcreteDecorator.AddedBehavior()");
    }
}
        

3. Composite: Composes objects into tree structures to represent part-whole hierarchies. It allows clients to treat individual objects and compositions of objects uniformly.

Example in C#:

public abstract class Component
{
    public abstract void Operation();
}

public class Leaf : Component
{
    public override void Operation()
    {
        Console.WriteLine("Leaf.Operation()");
    }
}

public class Composite : Component
{
    private readonly List<Component> _children = new List<Component>();

    public void Add(Component component)
    {
        _children.Add(component);
    }

    public void Remove(Component component)
    {
        _children.Remove(component);
    }

    public override void Operation()
    {
        Console.WriteLine("Composite.Operation()");
        foreach (Component component in _children)
        {
            component.Operation();
        }
    }
}
        

Behavioral Patterns

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

Example in C#:

public interface IObserver
{
    void Update();
}

public interface ISubject
{
    void Attach(IObserver observer);
    void Detach(IObserver observer);
    void Notify();
}

public class ConcreteSubject : ISubject
{
    private readonly List<IObserver> _observers = new List<IObserver>();

    public void Attach(IObserver observer)
    {
        _observers.Add(observer);
    }

    public void Detach(IObserver observer)
    {
        _observers.Remove(observer);
    }

    public void Notify()
    {
        foreach (var observer in _observers)
        {
            observer.Update();
        }
    }
}

public class ConcreteObserver : IObserver
{
    public void Update()
    {
        Console.WriteLine("Observer updated.");
    }
}
        

2. Strategy: Defines a family of algorithms, encapsulates each one, and makes them interchangeable. Allows the algorithm to vary independently from clients that use it.

Example in C#:

public interface IStrategy
{
    void AlgorithmInterface();
}

public class ConcreteStrategyA : IStrategy
{
    public void AlgorithmInterface()
    {
        Console.WriteLine("ConcreteStrategyA.AlgorithmInterface()");
    }
}

public class ConcreteStrategyB : IStrategy
{
    public void AlgorithmInterface()
    {
        Console.WriteLine("ConcreteStrategyB.AlgorithmInterface()");
    }
}

public class Context
{
    private IStrategy _strategy;

    public Context(IStrategy strategy)
    {
        _strategy = strategy;
    }

    public void SetStrategy(IStrategy strategy)
    {
        _strategy = strategy;
    }

    public void ContextInterface()
    {
        _strategy.AlgorithmInterface();
    }
}
        

3. Command: Encapsulates a request as an object, thereby allowing for parameterization of clients with different requests, queuing of requests, and logging of the requests. It also supports undoable operations.

Example in C#:

public interface ICommand
{
    void Execute();
}

public class ConcreteCommand : ICommand
{
    private readonly Receiver _receiver;

    public ConcreteCommand(Receiver receiver)
    {
        _receiver = receiver;
    }

    public void Execute()
    {
        _receiver.Action();
    }
}

public class Receiver
{
    public void Action()
    {
        Console.WriteLine("Receiver.Action()");
    }
}

public class Invoker
{
    private ICommand _command;

    public void SetCommand(ICommand command)
    {
        _command = command;
    }

    public void ExecuteCommand()
    {
        _command.Execute();
    }
}
        

Conclusion

Design patterns are fundamental tools in software development, providing effective solutions to common problems and facilitating the creation of robust and maintainable software. I hope this article has given you a clear and concise view of the different types of design patterns and their practical applications in C#. Implementing them properly can make a significant difference in the quality and efficiency of our software projects.

Thank you for reading!.

要查看或添加评论,请登录

Alejandro Gladchtein的更多文章

  • How to Get Started with Python

    How to Get Started with Python

    Python is one of the most popular and versatile programming languages today. With over 10 years of experience as a…

  • Javascript and frameworks

    Javascript and frameworks

    Introduction In the ever-evolving landscape of web development, JavaScript remains a cornerstone language that empowers…

  • Microservices: A Software Architect's Perspective

    Microservices: A Software Architect's Perspective

    Microservices: A Software Architect's Perspective As a software architect with 15 years of experience, I've had the…

  • AI and the Future of Developers

    AI and the Future of Developers

    Artificial Intelligence (AI) has rapidly emerged as a transformative force across various industries, reshaping how we…

  • The importance of Entity Framework: Evolution and code examples

    The importance of Entity Framework: Evolution and code examples

    With 18 years of experience in software development using Microsoft .NET, I have seen the evolution of many…

  • Fundamentals of JavaScript: How to start your journey in Web Development

    Fundamentals of JavaScript: How to start your journey in Web Development

    With 10 years of experience in software development using JavaScript, I've witnessed how this language has evolved and…

  • Cómo comenzar con Microsoft .NET

    Cómo comenzar con Microsoft .NET

    Cómo Comenzar y Convertirse en un Desarrollador .NET Senior: En el mundo del desarrollo de software, Microsoft .

    2 条评论

社区洞察

其他会员也浏览了