Adapter Design Pattern in Ruby

1. Definition

The Adapter Design Pattern allows objects with incompatible interfaces to work together. It acts as a bridge between two interfaces. The pattern involves a single class, the Adapter, which joins functionalities of independent or incompatible interfaces.

2. Problem Statement

Imagine you have an existing application that uses a certain class with a specific interface. Later on, you decide to integrate a third-party library, but its methods don't match your existing application's code. Rewriting the entire application to fit the new library can be costly and impractical.

3. Solution

Instead of modifying the application to fit the third-party library or vice-versa, you can introduce an adapter. This adapter translates calls from your application to the format required by the library, ensuring both can work seamlessly.

4. Real-World Use Cases

1. Power adapters that allow devices from one country to be used in another country's power outlet.

2. Software drivers that let apps work with new hardware devices.

3. Translating data from one format (e.g., XML) to another (e.g., JSON).

5. Implementation Steps

1. Identify the existing interface you wish to adapt (Target Interface).

2. Identify the interface you wish to use (Adaptee).

3. Create the adapter class that will implement the Target Interface and maintain a reference to the Adaptee.

4. The adapter will then translate calls from the Target Interface to the Adaptee's methods.

6. Implementation in Ruby

# Step 1 & 2: Target Interface and Adaptee
class OldPrinter
  def print_data(data)
    "Printing data: #{data}"
  end
end
class ModernPrinter
  def display(data)
    "Displaying data: #{data}"
  end
end
# Step 3: Adapter
class PrinterAdapter
  def initialize(modern_printer)
    @modern_printer = modern_printer
  end
  def print_data(data)
    @modern_printer.display(data)
  end
end
# Client code
old_printer = OldPrinter.new
puts old_printer.print_data("Hello World!")
modern_printer = ModernPrinter.new
adapter = PrinterAdapter.new(modern_printer)
puts adapter.print_data("Hello World!")

Output:

Printing data: Hello World!
Displaying data: Hello World!

Explanation:

1. OldPrinter represents the existing application's interface (Target Interface) that needs adaptation.

2. ModernPrinter is the new interface (Adaptee) that doesn't fit the application's expectations.

3. PrinterAdapter is the adapter class that implements the print_data method of the OldPrinter but internally calls the display method of the ModernPrinter.

4. The client code showcases how both the original method and the adapted method can be used seamlessly.

7. When to use?

Use the Adapter pattern when:

1. You want to use a class with an interface that doesn't match the rest of your application.

2. You need to work with multiple existing subclasses but it's impractical to subclass each one. An adapter can adapt the interface of its parent class.

3. You need to transform data into various formats that might not be initially compatible.


Comments