Monday, July 13, 2026

Composite Design Pattern

 Mastering Design Patterns in C# and ASP.NET Core

Part 3.3 – Composite Design Pattern

Series: Design Patterns in C# and ASP.NET Core
Pattern Category: Structural Design Pattern
Difficulty: ⭐⭐⭐⭐☆ (Intermediate)
Prerequisites: OOP Concepts, Interfaces, Recursion, Collections, Dependency Injection


Table of Contents

  1. Introduction

  2. What is the Composite Design Pattern?

  3. Why Do We Need the Composite Pattern?

  4. The Problem with Hierarchical Structures

  5. Real-World Analogy

  6. Composite Pattern Structure

  7. UML Class Diagram

  8. Components of the Composite Pattern

  9. Complete C# Console Application

  10. ASP.NET Core Implementation

  11. Real-World Examples

  12. Advantages

  13. Disadvantages

  14. Best Practices

  15. Common Mistakes

  16. Composite vs Decorator vs Flyweight

  17. Interview Questions

  18. Summary


Introduction

Many real-world applications deal with hierarchical structures, where individual objects and groups of objects need to be treated in the same way.

Examples include:

  • File systems (Folders and Files)

  • Organization charts (Managers and Employees)

  • Menu systems (Menus and Menu Items)

  • Product categories

  • HTML/XML DOM trees

  • UI controls (Panels containing Buttons, TextBoxes, etc.)

Without a proper design pattern, developers often write separate logic for individual objects and collections of objects, resulting in duplicated code and increased complexity.

The Composite Design Pattern provides a clean solution by allowing clients to treat individual objects (Leaf nodes) and groups of objects (Composite nodes) uniformly.


What is the Composite Design Pattern?

Definition

The Composite Pattern is a Structural Design Pattern that composes objects into tree structures to represent part-whole hierarchies.

It allows clients to treat individual objects and compositions of objects uniformly.

Simply put:

A single object and a group of objects share the same interface.


Why Do We Need the Composite Pattern?

Imagine a file explorer.

Documents
│
├── Resume.docx
├── Project
│   ├── Design.pdf
│   ├── Code.zip
│   └── Notes.txt
└── Photos
    ├── Image1.jpg
    └── Image2.jpg

Without the Composite Pattern:

  • Files require one set of operations.

  • Folders require another.

  • Recursive traversal becomes complicated.

With the Composite Pattern:

  • Both File and Folder implement the same interface.

  • The client simply calls Display() without worrying about whether it's a file or a folder.


The Problem with Hierarchical Structures

Consider an organization hierarchy.

CEO
 ├── Manager A
 │      ├── Developer 1
 │      └── Developer 2
 │
 └── Manager B
        ├── Tester
        └── Designer

Without Composite:

if(employee is Manager)
{
    // Traverse subordinates
}
else
{
    // Display employee
}

Every new hierarchy requires additional conditional logic.

Composite eliminates these checks by treating every node the same way.


Real-World Analogy

Think of a company organization chart.

  • An Employee may work individually.

  • A Manager is also an employee but manages a team.

Both are employees.

Similarly:

  • A File is an item.

  • A Folder is also an item that contains other items.

The client interacts with both using the same interface.


Composite Pattern Structure

The Composite Pattern consists of:

  • Component – Common interface

  • Leaf – Individual object

  • Composite – Collection of components

  • Client – Uses the component interface


UML Class Diagram

                    +----------------------+
                    |     Component        |
                    +----------------------+
                    | + Display()          |
                    +----------^-----------+
                               |
          +--------------------+--------------------+
          |                                         |
+----------------------+              +----------------------+
|       File           |              |      Folder          |
+----------------------+              +----------------------+
| Display()            |              | Add()               |
|                      |              | Remove()            |
+----------------------+              | Display()           |
                                      +---------+-----------+
                                                |
                                     Contains many Components

Components of the Composite Pattern

Component

Defines the common interface.

public interface IFileSystem
{
    void Display();
}

Leaf

Represents an individual object.

public class File : IFileSystem
{
    public string Name { get; }

    public File(string name)
    {
        Name = name;
    }

    public void Display()
    {
        Console.WriteLine($"File : {Name}");
    }
}

Composite

Represents a collection of components.

public class Folder : IFileSystem
{
    public string Name { get; }

    private readonly List<IFileSystem> items = new();

    public Folder(string name)
    {
        Name = name;
    }

    public void Add(IFileSystem item)
    {
        items.Add(item);
    }

