Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions docs/csharp/fundamentals/object-oriented/design-patterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
---
title: "Design patterns in C#"
description: Provides an overview of common design patterns and their use in C# applications.
ms.date: 01/30/2026
helpviewer_keywords:
- "design patterns [C#]"
- "creational patterns"
- "structural patterns"
- "behavioral patterns"
- "software design"
- "object-oriented design"
- "C# architecture"
---

# Overview of design patterns in C#

Design patterns are proven solutions to recurring problems in software design. Rather than providing finished code, a design pattern offers a general template that can be adapted to solve a particular design problem in a given context.

In C#, design patterns are commonly used to improve code readability, maintainability, testability, and extensibility. They are especially valuable in large applications where change is expected over time.

This article provides a high-level overview of the most commonly used design patterns and their purpose.

## What is a design pattern

A *design pattern* describes:

- A common problem in software design
- The context in which the problem occurs
- A reusable solution structure
- The consequences and trade-offs of using that solution

Design patterns do not depend on a specific programming language, but their implementation can vary depending on language features. C# provides strong support for design patterns through interfaces, inheritance, delegates, generics, and dependency injection.

## Categories of design patterns

Design patterns are traditionally grouped into three main categories.

### Creational patterns

Creational patterns focus on *object creation mechanisms*. They help control how and when objects are created, reducing tight coupling between code components.

Common creational patterns include:

- Factory Method
- Abstract Factory
- Builder
- Singleton
- Prototype

These patterns are often used when object creation involves complex logic or when the exact type of object should be determined at runtime.

## Structural patterns

Structural patterns deal with *object composition*. They help define how classes and objects are combined to form larger structures while keeping the system flexible.

Common structural patterns include:

- Adapter
- Bridge
- Composite
- Decorator
- Facade
- Flyweight
- Proxy

These patterns are useful when integrating legacy code, adding behavior dynamically, or simplifying complex subsystems.

## Behavioral patterns

Behavioral patterns focus on *communication and responsibility* between objects.

Common behavioral patterns include:

- Strategy
- Observer
- Command
- Iterator
- Mediator
- State
- Template Method
- Chain of Responsibility

These patterns help reduce coupling by defining clear interaction rules between components.

## Benefits of using design patterns

Using design patterns can provide several advantages:

- Improved code readability and structure
- Reduced coupling between components
- Better separation of concerns
- Easier testing and mocking
- Improved long-term maintainability

Design patterns also make code easier to understand for other developers, as many patterns are widely recognized across the software industry.

## Design patterns and SOLID principles

Design patterns often work hand-in-hand with the SOLID principles:

- **Single Responsibility Principle** encourages small, focused classes
- **Open/Closed Principle** promotes extension without modification
- **Liskov Substitution Principle** ensures safe polymorphism
- **Interface Segregation Principle** avoids large, monolithic interfaces
- **Dependency Inversion Principle** encourages abstraction over concretion

Many patterns, such as Strategy, Factory Method, and Dependency Injection, naturally support these principles.

## When not to use design patterns

Design patterns should not be applied automatically. Overusing patterns can lead to unnecessary complexity.

Avoid design patterns when:

- The problem is simple
- The solution is unlikely to change
- The added abstraction provides no clear benefit

Patterns should solve real problems, not be used for their own sake.

## Design patterns in modern C#

Modern C# features simplify many pattern implementations:

- Records reduce boilerplate for immutable objects
- Dependency injection is built into ASP.NET Core
- Delegates and lambdas simplify behavioral patterns
- Pattern matching improves readability
- Minimal APIs reduce structural overhead

These features often replace or simplify classic implementations found in older examples.

## Next steps

The following articles in this series describe individual design patterns with practical C# examples:

- Creational patterns
- Structural patterns
- Behavioral patterns

Each article explains when to use the pattern, how it works, and its advantages and disadvantages.

## C# Language Specification

