Friday, March 13, 2026

Chain of Responsibility Design Pattern in C#

Chain of Responsibility Design Pattern in C#

The Chain of Responsibility Design Pattern provides a mechanism to decouple senders from receivers by allowing more than one object to handle a request. In this pattern, a request is passed along a chain of potential handler objects until an object handles it or the chain's end is reached.

The key idea is that the sender broadcasts a request without knowing which object in the chain will serve the request, ensuring that the sender and receiver remain loosely coupled.


Why C# Programmers Should Study It

  1. Decoupling – Separates the sender of a request from the receiver, improving modular design.
  2. Flexible Request Handling – Handlers can be inserted, removed, or reordered easily.
  3. Maintainability – Each handler can be updated independently.
  4. Scalability – New handlers can be added without affecting existing code.
  5. Common in Middleware – Widely used in ASP.NET Core pipelines and other .NET middleware.
  6. Interception and Enhancements – Useful for logging, validation, and processing pipelines.
  7. Single Responsibility Principle – Each handler performs one task.
  8. Deepening OOP Mastery – Strengthens understanding of object-oriented design.
  9. Real-world Applicability – Useful for UI event processing and request pipelines.

Participants (for C# Students)

Handler

  • Declares the method for handling requests.
  • Stores a reference to the next Handler.
  • Forwards requests when unable to handle them.

ConcreteHandler

  • Handles requests within its responsibility range.
  • Decides whether to process or pass forward.
  • Sends unhandled requests to the successor.

Client

  • Sends request to the first handler.
  • Does not know which handler will process it.
  • Relies on the chain to manage the request.

C# Implementation

Handler Interface


public interface Handler
{
    Handler SetNext(Handler handler);
    object Handle(object request);
}

The default chaining behavior can be implemented in a base handler class. Returning the handler allows convenient chaining such as: mouse.SetNext(cat).SetNext(dog);

AbstractHandler.cs


abstract class AbstractHandler : Handler
{
    private Handler _nextHandler;

    public Handler SetNext(Handler handler)
    {
        this._nextHandler = handler;
        return handler;
    }
        
    public virtual object Handle(object request)
    {
        if (this._nextHandler != null)
        {
            return this._nextHandler.Handle(request);
        }
        else
        {
            return null;
        }
    }
}

MouseHandler.cs


class MouseHandler : AbstractHandler
{
    public override object Handle(object request)
    {
       if ((request as string) == "Cheese")
       {
           return $"Mouse: I'll eat the {request.ToString()}.\n";
       }
       else
       {
           return base.Handle(request);
       }
    }
}

CatHandler.cs


class CatHandler : AbstractHandler
{
    public override object Handle(object request)
    {
        if (request.ToString() == "Catnip")
        {
            return $"Cat: I love {request.ToString()}.\n";
        }
        else
        {
            return base.Handle(request);
        }
    }
}

DogHandler.cs


class DogHandler : AbstractHandler
{
    public override object Handle(object request)
    {
        if (request.ToString() == "Bone")
        {
            return $"Dog: Oh my!! I'll eat the {request.ToString()}.\n";
        }
        else
        {
            return base.Handle(request);
        }
    }
}

Client Code

The client usually interacts with a single handler and is unaware that a chain exists.


class Client
{
    public static void ClientCode(AbstractHandler handler)
    {
        foreach (var food in new List<string> { "Bone", "Catnip", "Cheese" })
        {
            Console.WriteLine($"Client: Who wants a {food}?");
            var result = handler.Handle(food);

            if (result != null)
            {
                Console.Write($"   {result}");
            }
            else
            {
                Console.WriteLine($"   {food} was left untouched.");
            }
        }
    }
}

Program.cs


class Program
{
    static void Main(string[] args)
    {
        var mouse = new MouseHandler();
        var cat = new CatHandler();
        var dog = new DogHandler();

        mouse.SetNext(cat).SetNext(dog);

        Console.WriteLine("Chain: Dog > Cat > Mouse\n");
        Client.ClientCode(mouse);
        Console.WriteLine();

        Console.WriteLine("Subchain: Dog > Cat\n");
        Client.ClientCode(cat);
    }
}

Expected Output


Chain: Dog > Cat > Mouse
Client: Who wants a Bone?
   Dog: Oh my!! I'll eat the Bone.
Client: Who wants a Catnip?
   Cat: I love Catnip.
Client: Who wants a Cheese?
   Mouse: I'll eat the Cheese.

Subchain: Dog > Cat
Client: Who wants a Bone?
   Dog: Oh my!! I'll eat the Bone.
Client: Who wants a Catnip?
   Cat: I love Catnip.
Client: Who wants a Cheese?
   Cheese was left untouched.

Or shall I say… the cheese stands alone.


S.W.O.T. Analysis

Strengths

  • Encapsulates request processing into independent handlers.
  • Promotes separation of concerns.
  • Allows scalable request pipelines.
  • Maintains consistency in processing behavior.

Weaknesses

  • Introduces additional classes and interfaces.
  • Can increase complexity in small applications.
  • Extending handler types may require refactoring.

Opportunities

  • Works well with modern C# features like dependency injection.
  • Ideal for middleware pipelines in ASP.NET Core.
  • Highly applicable in enterprise software systems.

Threats

  • Risk of overengineering when unnecessary.
  • Potential performance overhead from multiple handlers.
  • Misuse by inexperienced developers can complicate architecture.

Thursday, March 05, 2026

Abstract Factory Design Pattern

The Abstract Factory pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes. (For Java developers)

5 Reasons to Study the Abstract Factory Pattern (for Java Developers)

1. Modularity Encourages code that’s easier to maintain by abstracting object creation for UI themes or database drivers.
2. Scalability Enables building complex systems — like GUI frameworks — where object families must evolve without breaking code.
3. Flexibility Supports switching between different product variants with minimal code changes.
4. Consistency Helps ensure that Java components from the same family are used together, avoiding incompatibility bugs.
5. Testability Facilitates mocking dependencies in unit tests by abstracting object creation behind factory interfaces.


🧩 ORM / UML Structure

The Abstract Factory pattern defines a set of product interfaces and a factory interface that creates the products. Concrete factories implement the factory interface to produce specific product variants. Clients depend only on abstract interfaces, allowing products and families to vary independently. Each concrete factory produces related products that share consistent behavior.


🌟 Abstract Factory — Participants (for Java Students)

AbstractFactory

  • Declares creation methods for each type of product.
  • Defines how families of products should be created without specifying concrete classes.
  • Enables interchangeability of entire product families.

ConcreteFactory

  • Implements the product creation methods defined in the AbstractFactory.
  • Returns concrete product instances belonging to a specific family (e.g., Italian or Indian meals).
  • Ensures that related products are consistent with a family’s theme.

AbstractProduct

  • Defines the interface for a type of product.
  • Ensures that all products in a family share common behavior.
  • Makes it possible for client code to use products polymorphically.

ConcreteProduct

  • Implements the AbstractProduct interface.
  • Provides specific product behavior (e.g., ItalianMainCourse or IndianDessert).
  • Works with other products from the same family.

Client

  • Uses only abstract interfaces — AbstractFactory and AbstractProduct.
  • Does not depend on concrete product classes.
  • Remains unaffected by changes to product families.

💻 Java Code Example

This example demonstrates a meal creation system using the Abstract Factory pattern. Two product types — main courses and desserts — are created by factories representing different meal types (Italian and Indian). :contentReference[oaicite:1]{index=1}

File: MainCourse.java


// MainCourse.java
public interface MainCourse {
    void serve();
}

File: Dessert.java


// Dessert.java
public interface Dessert {
    void serve();
}

File: ItalianMainCourse.java


// ItalianMainCourse.java
public class ItalianMainCourse implements MainCourse {
    @Override
    public void serve() {
        System.out.println("Serving an Italian pasta dish.");
    }
}

File: IndianMainCourse.java


// IndianMainCourse.java
public class IndianMainCourse implements MainCourse {
    @Override
    public void serve() {
        System.out.println("Serving an Indian curry dish.");
    }
}

File: ItalianDessert.java


// ItalianDessert.java
public class ItalianDessert implements Dessert {
    @Override
    public void serve() {
        System.out.println("Serving an Italian tiramisu dessert.");
    }
}

File: IndianDessert.java


// IndianDessert.java
public class IndianDessert implements Dessert {
    @Override
    public void serve() {
        System.out.println("Serving an Indian gulab jamun dessert.");
    }
}

File: MealFactory.java


// MealFactory.java
public interface MealFactory {
    MainCourse createMainCourse();
    Dessert createDessert();
}

File: ItalianMealFactory.java


// ItalianMealFactory.java
public class ItalianMealFactory implements MealFactory {
    @Override
    public MainCourse createMainCourse() {
        return new ItalianMainCourse();
    }
    @Override
    public Dessert createDessert() {
        return new ItalianDessert();
    }
}

File: IndianMealFactory.java


// IndianMealFactory.java
public class IndianMealFactory implements MealFactory {
    @Override
    public MainCourse createMainCourse() {
        return new IndianMainCourse();
    }
    @Override
    public Dessert createDessert() {
        return new IndianDessert();
    }
}

File: Main.java


// Main.java
public class Main {
    public static void main(String[] args) {
        MealFactory italianFactory = new ItalianMealFactory();
        MealFactory indianFactory = new IndianMealFactory();

        MainCourse italianMainCourse = italianFactory.createMainCourse();
        Dessert italianDessert = italianFactory.createDessert();

        MainCourse indianMainCourse = indianFactory.createMainCourse();
        Dessert indianDessert = indianFactory.createDessert();

        italianMainCourse.serve();
        italianDessert.serve();
        indianMainCourse.serve();
        indianDessert.serve();
    }
}

🧠 S.W.O.T. Analysis

✅ Strengths

  • Promotes clean separation between interface and implementation layers.
  • Simplifies the addition of new product families.
  • Reduces code duplication across product variants.

⚠️ Weaknesses

  • Increases complexity with multiple classes and interfaces.
  • Can lead to over-engineering in simple applications.
  • May obscure concrete object behavior from developers.

🚀 Opportunities

  • Ideal for plug-in architectures and enterprise-level system design.
  • Enables cross-platform UI development using consistent abstractions.
  • Supports Java applications needing runtime product switching.

⚡ Threats

  • Misuse can make debugging object creation chains difficult.
  • May hinder performance if used inappropriately in resource-constrained apps.
  • Could confuse team members unfamiliar with design patterns.

Tuesday, February 17, 2026

C++ Chain Of Responsibility Design Pattern with S.W.O.T. Analysis

Chain Of Responsibility Design Pattern

The Chain of Responsibility Design Pattern is a behavioral pattern that allows a request to pass through a chain of handlers until one of them handles it.

Instead of having a single object responsible for processing a request, multiple objects are given a chance to handle it. The request moves along the chain until it reaches an object capable of processing it.

This design pattern promotes loose coupling between the sender and receiver. The sender does not need to know which object will handle the request — only that the request will be handled somewhere in the chain.

By decoupling request senders from receivers, we create flexible, maintainable, and efficient design.


🧩 ORM / UML Structure

The UML structure of Chain of Responsibility contains three primary participants:

  • Handler (Interface / Abstract Class)
  • ConcreteHandler
  • Client

The key relationship is:

Client → Handler → ConcreteHandler → ConcreteHandler → ...

Each handler contains:

  • A reference to the next handler
  • A method to handle the request
  • Logic to either process or forward the request

The power of this pattern lies in the dynamic linking of handlers at runtime.


🌟 Chain of Responsibility — Participants (for C++ Students)

Handler

  • Defines the interface for handling requests.
  • Stores a reference to the next Handler.
  • Forwards requests if it cannot handle them.

ConcreteHandler

  • Handles requests it is responsible for.
  • Decides whether to process or forward.
  • Passes unhandled requests to successor.

Client

  • Sends requests to the first Handler.
  • Does not know which Handler processes it.
  • Relies on the chain for handling.

💻 C++ Code Example

Below is the example formatted cleanly for blog presentation.

Handler Interface

#ifndef HANDLER_H
#define HANDLER_H

#include <string>

class Handler {
protected:
    Handler* next;

public:
    Handler() : next(nullptr) {}

    virtual ~Handler() {}

    void setNext(Handler* handler) {
        next = handler;
    }

    virtual void handleRequest(const std::string& request) = 0;
};

#endif

ConcreteHandlerA

#ifndef CONCRETEHANDLERA_H
#define CONCRETEHANDLERA_H

#include "Handler.h"
#include <iostream>

class ConcreteHandlerA : public Handler {
public:
    void handleRequest(const std::string& request) override {
        if (request == "A") {
            std::cout << "ConcreteHandlerA handled request A\n";
        } else if (next) {
            next->handleRequest(request);
        }
    }
};

#endif

ConcreteHandlerB

#ifndef CONCRETEHANDLERB_H
#define CONCRETEHANDLERB_H

#include "Handler.h"
#include <iostream>

class ConcreteHandlerB : public Handler {
public:
    void handleRequest(const std::string& request) override {
        if (request == "B") {
            std::cout << "ConcreteHandlerB handled request B\n";
        } else if (next) {
            next->handleRequest(request);
        }
    }
};

#endif

main.cpp (Client)

#include "ConcreteHandlerA.h"
#include "ConcreteHandlerB.h"

int main() {
    ConcreteHandlerA handlerA;
    ConcreteHandlerB handlerB;

    handlerA.setNext(&handlerB);

    handlerA.handleRequest("A");
    handlerA.handleRequest("B");
    handlerA.handleRequest("C");

    return 0;
}

🧠 S.W.O.T. Analysis

✅ Strengths

  • Promotes loose coupling between sender and receiver.
  • Flexible and dynamic chain construction.
  • Respects Open/Closed Principle.

⚠️ Weaknesses

  • Can be harder to debug.
  • No guarantee a request will be handled.
  • Chain configuration must be done carefully.

🚀 Opportunities

  • Ideal for middleware systems.
  • Excellent for event processing pipelines.
  • Common in logging, GUI event handling, and request processing systems.

⚡ Threats

  • Long chains may reduce performance.
  • Improper chain setup can cause silent failures.
  • Overuse may complicate simple workflows.

Tuesday, February 10, 2026

PHP Structural Pattern the Adapter with code sample and UML

Adapter Design Pattern

The Adapter Design Pattern, categorized under Structural design patterns, acts as a bridge between two incompatible interfaces, enabling them to work together.

The pattern introduces an intermediary interface (the Adapter) that converts one interface into another, allowing classes with incompatible interfaces to collaborate seamlessly.


Why Should PHP Programmers Study the Adapter Design Pattern?

  1. Legacy System Integration:
    Enables modern PHP systems to work with older codebases without large rewrites.
  2. Third-party Tool Compatibility:
    Smooths integration when external libraries expose incompatible interfaces.
  3. Improved Code Reusability:
    Adapts existing components instead of rewriting working logic.
  4. Better Modularity:
    Keeps interface translation separate from business logic.
  5. Flexible System Evolution:
    Supports adding new functionality without disturbing existing architecture.
  6. Open/Closed Principle:
    Extends behavior without modifying existing classes.
  7. Simplified Interfaces:
    Wraps complex APIs into developer-friendly interfaces.
  8. Versatile Adaptation:
    Multiple adapters can support multiple integration scenarios.

Adapter — UML

Target

  • Defines the interface the Client expects
  • Represents standard system behavior
  • Used directly by the Client

Client

  • Works through the Target interface
  • Is unaware of the Adaptee
  • Relies on Adapter for compatibility

Adaptee

  • Has an incompatible interface
  • Contains useful existing functionality
  • Cannot be easily modified

Adapter

  • Implements the Target interface
  • Translates Client requests
  • Connects incompatible interfaces

Target.php

interface Target
{
    public function request(): string;
}

Adaptee.php

class Adaptee
{
    public function specificRequest(): string
    {
        return "Specific request from Adaptee.";
    }
}

Adapter.php

class Adapter implements Target
{
    private $adaptee;

    public function __construct(Adaptee $adaptee)
    {
        $this->adaptee = $adaptee;
    }

    public function request(): string
    {
        return "Adapter: " . $this->adaptee->specificRequest();
    }
}

index.php

require_once 'Target.php';
require_once 'Adaptee.php';
require_once 'Adapter.php';

function clientCode(Target $target)
{
    echo $target->request();
}

echo "Client code with Adaptee:<br/>";
$adaptee = new Adaptee();
echo $adaptee->specificRequest();
echo "<br/><br/>";

echo "Client code with Adapter:<br/>";
$adapter = new Adapter($adaptee);
clientCode($adapter);

Key Idea

The Adapter pattern wraps an existing class and translates method calls from the Target interface into calls understood by the Adaptee, allowing incompatible interfaces to work together without modification.


S.W.O.T. Analysis — Adapter Pattern (PHP)

Strengths

  • Enables seamless interface compatibility
  • Promotes reuse of legacy and third-party code
  • Decouples client logic from implementation

Weaknesses

  • Adds abstraction layers
  • May introduce slight performance overhead
  • Often temporary when legacy systems are replaced

Opportunities

  • Legacy modernization
  • Third-party API standardization
  • Cross-language integration

Threats

  • Bypassing adapters leads to inconsistency
  • Overuse can clutter architecture
  • Direct refactoring may be a better long-term solution

Tuesday, February 03, 2026

Java Structural Adapter Design Pattern

Java Adapter Design Pattern

Adapter Design Pattern

The Adapter Design Pattern is classified as a Structural design pattern.

Its main role is to act as a bridge between two incompatible interfaces, allowing them to work together.

It achieves this by creating a new interface—the Adapter—which enables an existing class to be used with other classes without changing its source code.

In essence, the Adapter pattern converts the interface of one class into an interface expected by the Client.


Why Should Java Programmers Study the Adapter Design Pattern?

  1. Integration with Legacy Code:
    Java has a long history and many legacy systems. The Adapter pattern allows older components to work with newer implementations without a complete rewrite.
  2. Facilitating Third-Party Libraries:
    Java’s ecosystem includes countless third-party libraries whose interfaces may not align with your system. Adapters bridge this gap cleanly.
  3. Enhancing Code Reusability:
    Existing components with useful behavior can be reused by adapting their interfaces instead of rebuilding functionality from scratch.
  4. Promotion of Modularity:
    By separating interface adaptation from core logic, the Adapter pattern improves maintainability and clarity.
  5. Incremental System Evolution:
    New behaviors can be introduced gradually without destabilizing existing workflows.
  6. Open/Closed Principle Adherence:
    The Adapter pattern aligns with SOLID principles by extending behavior without modifying existing code.
  7. Simplification of Interfaces:
    Adapters can present a simpler, more convenient interface when the original one is too complex.
  8. Multiple Interface Adaptations:
    Multiple adapters can be built for the same class to support different systems or APIs.

Given Java’s prominence in enterprise and open-source environments, understanding the Adapter pattern is essential for building adaptable, maintainable systems that evolve gracefully.


Structural Pattern — Adapter (for Java Students)

Target

  • Defines the interface the Client expects to use
  • Represents standard system behavior
  • Used directly by the Client

Client

  • Works with objects through the Target interface
  • Does not know about the Adaptee’s interface
  • Uses Adapter to communicate correctly

Adaptee

  • Has an existing interface incompatible with Target
  • Contains useful behavior
  • Cannot be easily modified

Adapter

  • Implements the Target interface
  • Translates Client requests into Adaptee calls
  • Safely connects incompatible interfaces

Project Layout

/adapter-gof-demo
  Target.java
  Client.java
  Adaptee.java
  Adapter.java
  Main.java

Target.java

public interface Target {
    String Request();
}

Adaptee.java

public class Adaptee {

    private String adapteeValue;

    public Adaptee(String adapteeValue) {
        this.adapteeValue = adapteeValue;
    }

    public String SpecificRequest() {
        return "Adaptee.SpecificRequest(): adapteeValue=" + adapteeValue;
    }

    public String getAdapteeValue() {
        return adapteeValue;
    }

    public void setAdapteeValue(String adapteeValue) {
        this.adapteeValue = adapteeValue;
    }
}

Adapter.java

public class Adapter implements Target {

    private Adaptee adaptee;
    private String prefix;

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
        this.prefix = "[Adapter default prefix] ";
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    @Override
    public String Request() {
        return prefix + "Translated -> " + adaptee.SpecificRequest();
    }
}

Client.java

public class Client {

    private Target target;

    public Client(Target target) {
        this.target = target;
    }

    public void Operation() {
        System.out.println(target.Request());
    }
}

Main.java (Demo)

public class Main {

    public static void main(String[] args) {

        Adaptee adaptee = new Adaptee("ORIGINAL_ADAPTEE_VALUE");
        Adapter adapter = new Adapter(adaptee);
        Client client = new Client(adapter);

        System.out.println("=== First call ===");
        client.Operation();
        System.out.println("Adaptee value: " + adaptee.getAdapteeValue());

        adapter.setPrefix("[Adapter UPDATED prefix] ");

        System.out.println("\n=== Second call ===");
        client.Operation();
        System.out.println("Adaptee value (unchanged): " + adaptee.getAdapteeValue());

        adaptee.setAdapteeValue("ADAPTEE_VALUE_CHANGED_DIRECTLY");

        System.out.println("\n=== Third call ===");
        client.Operation();
        System.out.println("Adaptee value (changed): " + adaptee.getAdapteeValue());
    }
}

What This Demo Proves

  • The Client depends only on the Target interface
  • The Adapter translates calls without modifying Adaptee
  • Changing Adapter state does not mutate Adaptee state
  • Only direct calls modify Adaptee’s internal value

S.W.O.T. Analysis of the Adapter Design Pattern in Java

Strengths

  • Enables interface compatibility
  • Encourages reuse of existing implementations
  • Improves architectural flexibility

Weaknesses

  • Adds indirection and possible overhead
  • Can be confusing for beginners
  • Becomes obsolete if legacy code is replaced

Opportunities

  • Legacy system modernization
  • Third-party API adaptation
  • Cross-platform Java support

Threats

  • Overuse can clutter architecture
  • Refactoring may be a better solution
  • Performance impact in real-time systems

Thursday, January 29, 2026

C# Structural Design Pattern Adapter

Adapter Design Pattern and Its Relevance to C# Programmers

Let’s discuss the Adapter Design Pattern and why it matters for C# developers.

Adapter Design Pattern

The Adapter Design Pattern (often called the Wrapper) is a structural design pattern.

Its primary intent is to bridge the gap between two incompatible interfaces.

Essentially, this pattern involves creating a new interface (the adapter) that allows an existing class to work with other classes without modifying its source code.

It serves as an intermediary that translates requests from one interface to another.


Why Should C# Programmers Study the Adapter Design Pattern?

  1. Integrate Legacy Systems:
    In enterprise environments, there’s often a need to integrate legacy systems with newer systems. The Adapter pattern makes old systems work with new ones without widespread changes.
  2. Promote Code Reusability:
    C# developers may have libraries or components with useful features that don’t match the required interface. Instead of rewriting, they can adapt the component, promoting reuse.
  3. Enhance Modularity:
    The Adapter pattern separates the adaptation logic from core functionality, producing a more modular and maintainable codebase.
  4. Expand Third-Party Library Compatibility:
    Third-party libraries often come with interfaces that don’t fit seamlessly. Adapters mold these components into shapes your system expects.
  5. Flexible System Evolution:
    As systems evolve, new interfaces emerge. The Adapter pattern helps incorporate new behavior without disrupting existing workflows.
  6. Open/Closed Principle:
    The Adapter pattern supports the SOLID Open/Closed Principle: software entities should be open for extension but closed for modification.
  7. Simplify Complex Interfaces:
    When an interface is complicated, an adapter can provide a simpler, more convenient entry point.
  8. Multiple Adapters:
    For a single interface, multiple adapters can be created to support different systems, giving flexibility in how systems interact.

In the context of C#, with its rich standard library and extensive ecosystem of third-party components, understanding the Adapter Design Pattern is crucial. It provides a structured way to ensure components and systems work in harmony, improving integration, modularity, and maintainability.

By mastering this pattern, C# developers can build adaptable and resilient software architectures.


Adapter Pattern Example in C#

The Adapter pattern allows objects with incompatible interfaces to collaborate. The main roles are Client, Target, Adapter, and Adaptee.







Structure

  1. Target Interface
  2. Adapter Class
  3. Adaptee Class
  4. Client Class


1) ITarget.cs — Target Interface

public interface ITarget
{
    string GetRequest();
}

Explanation: ITarget defines the interface expected by the Client.


2) Adaptee.cs — Adaptee Class

public class Adaptee
{
    public string GetSpecificRequest()
    {
        return "Specific request.";
    }
}

Explanation: Adaptee contains useful logic but exposes an incompatible interface.


3) Adapter.cs — Adapter Class

public class Adapter : ITarget
{
    private readonly Adaptee _adaptee;

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

    public string GetRequest()
    {
        return $"This is '{_adaptee.GetSpecificRequest()}'";
    }
}

Explanation: Adapter translates the Adaptee interface into the Target interface.


4) Client.cs — Client Class

public class Client
{
    public void Main()
    {
        Adaptee adaptee = new Adaptee();
        ITarget target = new Adapter(adaptee);

        Console.WriteLine("Adaptee interface is incompatible with the client.");
        Console.WriteLine("But with adapter client can call it's method.");
        Console.WriteLine(target.GetRequest());
    }
}

Explanation: The Client depends only on ITarget and knows nothing about Adaptee.


5) Program.cs — Entry Point

class Program
{
    static void Main(string[] args)
    {
        new Client().Main();
    }
}

Order of Creation

  1. ITarget.cs
  2. Adaptee.cs
  3. Adapter.cs
  4. Client.cs
  5. Program.cs

Expected Output

Adaptee interface is incompatible with the client.
But with adapter client can call it's method.
This is 'Specific request.'

This demonstrates how incompatible interfaces can work together without modifying existing code.


S.W.O.T. Analysis of the Adapter Design Pattern in C#

Strengths

  1. Compatibility: Bridges incompatible interfaces, allowing legacy code integration with modern C# applications.
  2. Reusability: Increases code reuse by adapting existing functionality to meet new requirements.
  3. Encapsulation: Encapsulates conversion logic, keeping it separate from business logic.

Weaknesses

  1. Added Complexity: Adds complexity when adapting simple interfaces, especially for small projects.
  2. Overhead: Runtime overhead may occur due to additional layers of indirection.
  3. Dependency Risk: Over-reliance on adapters can lead to fragile dependencies in dynamic environments.

Opportunities

  1. Legacy Systems: Enables integration of legacy systems into modern .NET environments.
  2. API Bridging: Simplifies bridging third-party APIs with custom implementations in C#.
  3. Cross-Team Collaboration: Facilitates collaboration by allowing teams to work with preferred interfaces.

Threats

  1. Overuse: Excessive use can clutter code with adapters, reducing clarity.
  2. Confusion: Mismanagement of adapter roles may confuse developers during maintenance.
  3. Direct Solutions: Simpler approaches might solve certain compatibility problems without needing adapters.

