适配器模式的实际方面

Software development is improved every day by new concepts, methodologies, and high quality libraries and frameworks. But even with all these improvements, we cannot prevent change in software development. You may think that your system is designed perfectly to cater to all of its requirements, but there will always be a change request that ruins your perfect design. We have to be prepared for all possible changes as developers.

新概念,方法论,高质量的库和框架每天都在改善软件开发。 但是即使有所有这些改进,我们也不能阻止软件开发的变化。 您可能会认为您的系统设计得很完美,可以满足其所有要求,但是总会有更改请求,这会破坏您的完美设计。 作为开发人员,我们必须为所有可能的更改做好准备。

The Adapter pattern is a design pattern which is commonly used to manage changes in development. Throughout this article we’ll be looking at the usage and benefits of the patterns using real world applications.

适配器模式是一种设计模式,通常用于管理开发中的更改。 在整个本文中,我们将研究使用实际应用程序的模式的用法和好处。

什么是适配器模式? (What is the Adapter Pattern?)

The Adapter design pattern simplifies concerns by adapting to changes in existing functionalities as well as building new functionalities. In short, we can define an adapter as an interface which helps integrate incompatible components.

适配器设计模式通过适应现有功能的变化以及构建新功能来简化问题。 简而言之,我们可以将适配器定义为有助于集成不兼容组件的接口。

Assume we have a mobile phone which is used to access an email account to send emails. The phone and email application act as separate components which get connected through the Internet.

假设我们有一部手机,用于访问电子邮件帐户以发送电子邮件。 电话和电子邮件应用程序充当通过Internet连接的独立组件。

adapt-01

Now assume that we travel to a place where an Internet connection is not available for the phone. How do we access email in this situation? We need an adapter which connects our mobile phone to an email application. Let’s take a look at the features expected from such an adapter:

现在,假设我们前往的地方没有电话的Internet连接。 在这种情况下,我们如何访问电子邮件? 我们需要一个将手机连接到电子邮件应用程序的适配器。 让我们看一下这种适配器的预期功能:

  • Enable an Internet connection between mobile phone and email application.

    启用手机和电子邮件应用程序之间的Internet连接。
  • Access email application API to send an email.

    访问电子邮件应用程序API以发送电子邮件。

Considering the requirements, we can choose IFTTT as the adapter. IFTTT is a service that supports automating tasks with popular API’s. Thus we can use IFTTT as the adapter as shown in the illustration below.

考虑到要求,我们可以选择IFTTT作为适配器。 IFTTT是一项支持使用流行API自动化任务的服务。 因此,我们可以使用IFTTT作为适配器,如下图所示。

adapt-02

In this solution, we send a SMS to the IFTTT service containing the email text. There is no need for an Internet connection to send an SMS to an international number. Then the IFTTT service gets the message contents and initializes the recipe (recipes need to be configured prior) to send an email using the email application’s API.

在此解决方案中,我们将包含电子邮件文本的SMS发送到IFTTT服务。 无需Internet连接即可将SMS发送到国际号码。 然后,IFTTT服务获取消息内容并初始化配方(需要预先配置配方)以使用电子邮件应用程序的API发送电子邮件。

IFTTT has access to the email API as well as an Internet connection to the email application, completing both requirements expected from the adapter. IFTTT acts as the adapter to integrate our phone and email application which was in an incompatible state due to the unavailability of the Internet.

IFTTT可以访问电子邮件API以及与电子邮件应用程序的Internet连接,从而完成了适配器预期的两项要求。 IFTTT充当适配器来集成我们的电话和电子邮件应用程序,由于Internet的不可用,该应用程序处于不兼容状态。

I think by now you should have a clear understanding about the functionality of the Adapter pattern in the real world. Before we move further into the implementation, though, let’s take a look at the definition of the pattern as given by Wikipedia:

我认为到目前为止,您应该对现实世界中的Adapter模式的功能有了清晰的了解。 不过,在进一步进行实现之前,让我们看一下Wikipedia给出的模式的定义:

In computer programming, the adapter pattern is a design pattern that translates one interface for a class into a compatible interface.An adapter allows classes to work together that normally could not because of incompatible interfaces, by providing its interface to clients while using the original interface.

在计算机编程中,适配器模式是一种设计模式,可以将类的一个接口转换为兼容的接口。适配器通过在使用原始接口的同时将其接口提供给客户端,从而允许类由于接口不兼容而通常无法一起工作。