[!INCLUDE[CSharplangspec](~/includes/csharplangspec-md.md)]

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;
namespace Greeting.Contracts
{
public interface IGreet
{
string GetGreeting(int hour);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<None Remove="Contracts\" />
<None Remove="Models\" />
</ItemGroup>
<ItemGroup>
<Folder Include="Contracts\" />
<Folder Include="Models\" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using Greeting.Contracts;

namespace Greeting.Models
{
public class Greeter : IGreet
{
public string GetGreeting(int hour)
{
return hour switch
{
>= 5 and <= 11 => "Good morning!",
>= 12 and <= 17 => "Good afternoon!",
>= 18 and <= 22 => "Good evening!",
_ => "Good night!"

};
}
}
}

20 changes: 20 additions & 0 deletions docs/csharp/tutorials/snippets/Greeting/Greeting/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Greeting.Contracts;
using Greeting.Models;

namespace Greeting;
class Program
{
static void Main(string[] args)
{
Console.Write("Enter time: ");
int hour = int.Parse(Console.ReadLine());

IGreet greet = new Greeter();

string greetingResult = greet.GetGreeting(hour);

Console.WriteLine(greetingResult);
Console.ReadKey();
}
}

131 changes: 131 additions & 0 deletions docs/csharp/tutorials/snippets/Greeting/Greeting/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Interface Greeting Example

This project demonstrates a simple and clean use of **interfaces in C#** by implementing a greeting system based on the hour of the day.

The goal of this example is educational — to show how interfaces help separate **abstraction from implementation** while keeping the code easy to read, test, and extend.

---

## 📌 Overview

The application asks the user to enter an hour (0–23) and prints an appropriate greeting in English:

* **Good morning**
* **Good afternoon**
* **Good evening**
* **Good night**

The greeting logic is defined through an interface and implemented using a concrete class.

---

## 🧠 Key Concepts Demonstrated

* Interface-based design
* Separation of concerns
* Polymorphism
* Switch expressions with pattern matching
* Clean and readable C# code

---

## 🔌 Interface

The interface defines a contract for greeting behavior:

```csharp
public interface IGreet
{
string GetGreeting(int hour);
}
```

This allows different implementations to be created without changing the code that uses them.

---

## ⚙️ Implementation

The greeting logic is implemented using a modern C# switch expression:

```csharp
public class Greeter : IGreet
{
public string GetGreeting(int hour)
{
return hour switch
{
>= 5 and <= 11 => "Good morning!",
>= 12 and <= 17 => "Good afternoon!",
>= 18 and <= 22 => "Good evening!",
_ => "Good night!"
};
}
}
```

---

## ▶️ How It Works

1. The user enters an hour between **0 and 23**
2. The program calls the greeting service through the interface
3. The appropriate greeting is returned and displayed

```csharp
IGreetingService greetingService = new GreetingService();
Console.WriteLine(greetingService.GetGreeting(hour));
```

The program depends on the **interface**, not the concrete implementation.

---

## ✅ Why Use an Interface Here?

Using an interface provides several benefits:

* The program is loosely coupled
* The greeting logic can be replaced or extended
* The code is easier to test
* The design follows the **Dependency Inversion Principle**

For example, new implementations could be added later:

* `GermanGreetingService`
* `FrenchGreetingService`
* `TimeBasedGreetingService`

Without changing the main program logic.

---

## 🧪 Testing Friendly Design

Because the hour is passed as a parameter, the logic can be easily tested:

```csharp
GetGreeting(9); // Good morning
GetGreeting(15); // Good afternoon
GetGreeting(21); // Good evening
```

No system time dependencies are required.

---

## 🎯 Purpose

This project is intended as a **learning example** for developers who are beginning with:

* Interfaces in C#
* Clean architecture principles
* Writing maintainable and readable code

It is suitable for documentation, tutorials, and beginner-friendly demonstrations.

---

## 📄 License

This project is provided for educational purposes and can be freely used in learning materials or documentation examples.