Adapter Design Pattern – Design Patterns

Adapter Design Pattern – Design Patterns

Adapter design pattern is one of the most used structural design patterns. It is very handy when you want to update a component of your client with a different one and doesn’t want to change the client’s code. We will see step by step refactoring of applying the pattern in this post.

Adapter Pattern Structure

In a daily life, an adapter adapts the needed device to another device. Think that you have an old mouse has a PS/2 port but your computer only has USB inputs. You have to use an adapter to adapt the PS/2 to USB.

This pattern is exactly the same. The adapter class adapts the needed component into your client code. Let’s check the class diagram below.

Adapter Pattern UML Diagram
Adapter Pattern UML Diagram

The Scenario

Think that you are a freelancer software developer. You are working for a boss and building a commercial website. It uses one kind of payment system “CoolPay”. When you were applying the payment system, the boss said you won’t use any other payment systems and won’t change in the future. Thus, when you were applying the component, you directly used the client library’s module “CoolPayClient”.

1
2
3
4
5
6
7
8
9
@Service
public class PaymentService {

    @Autowired
    CoolPayClient coolPayClient;
    public void pay(CustomerPaymentInfo customerPaymentInfo) {
        coolPayClient.pay(customerPaymentInfo.getCreditCardNumber(), customerPaymentInfo.getPaymentAmount());
    }
}

In the meanwhile, boss urgently called you in an evening. Said that “I have found a better, cheaper and faster payment system “MoreCoolerPay” and we have to use this instead of our “CoolPay”.

You know, you are a paid worker. Obey the order, no matter what!

Despite the fact that the CoolPayClient codes are spread all over the applications codes, your boss immediately opened the new task “Refactoring CoolPay to MoreCoolerPay”. The next day you have to apply it efficiently and A.S.A.P. to decrease the costs and increase the customer satisfaction.

Refactoring with Adapter Pattern

When you have investigated the new payment client’s code, you have realized that the methods are different. CoolPayClient has a “pay” method that takes credit card number and the amount of the payment.

1
void pay(String creditCardNumber, Decimal paymentAmount);

Conversely, MoreCoolerPayClient has a “transaction” method that takes credit card number, payment amount and a unique id for that transaction.

1
void transact(String creditCardNumber, Decimal paymentAmount, String uniqueId);

To refactor with adapter pattern, first, you must create an abstraction of your current structure.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface PayClient {
    void pay(String creditCardNumber, Decimal paymentAmount);
}
@Service
public class PaymentService {

    @Autowired("coolPayPaymentClient")
    PayClient payClient;
    public void pay(CustomerPaymentInfo customerPaymentInfo) {
        payClient.pay(customerPaymentInfo.getCreditCardNumber(), customerPaymentInfo.getPaymentAmount());
    }
}
@Component
public class CoolPayPaymentClient implements PayClient {

    @Autowired
    CoolPayClient coolPayClient;

    @Override
    public void pay(String creditCardNumber, Decimal paymentAmount) {
        coolPayClient.pay(creditCardNumber, paymentAmount);
    }
}

As a result of the abstraction, it is very easy for us to apply the new payment system.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Service
public class PaymentService {

    @Autowired("moreCoolerPayPaymentClient")
    PayClient payClient;
    public void pay(CustomerPaymentInfo customerPaymentInfo) {
        payClient.pay(customerPaymentInfo.getCreditCardNumber(), customerPaymentInfo.getPaymentAmount());
    }
}
@Component
public class MoreCoolerPayPaymentClient implements PayClient {

    @Autowired
    MoreCoolerPayClient moreCoolerPayClient;

    @Override
    public void pay(String creditCardNumber, Decimal paymentAmount) {
        moreCoolerPayClient.transact(creditCardNumber, paymentAmount, UUID.randomUUID());
    }
}

This pattern is also known as “wrapper”. As you can see, we have wrapped the MoreCoolerPaymentClient object in the MoreCoolerPayPaymentClient class and called its method int he overridden pay routine.

Conclusion

With this structural pattern, you can make different components in need suitable for the design of your application. I hope the explanation was clear enough to understand easily at one shot. If you have any questions please leave it in the comments section.

Please don’t forget to read my other post about S.O.L.I.D. principles.

Read More

Related Post