适配器设计模式综合指南

admin 2024-01-03 102 阅读 0评论

设计模式是面向对象编程中的重要工具,它提供了可重用的解决方案来解决常见的软件设计问题。结构设计模式关注于类和对象之间的结构。

今天我们将讨论结构设计模式之一,即适配器设计模式。

定义

适配器模式是一种软件设计模式,它允许将现有类的接口转换为另一个接口。

适配器模式通过使用一个中间类来实现两个不兼容的接口,从而使两个接口兼容。该中间类实现了两个接口,并将来自一个接口的调用转换为另一个接口的调用。

问题

假设您正在构建一个产品源应用程序。该应用程序从不同的电子商务 API 源(例如:Shopify、BigCommerce、WooCommerce 和 Prestashop)下载产品数据,对其进行处理,并将它们保存在统一的产品表中。该表将准备好处理并发送到不同的市场。

然而,存在两个挑战:

  • 并非所有电子商务 API 都相同。它们可能具有不同的方法、参数和返回值。
  • 如果稍后需要添加新的产品源(例如 Magento),则需要对应用程序进行更改。

解决方案

适配器模式可以解决产品源应用程序面临的挑战。它由四个构建块组成:

  • 客户端:这是使用 Target 接口并想要连接到多个源的类。在我们的例子中,这是 ProductFeed 类。它包含了程序现有的业务逻辑。
  • 目标:定义客户端将与之交互的单个 API 的接口或合约。在我们的例子中,这是 FeedAdapterInterface 接口。
  • 适配器:实现 Target 接口的类,持有 Adaptee 的实例,并将来自 Target 接口的调用适配到 Adaptee 的接口。在我们的例子中,这将是 ShopifyApiAdapter 类。
  • 适配者:客户端想要连接的源具有不兼容的接口。所有请求都委托给适应者。在我们的例子中,我们可以有三个:ShopifyApiBigCommerceApi 和 WooCommerceApi

代码

首先,我们需要定义一个客户端接口,该接口定义了客户端需要的功能。在本例中,我们需要一个 ProductFeed 接口,它具有 getFeed() 方法,该方法返回一个产品数组。

interface FeedAdapterInterface
{
    /**
     * @return array<int, array<string, string>>
     */
    public function getFeed(): array;
}

接下来,我们需要定义适应者类。适应者类将一个不兼容的接口转换为另一个兼容的接口。在本例中,我们需要将三个 API 服务转换为 ProductFeed 接口。

// Adaptee Classes aka Service classes

class ShopifyApi {
    public function fetchItems(): array {
        // Returns an  `array` of products, that hold a `title`, `date`, `image` and `url`.
    }
}
 
class BigCommerceApi {
    public function getProducts(): array {
        // returns an array of products tha have 'name''published_at''picture_url''url'
    }
}

class WooCommerceApi{
    public function getList(): array {
        // returns an array of products tha have 'name''published_date''cover''link'
    }
}

正如您所看到的,这些 API 服务具有不同的方法名称和有效负载格式。

现在,我们将创建适配器类来实现 FeedAdapterInterface 接口的 getFeed() 方法,并将调用从客户端类 ProductFeed 委托给特定的适应者服务,例如 ShopifyApiBigCommerceApi 和 WooCommerceApi

class ShopifyApiAdapter implements FeedAdapterInterface
{
    public function __construct(public ShopifyApi $api) {}

    public function getFeed(): array
    {
        // 从 Shopify API 获取产品数据
        $products = $this->api->fetchItems();

        // 转换产品数据格式,使其符合 FeedAdapterInterface 的规范
        return array_map(function ($item) {
            return [
                'title' => $item['title'],
                'date' => DateTime::createFromFormat('H:i:s Y-m-d'$item['date'])->format('Y-m-d H:i:s'),
                'img' => $item['image'],
                'url' => $item['url'],
            ];
        }, $products);
    }
}
class BigCommerceApiAdapter implements FeedAdapterInterface
{
    public function __construct(public BigCommerceApi $api) {}

    public function getFeed(): array
    {
        // 从 BigCommerce API 获取产品数据
        $products = $this->api->getProducts();

        // 转换产品数据格式
        return array_map(function ($item) {
            return [
                'title' => $item['name'],
                'date' => DateTime::createFromFormat('H:i:s Y-m-d'$item['published_at'])->format('Y-m-d H:i:s'),
                'img' => $item['picture_url'],
                'url' => $item['url'],
            ];
        }, $products);
    }
}
class WooCommerceApiAdapter implements FeedAdapterInterface
{
    public function __construct(public WooCommerceApi $api) {}

    public function getFeed(): array
    {
        // 从 WooCommerce API 获取产品数据
        $products = $this->api->getList();

        // 转换产品数据格式
        return array_map(function ($item) {
            return [
                'title' => $item['name'],
                'date' => DateTime::createFromFormat('H:i:s Y-m-d'$item['published_date'])->format('Y-m-d H:i:s'),
                'img' => $item['cover'],
                'url' => $item['link'],
            ];
        }, $products);
    }
}

我们将创建实现 FeedAdapterInterface 接口的 ProductFeed 类。该类将使用适配器类来连接到服务类。

class ProductFeed implements FeedAdapterInterface
{
    public function __construct(private array $adapters) {}

    public function getFeed(): array
    {
        // 获取适配器列表
        $adapters = is_array($this->adapters) ? $this->adapters : func_get_args();

        // 循环每个适配器并获取产品数据
        $products = [];
        foreach ($adapters as $adapter) {
            $products = array_merge($products$adapter->getFeed());
        }

        return $products;
    }
}

该类循环每个适配器类,并从每个适配器类获取产品数据。然后,该类将从每个适配器类获取的产品数据合并为一个数组。

用法

ProductFeed 类通过其构造函数获取适配器列表。

// 创建适配器
$shopify = new ShopifyApiAdapter();
$wooCommerce = new WooCommerceApiAdapter();
$bigCommerce = new BigCommerceApiAdapter();

// 创建 ProductFeed 实例
$productFeed = new ProductFeed([
    $shopify,
    $wooCommerce,
    $bigCommerce,
]);

// 获取产品数据
$products = $productFeed->getAllProducts();

// 打印产品数据
foreach ($products as $product) {
    var_dump($product);
}

优点

  • 可以将接口或数据转换代码与程序的主要业务逻辑分离,提高程序的可维护性和扩展性。
  • 可以使客户端类与特定的适配器类解耦,从而可以灵活地添加或更换不同的适配器类。
  • 符合单一职责原则、开放/关闭原则和依赖倒置原则。

其他例子:

可以使用适配器设计模式的其他示例包括:

  • 从不同来源获取数据,例如从 Twitter、Facebook 或 Instagram 获取新闻源。
  • 通过不同渠道发送通知,例如通过短信、Slack 或推送通知。
  • 使用不同的缓存驱动程序,例如 Redis 或 MemCache。
  • 使用不同的文件存储适配器,例如本地文件系统、Amazon S3 或 Google Cloud Storage。
  • 记录数据,例如日志记录或事件记录。
  • 连接到不同的数据库,例如 MySQL、PostgreSQL 或 MongoDB。

发表评论

快捷回复: 表情:
Addoil Applause Badlaugh Bomb Coffee Fabulous Facepalm Feces Frown Heyha Insidious KeepFighting NoProb PigHead Shocked Sinistersmile Slap Social Sweat Tolaugh Watermelon Witty Wow Yeah Yellowdog
提交
评论列表 (有 0 条评论, 102人围观)