Design Patterns Basics Tutorial

3 hours ago 1
ARTICLE AD BOX

’ve read so many times questions about design patterns in C#, and most explanations are either too abstract or only show incomplete code snippets.

So here I’m providing a small, structured explanation of the most common design patterns with simple C# code examples. The goal is to make it easier to understand what each pattern does, why it exists, and how it looks in practice.

Is there a clear beginner-friendly overview of the most important design patterns with working examples?

Below is a compact tutorial of the most commonly used design patterns in C#. Each section follows the same structure:

What problem it solves

Basic idea

Full working C# example

Problem

In many applications, you need to create objects that belong together as a family (for example, Windows UI components or Mac UI components). The problem is that you don’t want your code to depend on specific implementations like WindowsButton or MacButton.

If you directly instantiate concrete classes everywhere, your code becomes tightly coupled and hard to change.

Idea

The Abstract Factory pattern solves this by introducing a factory interface that creates related objects without exposing their concrete classes.

Instead of calling new WindowsButton() directly, you ask a factory to give you a button.

This allows you to switch entire product families without changing client code.

Code

using System; interface IButton { void Render(); } class WindowsButton : IButton { public void Render() => Console.WriteLine("Windows Button"); } class MacButton : IButton { public void Render() => Console.WriteLine("Mac Button"); } interface IGUIFactory { IButton CreateButton(); } class WindowsFactory : IGUIFactory { public IButton CreateButton() => new WindowsButton(); } class MacFactory : IGUIFactory { public IButton CreateButton() => new MacButton(); } class Application { private IButton button; public Application(IGUIFactory factory) { button = factory.CreateButton(); } public void Render() { button.Render(); } }

Problem

Real software systems often contain many complex subsystems.

For example, starting a computer involves:

CPU initialization

Memory loading

Disk operations

BIOS steps

If a client has to call all these directly, the code becomes:

hard to read

hard to maintain

easy to misuse

So the problem is:

A complex subsystem should not expose all its complexity to the user. Without structure, the client must know and coordinate all of them, which leads to very complex code.

Idea

The Facade pattern provides a simple unified interface to a complex system.

Instead of interacting with multiple classes, the client interacts with only one “facade” class.

Code

using System; class CPU { public void Start() => Console.WriteLine("CPU started"); } class Memory { public void Load() => Console.WriteLine("Memory loaded"); } class HardDrive { public void Read() => Console.WriteLine("Disk read"); } class ComputerFacade { private CPU cpu = new CPU(); private Memory memory = new Memory(); private HardDrive disk = new HardDrive(); public void StartComputer() { cpu.Start(); memory.Load(); disk.Read(); } }

Problem

Some objects are complex and require multiple steps to construct. If you use constructors with many parameters, code becomes hard to read and error-prone.

Idea

The Builder pattern separates object construction from representation.

Instead of building everything at once, you construct an object step by step.

Code

using System; class Product { public string Parts = ""; public void Show() => Console.WriteLine(Parts); } interface IBuilder { void BuildPartA(); void BuildPartB(); Product GetResult(); } class ConcreteBuilder : IBuilder { private Product product = new Product(); public void BuildPartA() => product.Parts += "PartA "; public void BuildPartB() => product.Parts += "PartB "; public Product GetResult() => product; } class Director { public void Construct(IBuilder builder) { builder.BuildPartA(); builder.BuildPartB(); } }

Problem

Sometimes you want to extend behavior of objects without modifying their original code or creating many subclasses.

Idea

The Decorator pattern wraps an object and adds new behavior dynamically.

Instead of changing the original class, you “wrap” it with another class.

Code

using System; interface IComponent { void Operation(); } class ConcreteComponent : IComponent { public void Operation() => Console.WriteLine("Base Operation"); } class Decorator : IComponent { protected IComponent component; public Decorator(IComponent component) { this.component = component; } public virtual void Operation() { component.Operation(); } } class ConcreteDecorator : Decorator { public ConcreteDecorator(IComponent component) : base(component) {} public override void Operation() { base.Operation(); Console.WriteLine("Extended behavior"); } }

Problem

Collections should be traversable, but:

internal structure should stay hidden

client should not depend on implementation

Idea

The Iterator pattern provides a standard way to go through elements one by one.

Code

using System; using System.Collections; using System.Collections.Generic; class Numbers : IEnumerable<int> { private int[] values = { 1, 2, 3, 4 }; public IEnumerator<int> GetEnumerator() { foreach (var v in values) yield return v; } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }

Problem

Creating objects from scratch can be expensive or unnecessary when a similar object already exists.

Idea

Instead of creating new objects, you clone existing ones.

Code

using System; class Prototype { public int Value; public Prototype Clone() { return (Prototype)this.MemberwiseClone(); } }

Problem

You want to be able to switch algorithms at runtime without changing the code that uses them.

Idea

Encapsulate algorithms in separate classes and make them interchangeable.

Code

using System; interface IStrategy { void Execute(); } class StrategyA : IStrategy { public void Execute() => Console.WriteLine("Strategy A"); } class StrategyB : IStrategy { public void Execute() => Console.WriteLine("Strategy B"); } class Context { private IStrategy strategy; public Context(IStrategy strategy) { this.strategy = strategy; } public void SetStrategy(IStrategy strategy) { this.strategy = strategy; } public void Execute() { strategy.Execute(); } }

Problem

A request should be processed by multiple handlers, but you don’t know which one in advance.

Idea

Pass the request along a chain until someone handles it.

Code

using System; abstract class Handler { protected Handler next; public void SetNext(Handler next) { this.next = next; } public virtual void Handle(string request) { next?.Handle(request); } } class HandlerA : Handler { public override void Handle(string request) { if (request == "A") Console.WriteLine("Handled by A"); else base.Handle(request); } } class HandlerB : Handler { public override void Handle(string request) { if (request == "B") Console.WriteLine("Handled by B"); else base.Handle(request); } }

Problem

You need exactly one instance of a class (e.g. configuration, logging, database connection manager).

Idea

Restrict object creation so only one instance exists globally.

Code

using System; class Singleton { private static Singleton instance; private static readonly object lockObj = new object(); private Singleton() {} public static Singleton Instance { get { lock (lockObj) { if (instance == null) instance = new Singleton(); return instance; } } } public void DoSomething() { Console.WriteLine("Singleton working"); } }
Read Entire Article