Menu
Coddy logo textTech

Паттерн «Адаптер»

Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy. Урок 76 из 91.

Паттерн Adapter — это структурный шаблон проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе. Он действует как мост между двумя интерфейсами, преобразуя вызовы из одного формата в другой.

Представьте, что вы создаёте application для обработки платежей и вам нужно интегрировать стороннюю payment library. library отлично работает, но its interface не соответствует тому, что ожидает ваш код. Вместо того чтобы переписывать application целиком, вы создаёте adapter, который обеспечивает перевод между ними.

<?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";
    }
}

adapter implements ваш ожидаемый interface, при этом внутри использует class стороннего разработчика:

<?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

adapter converts доллары в центы и вызывает соответствующий method. Ваш application-код знает только о PaymentProcessor, оставаясь блаженно не осведомлённым о специфических требованиях Stripe. Если позже вы переключитесь на different платёжного провайдера, вы просто создадите новый adapter, не изменяя существующий код.

challenge icon

Задание

Легко

Давайте создадим систему преобразования температуры с использованием паттерна Adapter. У вас есть legacy-датчик температуры, который выдаёт показания в Fahrenheit, но ваше современное weather application expects все температуры в Celsius. Вместо изменения любой из систем вы создадите адаптер, который устранит этот разрыв.

Вы организуете код в четырёх файлах:

  • TemperatureSensor.php: Define интерфейс TemperatureSensor, который ожидает ваше weather application. У него должен быть method getTemperature(): float, возвращающий температуру в Celsius.
  • LegacySensor.php: Create LegacySensor class, имитирующий old оборудование. У этого class другой интерфейс, чем тот, который нужен вашему application:
    • Он принимает показание в Fahrenheit в своём constructor (используйте продвижение свойств constructor)
    • У него есть method readFahrenheit(): float, возвращающий сохранённое значение Fahrenheit
  • LegacySensorAdapter.php: Подключите интерфейс TemperatureSensor и class LegacySensor. Create class LegacySensorAdapter, который implements TemperatureSensor и оборачивает LegacySensor. Ваш адаптер должен:
    • Accept LegacySensor в своём constructor
    • Implement getTemperature(), считывая значение Fahrenheit с legacy-датчика и преобразуя его в Celsius по formula: (fahrenheit - 32) * 5 / 9
  • main.php: Подключите файл LegacySensorAdapter. Вы получите один input: температуру в Fahrenheit.

    Create LegacySensor с входной температурой, затем оберните его в LegacySensorAdapter. Call getTemperature() у адаптера и выведите результат, округлённый до двух знаков после запятой.

    Формат Output должен быть таким: Temperature: [celsius]C

Этот challenge демонстрирует, как паттерн Adapter позволяет интегрировать incompatible интерфейсы без изменения существующего кода. Ваше weather application работает исключительно с интерфейсом TemperatureSensor, совершенно не зная, что фактические показания поступают от legacy-датчика Fahrenheit.

Попробуйте сами

<?php
require_once 'LegacySensorAdapter.php';

// Прочитать ввод
$fahrenheit = floatval(trim(fgets(STDIN)));

// TODO: Создать LegacySensor с входной температурой
// TODO: Обернуть его в LegacySensorAdapter
// TODO: Вызвать getTemperature() на адаптере
// TODO: Вывести результат, округлённый до двух знаков после запятой
// Формат вывода: Temperature: [celsius]C

?>
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор PHP