CODERNEXv1.0.0-stable
$whoami$ls_projects$cat_blog$ssh_contact
sudo login
CPU: 2.4%
PING: 24ms
Terminal_Navigation_Menu
>Aboutwhoami>Projectsls_projects>Blogcat_blog>Contactssh_contact
INITIALIZE_LOGIN

Engineer: Borhan Uddin [cite: 1]

Base_Loc: Khulna, Bangladesh [cite: 2]

Status: SESSION_ACTIVE
cd ../blog

The Power Trifecta: Combining Factory, Strategy, and Adapter Patterns in TypeScript

Borhan Uddin
2026.01.05
5 MIN_READ
4 HITS
The Power Trifecta: Combining Factory, Strategy, and Adapter Patterns in TypeScript

Here is the technical breakdown of the Factory, Strategy, and Adapter patterns, including their definitions, UML concepts, and the unified TypeScript implementation.

1. The Concepts

The Strategy Pattern (Behavioral)

Purpose: Defines a family of algorithms, encapsulates each one, and makes them interchangeable. It lets the algorithm vary independently from clients that use it.

Use Case: When you have multiple ways to do a specific task (like paying) and want to switch between them dynamically without complex if/else statements.

The Adapter Pattern (Structural)

Purpose: Converts the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.

Use Case: When you need to integrate a legacy class or a third-party library that doesn't match your system's current interface.

The Factory Pattern (Creational)

Purpose: Defines an interface for creating an object, but let subclasses decide which class to instantiate.

Use Case: When the logic to create an object is complex (like setting up an adapter) or when the specific type of object needed is determined at runtime.

2. The Implementation (TypeScript)

Here is how these three patterns function together in a single system.

Scenario: A payment system that handles modern strategies (Credit Card) and adapts a legacy bank system, all orchestrated by a factory.

javascript
// ==========================================
// 1. STRATEGY PATTERN
// Defines the common interface for all behaviors
// ==========================================

interface PaymentStrategy {
  pay(amount: number): void;
}

// Concrete Strategy A: Modern implementation
class CreditCardPayment implements PaymentStrategy {
  pay(amount: number): void {
    console.log(`✅ Paid $${amount} via Credit Card.`);
  }
}

// ==========================================
// 2. ADAPTER PATTERN
// Adapts an incompatible legacy class to the Strategy interface
// ==========================================

// The Incompatible Class (e.g., old banking system)
class LegacyBankSystem {
  // Note: Different method name and return type than PaymentStrategy
  makeTransfer(totalAmount: number): string {
    console.log(`...Connecting to legacy bank for transfer of $${totalAmount}`);
    return "TX_LEGACY_SUCCESS";
  }
}

// The Adapter
class BankTransferAdapter implements PaymentStrategy {
  private legacySystem: LegacyBankSystem;

  constructor(legacySystem: LegacyBankSystem) {
    this.legacySystem = legacySystem;
  }

  // Translates the 'pay' call into the legacy specific call
  pay(amount: number): void {
    const response = this.legacySystem.makeTransfer(amount);
    console.log(`✅ Bank Adapter finished: ${response}`);
  }
}

// ==========================================
// 3. FACTORY PATTERN
// Orchestrates creation logic (hiding instantiation details)
// ==========================================

class PaymentStrategyFactory {
  static getPaymentStrategy(method: 'creditcard' | 'banktransfer'): PaymentStrategy {
    switch (method) {
      case 'creditcard':
        return new CreditCardPayment();

      case 'banktransfer':
        // Complex creation logic (instantiating legacy system + adapter) 
        // is hidden from the client here.
        const legacySystem = new LegacyBankSystem();
        return new BankTransferAdapter(legacySystem);

      default:
        throw new Error('Unknown payment method');
    }
  }
}

// ==========================================
// CLIENT CODE
// Clean, decoupled, and unaware of the complexity
// ==========================================

function processUserCheckout(amount: number, method: 'creditcard' | 'banktransfer') {
  try {
    // 1. Ask Factory for the tool
    const strategy = PaymentStrategyFactory.getPaymentStrategy(method);

    // 2. Use the tool (Polymorphism)
    strategy.pay(amount);

  } catch (err) {
    console.error(err);
  }
}

// Usage
console.log("--- Transaction 1 ---");
processUserCheckout(100, 'creditcard');

console.log("\n--- Transaction 2 ---");
processUserCheckout(5000, 'banktransfer');

3. Summary of Roles

ComponentRole in this SystemPaymentStrategyThe Contract: Guarantees that every payment object has a .pay() method.CreditCardPaymentThe Standard: A normal implementation of the contract.LegacyBankSystemThe Outsider: An old class that doesn't fit the contract.BankTransferAdapterThe Translator: Wraps the outsider so it looks like a standard strategy.PaymentStrategyFactoryThe Builder: Hides the complexity of creating the Adapter.

System_Menu
ls ./projectsssh ./contact

Identity_Verified

Author: Borhan Uddin

Role: Sys_Admin

End of Buffer — All Systems Operational