Design Patterns: The Adapter Pattern

Design Patterns: The Adapter Pattern##

Tiếp tục chuỗi bài viết về design pattern, trong bài viết lần trước chúng ta đã cùng tìm hiểu về facade design pattern thông qua việc dựng một class facade để thực hiện quản lý hệ thống lớn và phức tạp.

Bài viết lần này chúng ta sẽ cùng tìm hiểu về một design pattern khác đó là adapter design pattern. Pattern này được sử dụng khi code của bạn phải phụ thuộc vào một thư viện bên ngoài hoặc 1 class khác mà có sự thay đổi một cách thường xuyên. Pattern này là một loại thuộc structural patterns bởi vì nó giúp bạn tổ chức code một cách hiệu quả giúp cho việc quản lý và mở rộng dễ dàng hơn.

Vấn đề###

<?php
class PayPal {

    public function __construct() {
        // Your Code here //
    }

    public function sendPayment($amount) {
        // Paying via Paypal //
        echo "Paying via PayPal: ". $amount;
    }
}

$paypal = new PayPal();
$paypal->sendPayment('2629');

Trong code phía trên, bạn có thể thấy chúng ta sử dụng class PayPal để thực hiện thao tác thanh toán thông qua function sendPayment. Ở đây, chúng ta có thể tạo trực tiếp object PayPal và thực hiện thanh toán. Công việc này có thể thực hiện ở nhiều nơi trong hệ thống của bạn như sau: $paypal->sendPayment('amount here');

Một thời gian sau, Paypal thay đổi tên method từ sendPayment thành payAmount. Rõ ràng điều này làm ảnh hưởng đến những người đã sử dụng hàm sendPayment. Lúc này, chúng ta cần thay thế toàn bộ những chỗ nào gọi hàm sendPayment thành payAmount. Điều này sẽ rất mất thời gian và đòi hỏi thời gian để test lại hệ thống một lần nữa.

Giải Pháp###

Một trong những giải pháp cho vấn đề này là sử dụng adapter design pattern.

Theo wikipedia:

In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used from another interface. It is often used to make existing classes work with others without modifying their source code.

Trong trường hợp này, chúng ta sẽ tạo một class interface và chúng ta sẽ không thực hiện bất kỳ thay đổi nào bên trong class interface này.

// Concrete Implementation of PayPal Class
class PayPal {

    public function __construct() {
        // Your Code here //
    }

    public function sendPayment($amount) {
        // Paying via Paypal //
        echo "Paying via PayPal: ". $amount;
    }
}

// Simple Interface for each Adapter we create
interface paymentAdapter {
    public function pay($amount);
}

class paypalAdapter implements paymentAdapter {

    private $paypal;

    public function __construct(PayPal $paypal) {
        $this->paypal = $paypal;
    }

    public function pay($amount) {
        $this->paypal->sendPayment($amount);
    }
}

Nhìn vào code bên trên chúng ta thấy không có sự thay đổi nào cho class PayPal. Thay thế vào đó chúng ta tạo thêm 1 interface cho payment và 1 class adapter cho PayPal.

Sau đó, chúng ta sẽ tạo object của class adapter để sử dụng thay thế cho phần chính của class 'PayPal'. Khi chúng ta tạo object của class adapter thì sẽ truyền object của class Paypal vào như một tham số, vì thế adapter class có thể gọi các hàm của class Paypal như sau:

$paypal = new paypalAdapter(new PayPal());
$paypal->pay('2629');

Bây giờ, khi Paypal thay đổi tên method từ sendPayment thành payAmount, chúng ta chỉ cần thay đổi ở class papalAdapter.

class paypalAdapter implements paymentAdapter {

    private $paypal;

    public function __construct(PayPal $paypal) {
        $this->paypal = $paypal;
    }

    public function pay($amount) {
        $this->paypal->payAmount($amount);
    }
}

Tất cả chỉ cần 1 thay đổi.

Thêm Adapter mới###

Bây giờ, chúng ta có thể thấy là việc tạo thêm 1 adapter mới rất dễ dàng. Tương tự adapter đã tồn tại, chúng ta tạo class MoneyBooker implement từ adapter payment.

// Concrete Implementation of MoneyBooker Class
class MoneyBooker {

    public function __construct() {
        // Your Code here //
    }

    public function doPayment($amount) {
        // Paying via MoneyBooker //
        echo "Paying via MoneyBooker: ".  $amount;
    }
}

// MoneyBooker Adapter
class moneybookerAdapter implements paymentAdapter {

    private $moneybooker;

    public function __construct(MoneyBooker $moneybooker) {
        $this->moneybooker = $moneybooker;
    }

    public function pay($amount) {
        $this->moneybooker->doPayment($amount);
    }
}

// Client Code
$moneybooker = new moneybookerAdapter(new MoneyBooker());
$moneybooker->pay('2629');

Tổng kết###

Một hệ thống lớn luôn luôn sử dụng, kết nối đến các thư viện bên ngoài hoặc API, vì thế chúng ta nên áp dụng apapter design pattern cài đặt các method adapter. Áp dụng tốt pattern này sẽ giúp hệ thống không xảy ra xự cố khi mà API hay bên thứ ba có sự thay đổi code.


All Rights Reserved