    public void Remove(IFileSystem item)
    {
        items.Remove(item);
    }

    public void Display()
    {
        Console.WriteLine($"Folder : {Name}");

        foreach (var item in items)
        {
            item.Display();
        }
    }
}

Complete C# Console Application

class Program
{
    static void Main()
    {
        Folder root = new Folder("Documents");

        root.Add(new File("Resume.docx"));
        root.Add(new File("Profile.pdf"));

        Folder project = new Folder("Project");

        project.Add(new File("Design.pdf"));
        project.Add(new File("Code.zip"));

        root.Add(project);

        root.Display();
    }
}

Output

Folder : Documents
File : Resume.docx
File : Profile.pdf
Folder : Project
File : Design.pdf
File : Code.zip

Notice that both File and Folder are treated exactly the same through the IFileSystem interface.


ASP.NET Core Implementation

Suppose you're building a dynamic navigation menu.

Component

public interface IMenuComponent
{
    string Render();
}

Leaf

public class MenuItem : IMenuComponent
{
    public string Name { get; set; }

    public string Render()
    {
        return $"Menu : {Name}";
    }
}

Composite

public class MenuGroup : IMenuComponent
{
    public string Name { get; set; }

    private readonly List<IMenuComponent> menus = new();

    public void Add(IMenuComponent menu)
    {
        menus.Add(menu);
    }

    public string Render()
    {
        StringBuilder builder = new();

        builder.AppendLine(Name);

        foreach (var menu in menus)
        {
            builder.AppendLine(menu.Render());
        }

        return builder.ToString();
    }
}

The Composite Pattern makes it easy to render nested menus regardless of depth.


Real-World Examples

The Composite Pattern is ideal for representing tree-like structures.

File Explorer

Folder
    File
    File
    Folder
        File

Organization Hierarchy

CEO
    Manager
        Developer
        Tester

HTML DOM

html
 ├── head
 ├── body
 │      ├── div
 │      ├── button
 │      └── table

Product Categories

Electronics
    Laptop
    Mobile
    TV

Menu Systems

Admin
    Users
    Roles
    Permissions

UI Controls

Panel
    Button
    Label
    TextBox

Advantages

  • Treats individual objects and collections uniformly.

  • Simplifies client code.

  • Supports recursive tree structures naturally.

  • Makes adding new component types easier.

  • Promotes the Open/Closed Principle.

  • Reduces conditional statements.

  • Improves maintainability.


Disadvantages

  • Can make the design overly generic.

  • Difficult to restrict which components can contain children.

  • Deep hierarchies may affect performance.

  • Debugging recursive structures can be more challenging.


Best Practices

  • Use Composite when representing part-whole hierarchies.

  • Keep the Component interface simple and focused.

  • Use recursion carefully to avoid stack overflows with extremely deep trees.

  • Hide collection management from the client when possible.

  • Apply Dependency Injection for composite services in ASP.NET Core.


Common Mistakes

Adding Child Methods to Leaf Objects

Leaf objects should not expose Add() or Remove() methods if they cannot contain children.


Overusing Composite

Not every parent-child relationship requires the Composite Pattern. Use it only when clients need to treat single objects and groups uniformly.


Mixing Business Logic

Composite classes should focus on managing child components, not unrelated business operations.


Ignoring Performance

Very deep recursive hierarchies may require optimization or iterative traversal.


Composite vs Decorator vs Flyweight

FeatureCompositeDecoratorFlyweight
PurposeRepresent tree structuresAdd behavior dynamicallyShare objects to reduce memory
Uses Recursion✅ Yes❌ No❌ No
Parent–Child Relationship✅ Yes❌ No❌ No
Object Sharing❌ No❌ No✅ Yes
Runtime Behavior Extension❌ No✅ Yes❌ No

Interview Questions

1. What is the Composite Design Pattern?

A structural design pattern that composes objects into tree structures, allowing clients to treat individual objects and groups uniformly.


2. When should you use the Composite Pattern?

When working with hierarchical structures such as folders, menus, organization charts, or UI controls.


3. What is the difference between a Leaf and a Composite?

  • Leaf represents an individual object and cannot contain children.

  • Composite represents a group of objects and can contain both Leaf and Composite objects.


4. Which SOLID principle does the Composite Pattern support?

The Open/Closed Principle (OCP) because new component types can be introduced without modifying existing client code.


5. Is recursion commonly used in the Composite Pattern?

Yes. Recursive traversal is one of the key characteristics of the Composite Pattern.


