适配器设计模式综合指南

admin 2024-01-03 501 阅读 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。
喜欢就支持以下吧
点赞 0

发表评论

快捷回复: 表情:
aoman baiyan bishi bizui cahan ciya dabing daku deyi doge fadai fanu fendou ganga guzhang haixiu hanxiao zuohengheng zhuakuang zhouma zhemo zhayanjian zaijian yun youhengheng yiwen yinxian xu xieyanxiao xiaoku xiaojiujie xia wunai wozuimei weixiao weiqu tuosai tu touxiao tiaopi shui se saorao qiudale qinqin qiaoda piezui penxue nanguo liulei liuhan lenghan leiben kun kuaikule ku koubi kelian keai jingya jingxi jingkong jie huaixiao haqian aini OK qiang quantou shengli woshou gouyin baoquan aixin bangbangtang xiaoyanger xigua hexie pijiu lanqiu juhua hecai haobang caidao baojin chi dan kulou shuai shouqiang yangtuo youling
提交
评论列表 (有 0 条评论, 501人围观)

最近发表

热门文章

最新留言

热门推荐

标签列表