Top 6 ASP.NET Design Patterns: A Practical Guide

Said Belakbir
4 min readSep 24, 2023

--

Introduction

Design patterns are essential tools for developers to solve recurring problems in software development. They provide proven solutions, enhancing code maintainability, scalability, and overall application architecture. In the realm of ASP.NET, understanding and applying design patterns can significantly improve the quality and performance of web applications.

In this article, we’ll explore common design patterns used in ASP.NET and demonstrate their practical implementation through examples.

Table of Contents

  1. Singleton Pattern
  2. Factory Method Pattern
  3. Abstract Factory Pattern
  4. Builder Pattern
  5. Strategy Pattern
  6. Repository Pattern

1. Singleton Pattern

Explanation

The Singleton pattern ensures that a class has only one instance and provides a global point of access to that instance throughout the application. This is useful for scenarios where a single instance is required to control actions or centralize configuration settings.

|public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
    private Singleton() { }    public static Singleton Instance => instance;    public void DisplayMessage()
{
Console.WriteLine("Singleton pattern example");
}
}

private Singleton() { }

public static Singleton Instance => instance;

public void DisplayMessage()
{
Console.WriteLine(“Singleton pattern example”);
}
}

2. Factory Method Pattern

Explanation

The Factory Method pattern defines an interface for creating an object, allowing subclasses to alter the type of objects that will be created. This provides a way to create instances of a class with a common interface without specifying their concrete types.

Implementation Example

|public interface IProduct
{
void DisplayInfo();
}
public class ConcreteProductA : IProduct
{
public void DisplayInfo()
{
Console.WriteLine("Product A");
}
}
public class ConcreteProductB : IProduct
{
public void DisplayInfo()
{
Console.WriteLine("Product B");
}
}
public abstract class Creator
{
public abstract IProduct FactoryMethod();
}

3. Abstract Factory Pattern

Explanation

The Abstract Factory pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes. It ensures that created objects are compatible and follow a particular theme.

Implementation Example

|// Abstract factory interface
public interface IAbstractFactory
{
IProductA CreateProductA();
IProductB CreateProductB();
}
// Concrete factory implementing the abstract factory
public class ConcreteFactory1 : IAbstractFactory
{
public IProductA CreateProductA()
{
return new ProductA1();
}
public IProductB CreateProductB()
{
return new ProductB1();
}
}
// Another concrete factory implementing the abstract factory
public class ConcreteFactory2 : IAbstractFactory
{
public IProductA CreateProductA()
{
return new ProductA2();
}
public IProductB CreateProductB()
{
return new ProductB2();
}
}

4. Builder Pattern

Explanation

The Builder pattern separates the construction of a complex object from its representation, allowing the same construction process to create different types of objects. It is useful when an object needs various configurations to be created.

Implementation Example

|public class Product
{
public string Part1 { get; set; }
public string Part2 { get; set; }
    public void Display()
{
Console.WriteLine($"Part 1: {Part1}, Part 2: {Part2}");
}
}
public interface IBuilder
{
void BuildPart1();
void BuildPart2();
Product GetProduct();
}
public class ConcreteBuilder : IBuilder
{
private Product product = new Product();
public void BuildPart1()
{
product.Part1 = "Part 1 built";
}
public void BuildPart2()
{
product.Part2 = "Part 2 built";
}
public Product GetProduct()
{
return product;
}
}

5. Strategy Pattern

Explanation

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. It allows the algorithm to vary independently from the context that uses the algorithm.

Implementation Example

|public interface IStrategy
{
void Execute();
}
public class ConcreteStrategy1 : IStrategy
{
public void Execute()
{
Console.WriteLine("Executing strategy 1");
}
}
public class ConcreteStrategy2 : IStrategy
{
public void Execute()
{
Console.WriteLine("Executing strategy 2");
}
}
public class Context
{
private IStrategy strategy;
public Context(IStrategy strategy)
{
this.strategy = strategy;
}
public void SetStrategy(IStrategy strategy)
{
this.strategy = strategy;
}
public void ExecuteStrategy()
{
strategy.Execute();
}
}

Repository Pattern

Explanation

The Repository pattern abstracts the data source and provides a simple, consistent interface to access and manipulate data within the data source. It typically includes methods for common data operations such as Create, Read, Update, and Delete (CRUD).

Implementation Example

|public interface IRepository<T>
{
T GetById(int id);
IEnumerable<T> GetAll();
void Add(T entity);
void Update(T entity);
void Delete(T entity);
}
public class Repository<T> : IRepository<T>
{
private readonly List<T> data = new List<T>();
public T GetById(int id)
{
// Logic to retrieve an entity by its ID
throw new NotImplementedException();
}
public IEnumerable<T> GetAll()
{
// Logic to retrieve all entities
throw new NotImplementedException();
}
public void Add(T entity)
{
// Logic to add a new entity
data.Add(entity);
}
public void Update(T entity)
{
// Logic to update an existing entity
throw new NotImplementedException();
}
public void Delete(T entity)
{
// Logic to delete an entity
data.Remove(entity);
}
}
// Example usage
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Program
{
public static void Main()
{
IRepository<Product> productRepository = new Repository<Product>();
// Adding a product
Product product = new Product { Id = 1, Name = "Product A" };
productRepository.Add(product);
// Getting a product by ID
Product retrievedProduct = productRepository.GetById(1);
// Displaying the retrieved product
Console.WriteLine($"Retrieved Product: {retrievedProduct.Name}");
}
}

Conclusion

In this article, we’ve explored several essential design patterns in ASP.NET. Understanding and utilizing these patterns can greatly enhance your ability to design robust, maintainable, and scalable applications. By incorporating these patterns into your ASP.NET projects, you’ll be better equipped to handle complex software development challenges effectively.

--

--