Skip to main content

Real World Implementation Of Adapter Pattern

Consider the following scenario: your application uses a MySQL database, but you need to switch to a PostgreSQL database. The two databases use different methods for establishing connections, executing queries, and so on.

Here's a class diagram for the database example using the Adapter pattern,

This diagram illustrates the relationship between the MySQLDatabase, PostgreSQLDatabase, and DatabaseAdapter classes. The DatabaseAdapter adapts the PostgreSQLDatabase to the MySQLDatabase interface, allowing instances of PostgreSQLDatabase to be used where MySQLDatabase instances were expected.

Real World Implementation

First, let's define the original MySQL class:

class MySQLDatabase {
connectToMySQL(uri: string): void {
console.log(`Connecting to MySQL database at ${uri}`);
// Implementation...
}

executeMySQLQuery(query: string): void {
console.log(`Executing MySQL query: ${query}`);
// Implementation...
}
}

Your application code may be scattered with calls to these methods, like this:

let database = new MySQLDatabase();
database.connectToMySQL("mysql://localhost:3306/mydb");
database.executeMySQLQuery("SELECT * FROM users");

Now, you decide to switch to PostgreSQL, but you can't just replace the MySQLDatabase instances with PostgreSQLDatabase instances, because the PostgreSQLDatabase class has a different interface:

class PostgreSQLDatabase {
connectToPostgres(uri: string): void {
console.log(`Connecting to PostgreSQL database at ${uri}`);
// Implementation...
}

executePostgresQuery(query: string): void {
console.log(`Executing PostgreSQL query: ${query}`);
// Implementation...
}
}

Here's where the Adapter pattern comes in. You can create an adapter class that translates the MySQLDatabase interface to the PostgreSQLDatabase interface:

class DatabaseAdapter {
private database = new PostgreSQLDatabase();

connectToMySQL(uri: string): void {
this.database.connectToPostgres(uri);
}

executeMySQLQuery(query: string): void {
this.database.executePostgresQuery(query);
}
}

Now, you can use DatabaseAdapter in your application where MySQLDatabase was expected:

let database = new DatabaseAdapter();
database.connectToMySQL("mysql://localhost:3306/mydb");
database.executeMySQLQuery("SELECT * FROM users");

The DatabaseAdapter makes your application believe it's still working with a MySQLDatabase, but under the hood, it uses a PostgreSQLDatabase. This allows you to switch databases with minimal changes to your application's code.

Advantages Of The Adapter Pattern

The Adapter pattern offers several advantages.

Reusability and flexibility

The Adapter pattern allows developers to reuse existing code without significant modifications. This is particularly useful when dealing with legacy code or third-party libraries.

In the above example, the PostgreSQLDatabase class can be reused without any changes, even though its interface doesn't match the interface used by the rest of the application:

class PostgreSQLDatabase {
connectToPostgres(uri: string): void {
console.log(`Connecting to PostgreSQL database at ${uri}`);
// Implementation...
}

executePostgresQuery(query: string): void {
console.log(`Executing PostgreSQL query: ${query}`);
// Implementation...
}
}

Decoupling

The Adapter pattern helps in decoupling the client and the services which reduces the dependencies between them. Changes to one component do not directly impact others. This makes the code more maintainable.

In the given example, if the PostgreSQLDatabase class changes, the rest of the application isn't affected, because these changes are encapsulated in the DatabaseAdapter class:

class DatabaseAdapter {
private database = new PostgreSQLDatabase();

connectToMySQL(uri: string): void {
this.database.connectToPostgres(uri);
}

executeMySQLQuery(query: string): void {
this.database.executePostgresQuery(query);
}
}

Enabling interoperability

The Adapter pattern can help different parts of a system work together even if their interfaces don't match. This is especially important when you want to integrate existing components or libraries into your application.

In our example, the DatabaseAdapter class enables the PostgreSQLDatabase to work with the rest of the application that expects a MySQLDatabase interface:

let database = new DatabaseAdapter();
database.connectToMySQL("mysql://localhost:3306/mydb");
database.executeMySQLQuery("SELECT * FROM users");

The Adapter pattern is best used when it solves a specific problem like integrating an incompatible interface or when it simplifies the codebase by hiding complex or unnecessary details.

TypeScript Course Instructor Image
TypeScript Course Instructor Image

Time To Transition From JavaScript To TypeScript

Level Up Your TypeScript And Object Oriented Programming Skills. The only complete TypeScript course on the marketplace you building TypeScript apps like a PRO.

SEE COURSE DETAILS

What Can You Do Next 🙏😊

If you liked the article, consider subscribing to Cloudaffle, my YouTube Channel, where I keep posting in-depth tutorials and all edutainment stuff for ssoftware developers.

YouTube @cloudaffle