Tuesday, January 27, 2026

C++ Structural Design Pattern Adapter

Adapter Design Pattern (C++)

The Adapter pattern acts as a bridge between two incompatible interfaces. It allows two classes with incompatible interfaces to work together by converting the interface of one class into an interface that the other expects.

The Adapter pattern can be seen as a wrapper that modifies an existing class’s interface without altering its underlying code.

Core Roles

  1. Target – The interface the client expects.
  2. Adaptee – The existing interface that must be adapted.
  3. Adapter – Converts the Adaptee interface into the Target interface.

Code Example

Target.h

class Target
{
public:
    virtual void Request() = 0;
    virtual ~Target() = default;
};

Adaptee.h

class Adaptee
{
public:
    void SpecificRequest();
};

Adaptee.cpp

#include "Adaptee.h"
#include <iostream>

void Adaptee::SpecificRequest()
{
    std::cout << "[Adaptee] SpecificRequest\n";
}

Adapter.h

#include "Target.h"
#include "Adaptee.h"

class Adapter : public Target, public Adaptee
{
    Adaptee m_Adaptee;
public:
    void Request() override;
};

Adapter.cpp

#include "Adapter.h"
#include <iostream>

void Adapter::Request()
{
    std::cout << "[Adapter] Calling SpecificRequest\n";
    SpecificRequest();
}

main.cpp

#include "Target.h"
#include "Adapter.h"

void Client(Target* pTarget)
{
    pTarget->Request();
}

int main()
{
    Adapter adapter;
    Client(&adapter);
}

Program Output

[Adapter] Calling SpecificRequest
[Adaptee] SpecificRequest

S.W.O.T. Analysis (C++)

Strengths

  • Enables compatibility between incompatible interfaces
  • Promotes reuse of legacy and third-party code
  • Decouples client code from implementation details

Weaknesses

  • Introduces additional abstraction layers
  • Slight runtime overhead
  • Can overcomplicate small systems

Opportunities

  • Modernizing legacy C++ systems
  • Wrapping third-party APIs
  • Bridging C++ with other languages

Threats

  • Clients bypassing the adapter
  • Design clutter from excessive adapters
  • Simpler refactoring alternatives

Chain of Responsibility Design Pattern in C#

Chain of Responsibility Design Pattern in C# The Chain of Responsibility Design Pattern provides a mechanism to decouple senders from re...