Паттерн Адаптер
Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy — урок 76 из 91.
Паттерн Адаптер — это структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе. Он действует как мост между двумя интерфейсами, транслируя вызовы из одного формата в другой.
Представьте, что вы создаете приложение для обработки платежей и вам необходимо интегрировать стороннюю библиотеку платежей. Библиотека работает отлично, но ее интерфейс не соответствует ожиданиям вашего кода. Вместо того чтобы переписывать все приложение, вы создаете адаптер, который служит посредником между ними.
<?php
// Интерфейс, который ожидает ваше приложение
interface PaymentProcessor {
public function pay(float $amount): string;
}
// Сторонняя библиотека с другим интерфейсом
class StripeAPI {
public function createCharge(int $cents, string $currency): string {
return "Charged {$cents} {$currency} via Stripe";
}
}
Адаптер реализует ожидаемый вами интерфейс, используя внутри себя сторонний класс:
<?php
class StripeAdapter implements PaymentProcessor {
public function __construct(private StripeAPI $stripe) {}
public function pay(float $amount): string {
$cents = (int)($amount * 100);
return $this->stripe->createCharge($cents, 'USD');
}
}
$stripe = new StripeAPI();
$processor = new StripeAdapter($stripe);
echo $processor->pay(29.99);
Вывод:
Charged 2999 USD via StripeАдаптер конвертирует доллары в центы и вызывает соответствующий метод. Код вашего приложения знает только о PaymentProcessor, оставаясь в неведении относительно специфических требований Stripe. Если позже вы переключитесь на другого платежного провайдера, вы просто создадите новый адаптер, не меняя существующий код.
Задание
ЛегкоДавайте создадим систему преобразования температуры, используя паттерн Адаптер (Adapter). У вас есть устаревший датчик температуры, который выдает показания в градусах Фаренгейта, но ваше современное погодное приложение ожидает все значения в градусах Цельсия. Вместо того чтобы изменять какую-либо из систем, вы создадите адаптер, который устранит этот разрыв.
Вы распределите свой код по четырем файлам:
TemperatureSensor.php— Определите интерфейсTemperatureSensor, который ожидает ваше погодное приложение. Он должен иметь методgetTemperature(): float, который возвращает температуру в градусах Цельсия.LegacySensor.php— Создайте классLegacySensor, который имитирует старое оборудование. Этот класс имеет интерфейс, отличный от того, который нужен вашему приложению:- Он принимает значение в Фаренгейтах в своем конструкторе (используйте продвижение свойств в конструкторе — constructor promotion)
- Он имеет метод
readFahrenheit(): float, который возвращает сохраненное значение в Фаренгейтах
LegacySensorAdapter.php— Подключите интерфейс TemperatureSensor и класс LegacySensor. Создайте классLegacySensorAdapter, который реализуетTemperatureSensorи оборачиваетLegacySensor. Ваш адаптер должен:- Принимать
LegacySensorв своем конструкторе - Реализовывать
getTemperature(), считывая значение Фаренгейта из устаревшего датчика и конвертируя его в Цельсии по формуле:(fahrenheit - 32) * 5 / 9
- Принимать
main.php— Подключите файл LegacySensorAdapter. Вы получите на вход одно значение: температуру в Фаренгейтах.Создайте объект
LegacySensorс входной температурой, затем оберните его вLegacySensorAdapter. ВызовитеgetTemperature()у адаптера и выведите результат, округленный до двух знаков после запятой.Формат вывода должен быть:
Temperature: [celsius]C
Эта задача демонстрирует, как паттерн Адаптер позволяет интегрировать несовместимые интерфейсы без изменения существующего кода. Ваше погодное приложение работает исключительно с интерфейсом TemperatureSensor, совершенно не подозревая, что фактические показания поступают от устаревшего датчика Фаренгейта.
Шпаргалка
Паттерн Адаптер (Adapter Pattern) — это структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе, выступая в роли моста между ними.
Базовая структура
Определите интерфейс, который ожидает ваше приложение:
<?php
interface PaymentProcessor {
public function pay(float $amount): string;
}Сторонний класс с другим интерфейсом:
<?php
class StripeAPI {
public function createCharge(int $cents, string $currency): string {
return "Charged {$cents} {$currency} via Stripe";
}
}Создание адаптера
Адаптер реализует ожидаемый интерфейс, используя внутри себя сторонний класс:
<?php
class StripeAdapter implements PaymentProcessor {
public function __construct(private StripeAPI $stripe) {}
public function pay(float $amount): string {
$cents = (int)($amount * 100);
return $this->stripe->createCharge($cents, 'USD');
}
}Использование
$stripe = new StripeAPI();
$processor = new StripeAdapter($stripe);
echo $processor->pay(29.99);
// Output: Charged 2999 USD via StripeАдаптер выполняет преобразование между интерфейсами, позволяя коду вашего приложения оставаться неизменным при интеграции сторонних библиотек или смене реализаций.
Попробуйте сами
<?php
require_once 'LegacySensorAdapter.php';
// Чтение входных данных
$fahrenheit = floatval(trim(fgets(STDIN)));
// TODO: Создать LegacySensor с входной температурой
// TODO: Обернуть его в LegacySensorAdapter
// TODO: Вызвать getTemperature() у адаптера
// TODO: Вывести результат, округленный до двух знаков после запятой
// Формат вывода: Temperature: [celsius]C
?>В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово $thisМетодыСвойстваКонструктор (__construct)Деструктор (__destruct)Итоги — Простой калькулятор4Наследование
Основы наследованияКлючевое слово parent::Переопределение методовКлючевое слово finalАбстрактные классыИтоги — Иерархия сотрудников7Инкапсуляция
Public, Protected, PrivateПодробный разбор модификаторов доступаГеттеры и сеттерыСокрытие данныхConstructor Promotion (8.0)Итоги — Система учета студентов10Продвинутые концепции ООП
Композиция против наследованияВнедрение зависимостейАнонимные классыПеречисления (PHP 8.1)Файберы (PHP 8.1)Глубокое погружение в клонирование объектовГенераторы и итераторы13Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий2Пространства имен и автозагрузка
Введение в пространства именКлючевое слово useСтандарт автозагрузки PSR-4Автозагрузчик ComposerИтоги: структурированный проект5Интерфейсы и контракты
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовИнтерфейс против абстрактного классаType Hinting с интерфейсамиИтоги — Калькулятор фигур8Магические методы
Введение в магические методы__toString и __debugInfo__get, __set, __isset, __unset__call и __callStatic__clone и клонирование объектов__serialize и __unserializeИтоги — Кастомная коллекция11Система типов и обработка ошибок
Объявление типовNullable-типыТипы объединения и пересеченияКлассы исключенийИерархия пользовательских исключенийTry, Catch, FinallyИтоги — Валидатор форм14Проект: Управление библиотекой
Обзор проектаКлассы Book и User3Свойства классов
Свойства экземпляра и статические свойстваКонстанты в классахСтатические методы и свойстваПриватные и защищенные свойстваСвойства Readonly (PHP 8.1)Итоги — Менеджер банковского счета6Полиморфизм
Снова о переопределении методовПолиморфизм через интерфейсыType Hinting и Union TypesПозднее статическое связываниеИтоги — Payment Processor9Трейты
Введение в трейтыИспользование нескольких трейтовРазрешение конфликтов трейтовАбстрактные методы в трейтахТрейты и наследование12Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy