Adapter Design Pattern in R

1. Definition

The Adapter Design Pattern acts as a bridge between two incompatible interfaces. It allows classes with incompatible interfaces to work together by converting the interface of one class into an interface expected by the client.

2. Problem Statement

Suppose you have an old system that returns data in a legacy format. You're building a new system that expects data in a modern format. Instead of refactoring the old system, which can be risky and time-consuming, how can you make these two systems work together?

3. Solution

Introduce an Adapter. This Adapter takes the output from the old system (legacy format) and transforms it into the format expected by the new system (modern format).

4. Real-World Use Cases

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

2. Software libraries that provide a modern interface to legacy software.

3. Middleware that translates data between systems that use different data formats.

5. Implementation Steps

1. Identify the interfaces that are incompatible.

2. Create an adapter class that joins functionalities of independent or incompatible interfaces.

3. The adapter class will implement the expected interface and will have a reference to the instance of the legacy system.

6. Implementation in R Programming

# Step 1: Incompatible interfaces
# Old System (Legacy interface)
OldSystem <- function() {
  list(
    getLegacyData = function() {
      return("name:John;age:25;city:NY;")
    }
  )
}
# New System expects data as a list
NewSystem <- function() {
  list(
    processData = function(data) {
      cat("Name:", data$name, "\nAge:", data$age, "\nCity:", data$city, "\n")
    }
  )
}
# Step 2: Adapter class
Adapter <- function(oldSystemInstance) {
  list(
    getAdaptedData = function() {
      legacyData <- oldSystemInstance$getLegacyData()
      dataParts <- unlist(strsplit(legacyData, ";"))
      adaptedData <- list(
        name = sub("name:", "", dataParts[1]),
        age = as.numeric(sub("age:", "", dataParts[2])),
        city = sub("city:", "", dataParts[3])
      )
      return(adaptedData)
    }
  )
}
# Step 3: Client code
oldSystem <- OldSystem()
adapter <- Adapter(oldSystem)
newSystem <- NewSystem()
adaptedData <- adapter$getAdaptedData()
newSystem$processData(adaptedData)

Output:

Name: John
Age: 25
City: NY

Explanation:

The Adapter Design Pattern in this scenario facilitates communication between the old system and the new system.

1. We initiated with the old system that provides data in a semi-colon separated format.

2. The new system, on the other hand, expects the data in a list format.

3. The Adapter plays a crucial role by fetching data from the old system, adapting it, and making it ready for the new system.

4. In the client code, the old system's data is adapted using the adapter and then processed by the new system.

The adapter pattern provides a versatile solution to combine functionalities of independent or incompatible interfaces without modifying their source code.

7. When to use?

Use the Adapter Pattern when:

1. You want to use an existing class, but its interface doesn't match your needs.

2. You want to create a reusable class that collaborates with unrelated or unforeseen classes.

3. Several classes should be used, but it's impractical to adapt their interface by subclassing them.

4. The Adapter pattern is a valuable tool when integrating systems or working with third-party libraries that can't be altered.

Remember, the main objective of the Adapter pattern is to make two existing interfaces work together without altering their code.


Comments