了解适配器模式实现 (Understanding Adapter Pattern Implementation)

It would be ideal to make use of a practical scenario to understand the process and components of the Adapter pattern, so assume that we have a common interface for email subscriptions for our website. The following code contains the implementation of an email subscription interface.

理想的是利用实际方案来了解Adapter模式的过程和组件,因此,假设我们有一个用于网站电子邮件订阅的通用接口。 以下代码包含电子邮件订阅接口的实现。

<?php
interface EmailSubscribe
{
    public function subscribe($email);
    public function unsubscribe($email);
    public function sendUpdates();
}

Developers and email service providers can implement this interface to provide email subscription classes for each of the email providers such as Feedburner, Mailchimp, etc. The following code contains a sample implementation for one service and the initialization of the sendUpdates() method.

开发人员和电子邮件服务提供者可以实现此接口,以为每个电子邮件提供者(例如Feedburner,Mailchimp等)提供电子邮件订阅类。以下代码包含一种服务的示例实现以及sendUpdates()方法的初始化。

<?php
class FeedburnerEmail implements EmailSubscribe
{
    public function subscribe($email) { }
    public function unsubscribe($email) { }

    public function sendUpdates() {
   	 // Get Available Subscribers
   	 // Get Website Updates
   	 // Send Emails
    }
}

$feedburner_email = new FeedburnerEmail();
$feedburner_email->sendUpdates();

Now assume Feedburner decides to change its library with the latest version.

现在假设Feedburner决定使用最新版本更改其库。

<?php
class FeedburnerEmailVersion2
{
    public function subscribe($email) { }
    public function unsubscribe($email) { }

    public function getSubscribers() {
   	 // Return Subscribers
    }

    public function sendEmails($subscribers) {
   	 // Get Website Updates
   	 // Send Emails
   	 echo "emails sent today";
    }
}

$feedburner_email = new FeedburnerEmailVersion2();
$subscribers = $feedburner_email->getSubscribers();
$feedburner_email->sendEmails($subscribers);

According to the preceding code, new methods are added and existing functionality is modified in the new version of the library. The initialization code has changed and the latest version of Feedburner has become incompatible with the EmailSubscribe interface.

根据前面的代码,在新版本的库中添加了新方法,并修改了现有功能。 初始化代码已更改,并且Feedburner的最新版本已与EmailSubscribe接口不兼容。

We cannot implement the common interface, and therefore we need an adapter to make the library compatible with the original interface to keep consistency in our code. Since the current version is not implementing the interface, we have no choice other than to create the adapter based on the interface.

我们无法实现公共接口,因此我们需要一个适配器来使库与原始接口兼容,以保持代码的一致性。 由于当前版本未实现该接口,因此除了基于该接口创建适配器之外,我们别无选择。

<?php
class FeedburnerAdapter implements EmailSubscribe
{
    public function subscribe($email) { }
    public function unsubscribe($email) { }

    public function sendUpdates() {
   	 $feedburner = new FeedburnerEmailVersion2();
   	 $subscribers = $feedburner->getSubscribers();
   	 $feedburner->sendEmails($subscribers);
    }
}

$feedburner_email = new FeedburnerAdapter();
$feedburner_email->sendUpdates();

The FeedburnerAdapter adapter initializes the Feedburner email library inside it’s sendUpdates() method and reconstructs the previous implementation by calling new methods in the latest version of the library. Now our application and the Feedburner library communicate through the standard interface of FeedburnerAdapter. Our application does not know that the implementation has changed and an adapter is working in place of the original library class. Developers can call the standard set of methods without making any change to their original code.

FeedburnerAdapter适配器在其sendUpdates()方法中初始化Feedburner电子邮件库,并通过在库的最新版本中调用新方法来重建以前的实现。 现在我们的应用程序和Feedburner的库通过标准接口进行通信FeedburnerAdapter 。 我们的应用程序不知道实现已更改,并且适配器正在代替原始库类工作。 开发人员可以调用标准方法集,而无需更改其原始代码。

Now it’s time for understanding theoretical aspects of the Adapter pattern using its class diagram.

现在是时候使用其类图来理解Adapter模式的理论方面了。

adapt-03

Usually we have a Client, Target, and Adaptee in our application, and the Adaptee class implements the Target interface. In situations where Client and Adaptee become incompatible, we create a new class called Adapter and place it in between Target and Adaptee to make the components compatible with each other.