6. What are common real-world examples?

  • File systems

  • HTML DOM

  • Organization charts

  • Product categories

  • Navigation menus

  • UI component hierarchies


Summary

The Composite Design Pattern is one of the most effective ways to model hierarchical data structures. By allowing individual objects and groups of objects to share a common interface, it simplifies client code, reduces conditional logic, and makes recursive operations straightforward.

In enterprise C# and ASP.NET Core applications, the Composite Pattern is widely used for file systems, menu structures, organization hierarchies, UI components, and document object models. Mastering this pattern will help you design scalable, maintainable applications that work naturally with tree-like data.


Coming Up Next: Part 3.4 – Decorator Design Pattern

In the next article, we'll explore the Decorator Design Pattern, including:

  • What is the Decorator Pattern?

  • Why inheritance is not always the best choice

  • Dynamically adding behavior to objects

  • Decorator Pattern Structure

  • UML Class Diagram

  • Complete C# Console Application

  • ASP.NET Core implementation

  • Real-world examples (Coffee Shop, Notification System, ASP.NET Core Middleware)

  • Advantages and disadvantages

  • Best practices

  • Common mistakes

  • Interview questions

You'll learn how the Decorator Pattern enables you to extend object functionality dynamically without modifying existing classes, making it one of the most widely used structural patterns in modern C# and ASP.NET Core applications.

Bridge Design Pattern

 Mastering Design Patterns in C# and ASP.NET Core

Part 3.2 – Bridge Design Pattern

Series: Design Patterns in C# and ASP.NET Core
Pattern Category: Structural Design Pattern
Difficulty: ⭐⭐⭐⭐☆ (Intermediate)
Prerequisites: OOP Concepts, Interfaces, Abstract Classes, Composition, Dependency Injection


Table of Contents

  1. Introduction

  2. What is the Bridge Design Pattern?

  3. Why Do We Need the Bridge Pattern?

  4. The Problem with Inheritance

  5. Real-World Analogy

  6. Bridge Pattern Structure

  7. UML Class Diagram

  8. Components of the Bridge Pattern

  9. Complete C# Console Application

  10. ASP.NET Core Implementation

  11. Real-World Examples

  12. Advantages

  13. Disadvantages

  14. Best Practices

  15. Common Mistakes

  16. Bridge vs Adapter vs Strategy

  17. Interview Questions

  18. Summary


Introduction

Inheritance is one of the most fundamental concepts in Object-Oriented Programming (OOP). It allows us to reuse code and create specialized classes from existing ones.

However, as applications grow, relying solely on inheritance can lead to an explosion of classes.

Imagine you're building a notification system that supports:

Notification Types

  • Email

  • SMS

  • Push Notification

Providers

  • Azure Communication Services

  • Twilio

  • SendGrid

If you use inheritance for every combination, you might end up with classes like:

AzureEmailNotification
AzureSmsNotification
AzurePushNotification

TwilioEmailNotification
TwilioSmsNotification
TwilioPushNotification

SendGridEmailNotification
SendGridSmsNotification
SendGridPushNotification

With just 3 notification types and 3 providers, you've already created 9 classes.

If tomorrow another provider is introduced, the number of classes increases significantly.

The Bridge Pattern solves this problem by separating the abstraction from its implementation.


What is the Bridge Design Pattern?

Definition

The Bridge Pattern is a Structural Design Pattern that separates an abstraction from its implementation so that both can evolve independently.

Instead of combining every abstraction with every implementation through inheritance, the Bridge Pattern connects them using composition.

This significantly reduces the number of classes and improves flexibility.


Why Do We Need the Bridge Pattern?

Suppose your application supports multiple report types:

  • Sales Report

  • Employee Report

  • Inventory Report

Each report can be exported as:

  • PDF

  • Excel

  • Word

Without the Bridge Pattern, you might create:

SalesPdfReport
SalesExcelReport
SalesWordReport

EmployeePdfReport
EmployeeExcelReport
EmployeeWordReport

InventoryPdfReport
InventoryExcelReport
InventoryWordReport

As report types or export formats grow, the number of classes increases rapidly.

Instead, we can separate:

  • Report (Abstraction)

  • Exporter (Implementation)

Any report can now work with any exporter at runtime.


The Problem with Inheritance

Consider two independent dimensions:

  • Notification Type

  • Provider

If each notification type inherits from each provider, the design becomes rigid.

