12/08/2018, 13:30

Tìm hiểu Apdapter Pattern

Tìm hiểu về Strategy pattern. </br> Bài viết được tham khảo từ cuốn Design pattern for dummies </br> Ở bài trước, tôi đã giới thiệu cho các bạn về Strategy pattern: https://viblo.asia/trung.nn.92/posts/znmMdy7YGr69. Hôm nay chúng ta sẽ tìm hiểm về Adapter pattern. Adapter ...

Tìm hiểu về Strategy pattern.

</br>

Bài viết được tham khảo từ cuốn Design pattern for dummies

</br>

Ở bài trước, tôi đã giới thiệu cho các bạn về Strategy pattern: https://viblo.asia/trung.nn.92/posts/znmMdy7YGr69. Hôm nay chúng ta sẽ tìm hiểm về Adapter pattern.

Adapter pattern(mẫu thiết kế tiếp hợp) là một mẫu thiết kế tiếp hợp cho phép chuyển đổi một interface có sẵn thành một interface khác thích hợp cho lớp đang viết. Một adapter cho phép các lớp làm việc với nhau, mà bình thường là không thể do sự không tương thích về interfaces, bằng cách bao đóng interface của riêng nó cho phù hợp với một lớp có sẵn.

Bài toán thực tế:

Bạn nhận được một yêu cầu từ quản lý là phải chuyển đổi cơ sở hạ tầng sang một hệ thống mới. Đó quả thực là một vấn đề. Hiện tại hệ thống của công ty đang cho phép người dùng sử dụng dịch vụ từ phần mềm của công ty ace và đóng gói chúng vào 1 lớp ace. Làm sao để chúng ta có thể chuyển đổi chúng cho phù hợp với hệ thống mới? Hệ thống đang được vân hành như hình vẽ:

c6-1.gif

Nhưng khi hệ thống thay đổi, nó yêu cầu đối tượng acme chứ không phải ace, vì vậy đối tượng ace không còn thích hợp nữa. Giải pháp ở đây là bạn sẽ sử dụng mẫu chuyển đổi Adapter như hình sau:

c6-3.gif

Đó là ý tưởng, bây giờ chúng ta cần phải giải quyết những rắc rối khi sử dụng với Adapter pattern:

</br> Adapter pattern cho phép bạn sửa đổi 1 interface giữa object và 1 class mà không phải sửa đổi trực tiếp lên chúng. Khi bạn làm việc với 1 ứng dụng được mua sẵn, sản phầm bạn nhận được thường không như những gì bạn mong muốn. Thông thường, giải pháp có thể là sử dụng một bộ chuyển đổi nhỏ.

Cách tốt nhất để xem mẫu Adapter làm việc là thông qua ví dụ. Hiện tại dữ liệu người dùng đang được đóng gói trong lớp Ace. Lớp này quản lý tên của khách hàng với 2 hàm: setName và getName.

Nhưng chúng ta cần phải chuyển sang sử dụng phần mềm Acme, mà cách thức quản lý khách hàng có đối chút khác biệt. Vấn đề ở đây là phần mềm Acme cần 1 đối tượng Acme. Đối tượng này sử dụng 4 hàm để quản lý tên của khách hàng như sau: setFirstName, getFirstName, setLastName, getLastName.

</br> Vì vậy bạn cần một bộ chuyển đổi để chắc chắn hệ thống Acme có thể xử lý được các đối tượng của Ace. Bộ chuyển đổi này gọi 2 hàm của đối tượng Ace và mở rộng chúng thành 4 hàm mà đối tượng Acme cần, hình như sau:

c6-6.gif

Đây chính là cách thức làm việc của mấu Adapter. Bạn sử dụng mẫu Adapter khi bạn cố gắng đưa 1 cái chốt hình vuông vào 1 cái lỗ hình tròn. Nếu không tương thích, bạn cần phải có thêm vào 1 bộ chuyển đổi (Adapter) để có thể thực hiện được yêu cầu như mong muốn.

Pattern này đặc biệt tốt khi bạn đang làm việc với mã nguồn cũ mà yêu cầu là không được thay đổi mã cũ, trong khi muốn thực thi những công việc mới.

Giờ đã đến lúc để mẫu Adapter làm việc.

Tạo một đối tượng Ace

Class Ace đang quản lý khách hàng với 2 hàm như sau được viết trong 1 interface AceInterface

public interface AceInterface
{
    public function setName($name);
    public function getName();
}

Đối tượng Ace sẽ được tạo ra từ lớp AceClass, lớp này hiện thực hóa interface trên.

class AceClass implements AceInterface
{
    private $name

    public function setName($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }
}

Đó là tất cả những gì bạn cần trong hệ thống cũ. Một đối tượng Ace được trả về. Tuy nhiên hiện tại, bạn muốn chuyển sang hệ thống Acme, hệ thống mới cần giao tiếp với đối tượng Acme.

Tạo đối tượng Acme

Tương tự như class Ace, chúng ta cũng cần tạo ra 1 Interface cho lớp Acme.

interface AcmeInterface
{
    public setFirstName($firstName);
    public setLastName($lastName);
    public getFirstName();
    public getLastName();
}

Đối tượng Acme được tạo từ lớp Acme, hiện thực interface AcmeInterface như sau:

class AcmeClass
{
    private $firstName;
    private $lastName;

    public function setFirstName($firstName)
    {
        $this->firstName = $firstName;
    }

    public function setLastName($lastName)
    {
        $this->lastName = $lastName;
    }

    public function getFirstName()
    {
        return $this->firstName;
    }

    public function getLastName()
    {
        return $this->lastName;
    }
}

Giờ thì bạn đã có 2 đối tượng Ace và Acme. Việc cần làm lúc này là bạn cần 1 bộ chuyển đổi (Adapter) để gắn đối tượng Ace với hệ thống Acme.

Tạo đối tượng chuyển đổi Ace-to-Acme

Ta tạo một bộ chuyển đổi là AceToAcmeAdapter và nó sẽ hiện thực interface AcmeInterface:

class AceToAcmeAdapter implements AcmeInterface
{
    public $aceObject;

    public function AceToAcmeAdapter(AceClass $ace)
    {
        $this->aceObject = $ace;
    }
}

Điểm khác biệt giữa Ace và Acme là đối tượng Ace chỉ định nghĩa tên khách hàng trong khi Acme thì định nghĩa tên theo họ và tên riêng biệt. Để chuyển đổi giữa đối tượng Ace và Acme, ta tách phần tên trong Ace ra thành họ và tên. Bạn vẫn có thể nhận được tên khách hàng lưu trữ trong đối tượng Ace khi dùng hàm getName như sau:

class AceToAcmeAdapter implements AcmeInterface
{
    public $aceObject;
    private $firstName;
    private $lastName;

    public function AceToAcmeAdapter(AceClass $ace)
    {
        $this->aceObject = $ace;
        $this->firstName = explode(' ', $this->aceObject->getName())[0];
        $this->lastName = explode(' ', $this->aceObject->getName())[1];
    }
}

Bây giờ bạn đã có tên va họ của khách hàng. Để tương thích với đối tượng Acme, bạn phải hiện thực các hàm của Acme như sau:

class AceToAcmeAdapter implements AcmeInterface
{
    public $aceObject;
    private $firstName;
    private $lastName;

    public function AceToAcmeAdapter(AceClass $ace)
    {
        $this->aceObject = $ace;
        $this->firstName = explode(' ', $this->aceObject->getName())[0];
        $this->lastName = explode(' ', $this->aceObject->getName())[1];
    }

    public function setFirstName($firstName)
    {
        $this->firstName = $firstName;
    }

     public function setLastName($lastName)
    {
        $this->lastName = $lastName;
    }

    public function getFirstName()
    {
        return $this->firstName;
    }

    public function getLastName()
    {
        return $this->lastName;
    }
}

Vậy là bạn đã có 1 Adapter. Giờ chúng ta se tìm hiểu cách nó hoạt động ra sao.

Vân hành Adapter

Thông qua phần trên, bạn đã chuyển đổi 1 đối tượng Ace để chúng tronng giống như đối tượng Acme. Giờ là lúc để thấy cách mẫu Adapter làm việc. Bắt đầu bằng việc tạo 1 đối tương Ace:

$aceObject = new AceClass();
$aceObject->setName('Set Name');
$adapter = new AceToAcmeAdapter($aceObject);
$firstName = $adapter->getFirstName();
$lastName = $adapter->getLastName();

Đó là những gì bạn mong muốn khi sử dụng đối tượng Acme thật sự. Bạn đã sử dụng một đối tượng Acme từ một đối tượng Ace nhờ 1 Adapter. Đó là cách làm việc của mẫu Adapter.

</br> Vậy là chúng ta vừa tìm hiểu xong về mẫu thiết kế Adapter (Adapter pattern). Hy vọng bài viết sẽ giúp ích cho các bạn trong việc lập trình.
0