通常,我们的应用程序中有一个ClientTargetAdaptee ,而Adaptee类实现了Target接口。 在ClientAdaptee不兼容的情况下,我们创建一个称为Adapter的新类,并将其放在TargetAdaptee之间,以使组件彼此兼容。

The diagram above contains the original design of the Adapter pattern to use in best-case scenarios. Interfaces are not used widely in PHP projects but this doesn’t mean that you cannot use the Adapter pattern. As long as some component integrates incompatible interfaces, it can be considered an adapter.

上图包含了在最佳情况下使用的Adapter模式的原始设计。 接口没有在PHP项目中广泛使用,但这并不意味着您不能使用Adapter模式。 只要某些组件集成了不兼容的接口,就可以将其视为适配器。

In my last article on Opauth, we discussed about using a strategy class in the Opauth library. It also acts as an adapter, even though it doesn’t implement any interfaces. The strategy adapter made the open authentication libraries compatible with the core Opauth library.

上一篇有关Opauth的文章中 ,我们讨论了在Opauth库中使用策略类的问题。 即使没有实现任何接口,它也可以充当适配器。 策略适配器使开放式身份验证库与核心Opauth库兼容。

谁开发适配器类? (Who Develops the Adapter Class?)

When we’re in need of an adapter, either we can create it as developers or we can ask the vendor to provide the adapter. It depends on the situation and type of the project we’re working on. If we are developing applications by using common third party libraries, then we will be responsible for creating adapters to suit the requirements. On the other hand, we might be developing a large scale application and expect the vendors to develop libraries specially for our application. In such scenarios, if the vendors changes their library then they should provide the Adapter as well.

当我们需要适配器时,可以将其创建为开发人员,也可以要求供应商提供适配器。 这取决于我们正在处理的项目的情况和类型。 如果我们使用通用的第三方库来开发应用程序,那么我们将负责创建适合需求的适配器。 另一方面,我们可能正在开发大型应用程序,并期望供应商为我们的应用程序专门开发库。 在这种情况下,如果供应商更改了其库,则他们也应提供适配器。

适配器模式–错误的方式 (Adapter Pattern – The Wrong Way)

Many experienced developers think that the Adapter pattern is used to fix poorly designed systems. Depending on the situation, we might have to agree with that. But let’s consider a slightly modified version of the email subscription example we discussed previously.

许多有经验的开发人员认为,适配器模式用于修复设计不佳的系统。 根据情况,我们可能不得不同意这一点。 但是,让我们考虑一下我们之前讨论的电子邮件订阅示例的略微修改版本。

Assume that two teams have been assigned to develop Feedburner and Mailchimp classes separately based on the original interface we used earlier.

假设已经分配了两个团队根据我们之前使用的原始接口分别开发Feedburner和Mailchimp类。

<?php
class FeedburnerEmail implements EmailSubscribe
{
    public function subscribe($email) { }
    public function unsubscribe($email) { }

    public function getSubscribers() {
   	 // Returns list of subscribers
    }

    public function sendUpdates() {
   	 $this->getSubscribers();
   	 // Get Website Updates
   	 // Send Emails
    }
}
<?php
class MailchimpEmail implements EmailSubscribe
{
    public $subscribers;

    public function subscribe($email) { }
    public function unsubscribe($email) { }

    public function getSubscribers() {
   	 $this->subscribers = "List of subscribers";
    }

    public function sendUpdates() {
   	 $subscribers = $this->subscribers;
   	 // Get Website Updates
   	 // Send Emails
    }
}

Even though both classes are compatible with the target interface, there is an incompatibility between the client and these two classes. Consider the initialization code to understand this better:

即使这两个类都与目标接口兼容,但客户端与这两个类之间仍不兼容。 考虑初始化代码以更好地理解这一点:

<?php
$email = FeedburnerEmail();
$email->sendUpdates();

$email = MailchimpEmail();
$email->getSubscribers();
$email->sendUpdates();

The code from Team 2 doesn’t match with the client initialization code and hence becomes incompatible. We need an adapter to fix the issue for the Mailchimp class. This is considered a bad use of Adapter pattern since we could have planned the interface properly to avoid such incompatibility issues.

Team 2中的代码与客户端初始化代码不匹配,因此不兼容。 我们需要一个适配器来解决Mailchimp类的问题。 这被认为是对适配器模式的错误使用,因为我们可以适当地计划接口以避免此类不兼容问题。