Problems include:

  • Too many classes

  • Difficult maintenance

  • Code duplication

  • Limited flexibility

  • Violates the principle of Composition over Inheritance

The Bridge Pattern addresses this by connecting abstractions and implementations through interfaces.


Real-World Analogy

Think of a television and its remote control.

Different TV brands:

  • Samsung

  • Sony

  • LG

Different remote types:

  • Basic Remote

  • Smart Remote

  • Voice Remote

You don't create separate remotes for every TV model.

Instead:

  • The remote (abstraction) communicates with the TV (implementation).

  • Either the remote or the TV can change independently.

This is the essence of the Bridge Pattern.


Bridge Pattern Structure

The Bridge Pattern consists of:

  • Abstraction

  • Refined Abstraction

  • Implementor

  • Concrete Implementor

The abstraction holds a reference to the implementor instead of inheriting from it.


UML Class Diagram

                +----------------------+
                |     IExporter        |
                +----------------------+
                | + Export(string)     |
                +----------^-----------+
                           |
          +----------------+----------------+
          |                                 |
+----------------------+         +----------------------+
| PdfExporter          |         | ExcelExporter        |
+----------------------+         +----------------------+
| Export()             |         | Export()             |
+----------------------+         +----------------------+

                 Composition
                      ▲
                      |
            +----------------------+
            |      Report          |
            +----------------------+
            | - exporter           |
            | + Generate()         |
            +----------^-----------+
                       |
          +------------+-------------+
          |                          |
+----------------------+   +----------------------+
| SalesReport          |   | EmployeeReport      |
+----------------------+   +----------------------+
| Generate()           |   | Generate()          |
+----------------------+   +----------------------+

Components of the Bridge Pattern

Implementor

Defines the implementation interface.

public interface IExporter
{
    void Export(string data);
}

Concrete Implementors

public class PdfExporter : IExporter
{
    public void Export(string data)
    {
        Console.WriteLine($"Exporting PDF: {data}");
    }
}

public class ExcelExporter : IExporter
{
    public void Export(string data)
    {
        Console.WriteLine($"Exporting Excel: {data}");
    }
}

Abstraction

public abstract class Report
{
    protected readonly IExporter exporter;

    protected Report(IExporter exporter)
    {
        this.exporter = exporter;
    }

    public abstract void Generate();
}

Refined Abstraction

public class SalesReport : Report
{
    public SalesReport(IExporter exporter)
        : base(exporter)
    {
    }

    public override void Generate()
    {
        exporter.Export("Sales Report");
    }
}

Complete C# Console Application

class Program
{
    static void Main()
    {
        Report report = new SalesReport(new PdfExporter());

        report.Generate();

        report = new SalesReport(new ExcelExporter());

        report.Generate();
    }
}

Output

Exporting PDF: Sales Report

Exporting Excel: Sales Report

Notice that the SalesReport class never changes.

Only the exporter changes.


ASP.NET Core Implementation

Suppose an ASP.NET Core application generates invoices.

Invoices can be exported to:

  • PDF

  • Excel

Export Interface

public interface IInvoiceExporter
{
    void Export(string invoice);
}

Exporters

public class PdfInvoiceExporter : IInvoiceExporter
{
    public void Export(string invoice)
    {
        Console.WriteLine($"PDF Invoice: {invoice}");
    }
}

public class ExcelInvoiceExporter : IInvoiceExporter
{
    public void Export(string invoice)
    {
        Console.WriteLine($"Excel Invoice: {invoice}");
    }
}

Invoice Service

public class InvoiceService
{
    private readonly IInvoiceExporter _exporter;

    public InvoiceService(IInvoiceExporter exporter)
    {
        _exporter = exporter;
    }

    public void GenerateInvoice()
    {
        _exporter.Export("Invoice #1001");
    }
}

Dependency Injection

builder.Services.AddScoped<IInvoiceExporter, PdfInvoiceExporter>();

builder.Services.AddScoped<InvoiceService>();

Changing the registration to:

builder.Services.AddScoped<IInvoiceExporter, ExcelInvoiceExporter>();

changes the implementation without modifying the business logic.


Real-World Examples

The Bridge Pattern is commonly used when two dimensions of variation should evolve independently.

Document Generation

Reports:

  • Sales

  • Employee

  • Inventory

Formats:

  • PDF

  • Excel

  • Word


Notification Systems

Notifications:

  • Email

  • SMS

  • Push

Providers:

  • Azure Communication Services

  • Twilio

  • SendGrid


Payment Processing

Payment Types:

  • Card

  • UPI

  • Wallet

