Menu
CoddyTech

适配器模式

本课属于 Coddy PHP 学习旅程的“面向对象编程”单元。第 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 的具体要求。如果你之后改用其他支付服务提供商,只需创建一个新的适配器,无需更改现有代码。

challenge icon

挑战

简单

让我们使用适配器模式构建一个温度转换系统。你有一个旧版温度传感器,它输出华氏温度读数,但你的现代天气应用需要所有温度都使用摄氏度。你不必修改任一系统,而是创建一个适配器来弥合两者之间的差异。

你将把代码分成 4 个文件:

  • TemperatureSensor.php:定义天气应用所需的 TemperatureSensor 接口。它应该有一个 getTemperature(): float 方法,用于返回摄氏温度。
  • LegacySensor.php:创建一个模拟旧硬件的 LegacySensor 类。这个类的接口与你的应用所需的接口不同:
    • 它在构造函数中接收华氏温度读数(使用构造函数属性提升)
    • 它有一个 readFahrenheit(): float 方法,用于返回存储的华氏温度值
  • LegacySensorAdapter.php:引入 TemperatureSensor 接口和 LegacySensor 类。创建一个实现 TemperatureSensor 并包装 LegacySensor 的 LegacySensorAdapter 类。你的适配器应该:
    • 在构造函数中接收一个 LegacySensor
    • 通过读取旧版传感器中的华氏温度值,并使用公式 (fahrenheit - 32) * 5 / 9 将其转换为摄氏温度,实现 getTemperature()
  • main.php:引入 LegacySensorAdapter 文件。你会收到一个输入:华氏温度。

    使用输入的温度创建一个 LegacySensor,然后用 LegacySensorAdapter 包装它。在适配器上调用 getTemperature(),并打印四舍五入到小数点后两位的结果。

    输出格式应为:Temperature: [celsius]C

这个挑战展示了适配器模式如何在不修改现有代码的情况下集成不兼容的接口。你的天气应用只使用 TemperatureSensor 接口,完全不需要知道实际读数来自旧版华氏温度传感器。

亲手试试

<?php
require_once 'LegacySensorAdapter.php';

// 读取输入
$fahrenheit = floatval(trim(fgets(STDIN)));

// TODO:用输入的温度创建一个 LegacySensor
// TODO:用 LegacySensorAdapter 包装它
// TODO:调用适配器的 getTemperature() 方法
// TODO:将结果四舍五入到小数点后两位并输出
// 输出格式:Temperature: [celsius]C

?>
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: PHP 在线编译器