Адаптер (Adapter) — шаблон проектирования

20 января 2020
Шаблон предлагает прослойку в виде класса-адаптера для совместимости интегрируемого класса и пользовательского кода.

Пример задачи: получение ссылки на страницу оплаты для разных платёжных систем.

Далее приводится реализация паттерна на языке PHP. Скачать исходники (ZIP, 3 Кб)



std_pay_system.class.php 

Cодержит типовой класс StdPaySystem платёжной системы. У класса есть метод getLinkPay(), возвращающий ссылку на оплату заказа.
<?
/**
 * Стандартная платёжная система сайта
 */
class StdPaySystem
{
    /**
     * ID заказа на сайте
     * @var int
     */
    private $orderId;

    /**
     * Сумма заказа к оплате
     * @var int
     */
    private $paySum;

    /**
     * Конструктор объекта
     * @param int $orderId - ID заказа
     * @param int $paySum - сумма заказа к оплате
     */
    public function __construct($orderId, $paySum) 
    {
        $this->orderId = (int)$orderId;
        $this->paySum = (int)$paySum;
    }

    /**
     * Возвращает ссылку на оплату заказа
     * @return string
     */
    public function getLinkPay()
    {
        return 'https://test-payment-system1.ru/form-pay.html?order='.($this->orderId).'&sum='.($this->paySum);
    }
}

?>

test_std_pay.php

Скрипт с клиентским кодом, в котором создаётся объект платёжной системы, задаются ID заказа и сумма к оплате, а затем происходит получение и вывод ссылки на оплату заказа.
<?
//Стандартная платёжная система
require_once(__DIR__.'/std_pay_system.class.php');

$orderId = 1234567;
$orderSum = 1000;

$paySystem = new StdPaySystem($orderId, $orderSum);
$linkPay = $paySystem->getLinkPay();

echo $linkPay;

?>

Результат работы скрипта test_std_pay.php
https://test-payment-system1.ru/form-pay.html?order=1234567&sum=1000

int_pay.class.php

Содержит класс IntPay интегрируемой платёжной системы «ИнтПей». По легенде, это официальная PHP-библиотека от разработчиков системы и она не содержит отдельной функции для получения ссылки на оплату заказа. Но разработчики ведут документацию по API системы и предлагают через официальный класс посылать запросы в API, используя метод requestToAPI($method, $params=array())
<?
/**
 * Платёжная система "ИнтПей" (тестовая интегрируемая платёжная система)
 */
class IntPay
{
    /**
     * Адрес API для отправки запросов
     * @var string
     */
    private static $urlAPI = 'https://api.test-intpay.ru';

    /**
     * Отправляет запрос в API
     *
     * @param string $method - символьный код функции сервиса
     * @param array $params - массив с параметрами запроса

     * @return array - вернёт массив c ответом
     */
    public static function requestToAPI($method, $params=array())
    {
        //Приведение типов
        if (is_array($params) == false) {
        	$params = array();
        }

        //Адрес запроса
        $requestUrl = self::$urlAPI . '/'.$method.'/';

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $requestUrl);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT, 10);
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_AUTOREFERER, true);
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $params);

        $response = curl_exec($curl);
        curl_close($curl);

        if ($response != '') {
            $response = json_decode($response, true);
        }

        if (is_array($response) == false) {
            $response = array();
        }

        return $response;
    }
}

?>

Прочитав документацию ИнтПей, мы узнали, что для получения ссылки на оплату заказа в API есть метод pay_link.

Как вариант, для получения платёжной ссылки ИнтПей, мы можем прямо в клиентском коде написать обработку, которая отправляла бы запрос в API и обрабатывала результат. Но такой подход не гибок, потому что если нам понадобится получить платёжную ссылку где-то в другом месте проекта, то придётся снова писать отправку запроса в API, плодя дубли кода.

Другой вариант решения — создать класс-прослойку (адаптер), который будет содержать публичные методы, как у типового класса StdPaySystem и соответственно легко интегрироваться в клиентский код. Но при этом внутри себя адаптер будет работать с платёжной системой ИнтеПей, используя официальный класс IntPay

int_pay_adapter.class.php 

Содержит класс-прослойку IntPayAdapter. Для получения ссылки на оплату используется метод getLinkPay().
<?
//Платёжная система IntPay
require_once(__DIR__.'/int_pay.class.php');

/**
 * Класс-адаптер платёжной системы "ИнтПей"
 */
class IntPayAdapter
{
    /**
     * ID заказа на сайте
     * @var int
     */
    private $orderId;

    /**
     * Сумма заказа к оплате
     * @var int
     */
    private $paySum;

    /**
     * Конструктор объекта
     * @param int $orderId - ID заказа
     * @param int $paySum - сумма заказа к оплате
     */
    public function __construct($orderId, $paySum) 
    {
        $this->orderId = (int)$orderId;
        $this->paySum = (int)$paySum;
    }

    /**
     * Возвращает ссылку на оплату заказа
     * @return string
     */
    public function getLinkPay()
    {
        $response = IntPay::requestToAPI('pay_link', array(
            'order_num' => $this->orderId,
            'sum' => $this->paySum
        ));
        if (is_array($response)) {
            if (isset($response['url']) && $response['url'] != '') {
                return $response['url'];
            } else {
                if (isset($response['error']) && $response['error'] != '') {
                    throw new Exception('Ошибка. При получении ссылки на оплату вернулась ошибка "'.$response['error'].'"');
                } else {
                    throw new Exception('Ошибка. Не удалось получить ссылку на оплату');
                }
            }
        } else {
            throw new Exception('Ошибка. В ответ на запрос IntPay::requestToAPI пришёл не массив');
        }
    }
}

?>

test_int_pay.php 

Скрипт клиентского кода, в котором создаётся объект платёжной системы ИнтПей, указываются ID заказа и сумма к оплате, затем через метод getLinkPay() происходит получение и вывод платёжной ссылки.

Используя класс-адаптер IntPayAdapter, работа c ИнтПей стала такой же, как работа с типовой платёжной системой сайта.
<?
//Адаптер платёжной системы "Интпей"
require_once(__DIR__.'/int_pay_adapter.class.php');

$orderId = 1234567;
$orderSum = 1000;

$paySystem = new IntPayAdapter($orderId, $orderSum);
$linkPay = $paySystem->getLinkPay();

echo $linkPay;

?>

Результат работы скрипта test_int_pay.php
https://test-intpay.ru/pay.html?token=0fe65e4b0c7babf3cceff90b1e2f408b4e9ceee3