适配器模式–正确的方法 (Adapter Pattern – The Right Way)

Adapters are mostly used in situations where we work with third-party libraries or create a new functionality which is considerably different from the original requirements. So, let’s consider the following scenario for effective use of adapters.

适配器通常用于我们使用第三方库或创建与原始要求有很大不同的新功能的情况。 因此,让我们考虑以下有效使用适配器的方案。

Email subscriptions is working perfectly on our website. Due to the success of subscriptions, management is planning to implement Twitter subscriptions for the site. Currently, when a user subscribes through email, he or she will get email notifications about updates in website content. With the new requirement, basically the user subscribes by authenticating their Twitter account for our website. Whenever site is updated, new tweets will be created in their tweet stream about the update.

电子邮件订阅在我们的网站上运行良好。 由于订阅成功,管理层计划对网站实施Twitter订阅。 当前,当用户通过电子邮件订阅时,他或她将收到有关网站内容更新的电子邮件通知。 根据新要求,基本上,用户通过验证其Twitter帐户来订阅我们的网站。 每当网站更新时,都会在其有关更新的推文流中创建新的推文。

The following code contains the Twitter library for this implementation.

以下代码包含此实现的Twitter库。

<?php
class TwitterService
{
    public function authenticate($username) {}
    public function deauthenticate($username) {}

    public function tweet($message,$user) {
        // Update  wall with new tweet
    }

    public function getUpdates() {
        // Return Updates
    }

    public function getFollowers() {
        // Return followers
    }
}

There is no way we can make TwitterService compatible with a target interface or client with its original implementation. But we can see that logic of the class is similar to EmailSubscription. Therefore, we can effectively use an adapter class in this situation to make TwitterService compatible with the client without changing client code.

我们无法通过原始实现使TwitterService与目标接口或客户端兼容。 但是我们可以看到该类的逻辑类似于EmailSubscription 。 因此,在这种情况下,我们可以有效地使用适配器类,以使TwitterService与客户端兼容,而无需更改客户端代码。

Let’s look at the implementation of the TwitterAdapter class.

让我们看一下TwitterAdapter类的实现。

<?php
class TwitterAdapter implements EmailSubscribe
{
    public function subscribe($username) { }
    public function unsubscribe($username) { }

    public function sendUpdates() {
        $tw_service = new TwitterService();
        $updates = $tw_service->getUpdates();
        $subscribers = $tw_service->getFollowers();
        $tw_service->tweet($updates,$subscribers);
    }
}

$twitter_subscribe = new TwitterAdapter();
$twitter_subscribe->sendUpdates();

The TwitterAdapter class implements our target interface with original email subscription related functionalities. Internally it creates an object of TwitterService and makes the tweet function compatible with sendUpdates() by calling the necessary functions and returning the output expected by the client.

TwitterAdapter类通过与原始电子邮件订阅相关的功能来实现我们的目标接口。 在内部,它通过调用必要的函数并返回客户端期望的输出,来创建TwitterService的对象,并使tweet函数与sendUpdates()兼容。

The initialization code seems similar to the previous code. Therefore, the client class doesn’t know that Twitter service sends a tweet on updates instead of an email. The client class keeps calling the sendUpdate() method for all the services and the respective updating techniques will be executed through adapters.

初始化代码似乎与之前的代码相似。 因此,客户端类不知道Twitter服务发送有关更新的推文,而不是电子邮件。 客户端类继续为所有服务调用sendUpdate()方法,并且相应的更新技术将通过适配器执行。

摘要 (Summary)

Throughout this article we’ve looked at the Adapter pattern and tried to to understand the effective uses of it through some practical examples. We learned that there are both good and bad uses of the Adapter pattern, and now it’s up to you to decide when to go with adapters.

在整个本文中,我们研究了Adapter模式,并试图通过一些实际示例来了解它的有效用法。 我们了解到,适配器模式有好有坏,现在由您决定何时使用适配器。

Let me know about the practical scenarios which you faced in application development and how you provided a solution through adapters in the comments below.

让我知道您在应用程序开发中遇到的实际情况,以及如何在下面的注释中通过适配器提供解决方案。

Image via Fotolia

图片来自Fotolia

翻译自: https://www.sitepoint.com/practical-aspects-of-the-adapter-pattern/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值