Providers:

  • Stripe

  • Razorpay

  • PayPal


Logging Systems

Log Types:

  • File

  • Database

  • Cloud

Frameworks:

  • Serilog

  • NLog

  • Microsoft Logging


Cloud Storage

Storage Operations:

  • Upload

  • Download

  • Delete

Providers:

  • Azure Blob Storage

  • Amazon S3

  • Google Cloud Storage


Advantages

  • Reduces class explosion.

  • Separates abstraction from implementation.

  • Promotes composition over inheritance.

  • Supports the Open/Closed Principle.

  • Improves maintainability.

  • Allows runtime switching of implementations.

  • Encourages loose coupling.


Disadvantages

  • Introduces additional abstractions and interfaces.

  • Can increase the number of classes for simple applications.

  • Requires careful design to identify independent dimensions.


Best Practices

  • Use the Bridge Pattern when there are two or more independent dimensions of variation.

  • Prefer composition instead of inheritance.

  • Inject implementors using Dependency Injection.

  • Keep abstractions focused on business behavior and implementors focused on technical details.

  • Avoid adding business logic to implementor classes.


Common Mistakes

Confusing Bridge with Adapter

The Adapter Pattern makes incompatible interfaces work together.

The Bridge Pattern separates abstraction from implementation.


Overusing Inheritance

If you're creating many subclasses just to support combinations of features, consider using the Bridge Pattern instead.


Mixing Responsibilities

Keep the abstraction responsible for business operations and the implementor responsible for execution details.


Using Bridge for Simple Designs

If there is only one implementation and no expectation of future variation, the Bridge Pattern may add unnecessary complexity.


Bridge vs Adapter vs Strategy

FeatureBridgeAdapterStrategy
PurposeSeparate abstraction and implementationConvert incompatible interfacesEncapsulate interchangeable algorithms
Uses Composition✅ Yes✅ Yes✅ Yes
Changes Interface❌ No✅ Yes❌ No
Runtime FlexibilityHighModerateHigh
Primary GoalAvoid class explosionIntegrationAlgorithm selection

Interview Questions

1. What is the Bridge Design Pattern?

A structural design pattern that separates an abstraction from its implementation so that both can evolve independently.


2. What problem does the Bridge Pattern solve?

It prevents a combinatorial explosion of subclasses when there are multiple independent dimensions of variation.


3. What is the difference between Bridge and Adapter?

  • Bridge is designed during system architecture to separate abstractions from implementations.

  • Adapter is typically introduced later to make incompatible interfaces work together.


4. Which SOLID principles does the Bridge Pattern support?

  • Open/Closed Principle (OCP): New abstractions and implementations can be added without modifying existing code.

  • Dependency Inversion Principle (DIP): Abstractions depend on interfaces rather than concrete implementations.


5. Where is the Bridge Pattern commonly used?

  • Report generation

  • Notification systems

  • Payment providers

  • Logging frameworks

  • Cloud storage abstractions

  • Cross-platform applications


6. Why is composition preferred over inheritance in the Bridge Pattern?

Composition provides greater flexibility, reduces coupling, and allows implementations to be changed at runtime without modifying the abstraction.


Summary

The Bridge Design Pattern is a powerful structural pattern that separates what an object does (abstraction) from how it does it (implementation). By connecting the two through composition instead of inheritance, it prevents class explosion, improves flexibility, and supports scalable application design.

In modern C# and ASP.NET Core applications, the Bridge Pattern is particularly valuable when integrating multiple providers, supporting different output formats, or designing systems with independently evolving dimensions.

Mastering the Bridge Pattern will help you create cleaner architectures that are easier to extend, maintain, and test as business requirements grow.


Coming Up Next: Part 3.3 – Composite Design Pattern

In the next article, we'll explore the Composite Design Pattern, including:

  • What is the Composite Pattern?

  • Tree Structures and Hierarchical Objects

  • Parent–Child Relationships

  • Leaf vs. Composite Objects

  • UML Class Diagram

  • Complete C# Console Application

  • ASP.NET Core implementation

  • Real-world examples (File System, Organization Hierarchy, Menu Systems)

  • Advantages and disadvantages

  • Best practices

  • Common mistakes

  • Interview questions

You'll learn how the Composite Pattern enables you to treat individual objects and groups of objects uniformly, making it ideal for representing hierarchical structures such as folders, menus, organizational charts, and UI components.

Don't Copy

Protected by Copyscape Online Plagiarism Checker