デザインパターン: アダプターパターン

WBOY
リリース: 2016-07-30 13:29:25
オリジナル
816 人が閲覧しました

アダプターは簡単に理解できます。ほとんどの人は携帯電話を充電するために使用します。USB コネクターしかない場合、携帯電話は標準のソケットに接続できません。もちろん、電動工具を取り出して USB コネクタを変更したり、ソケットを再取り付けしたりすることもできますが、これにより多くのトラブルが発生します。余分な作業が必要になり、コネクタまたはソケットが壊れる可能性があります。したがって、最も望ましい方法は、ソフトウェア開発にも当てはまります。

クラス アダプター パターン。クラスアダプタパターンは非常にシンプルですが、オブジェクトアダプタパターンに比べてパターンの自由度が低いのは、アダプタ(Adapter)がアダプティ(Adapte)から機能を継承するためです。アダプテーション パターンに記述するコードは少なくなります クラス アダプター パターンには 2 つの継承が含まれているため、PHP は二重継承をサポートしていませんが、幸いなことに、PHP はインターフェイスを使用して二重継承をシミュレートできます。これはクラスを継承するだけでなく、インターフェイスも継承します

class ChildClass extends ParentClass implements ISomeAdapter
{
    
}
ログイン後にコピー

クラスアダプターパターンを実装する場合、参加者はPHPインターフェイスを含める必要があります

以下は、デモンストレーションする通貨交換の例です:

ソフトウェア サービスとソフトウェア製品を同時に販売する企業 Web サイトがあるとします。現在、すべての取引は米国内で行われるため、開発者はすべての計算を米ドルで行うことができると考えています。アダプターを追加することで、ドルとユーロの変換をドル単位またはユーロ単位で計算できるようになりました。このクラスには $rate 属性があり、requestTotal() メソッドは $rate を使用してトランザクションの金額を計算していることがわかります。このバージョンでは、この値は 1 に設定されています。実際、合計金額は 1 に設定されています。為替レートに調整する必要はありませんが、顧客に割引を提供したり、追加のサービスや製品に追加料金を追加したりする場合、このクラスはモデルの一部には適していませんが、これは便利です。

ニーズが変わりました

今度は、顧客の会社がヨーロッパで開発する予定であるため、同じ計算をユーロで実行できるアプリケーションを開発する必要があります。このユーロ計算を DollarCalc のようにしたいと考えています。同様に、必要なのは変数名を変更することだけです。

EuroCalc.php

<?php
class DollarCalc
{
    private $dollar;
    private $product;
    private $service;
    public $rate = 1;
    public function requestCalc($productNow, $serviceNow)
    {
        $this->product = $productNow;
        $this->service = $serviceNow;
        $this->dollar = $this->product + $this->service;
        return $this->requestTotal();
    }
    public function requestTotal()
    {
        $this->dollar *= $this->rate;
        return $this->dollar;
    }
}
ログイン後にコピー

次に、アプリケーションの残りの部分を EuroCalc クラスに接続します。ただし、すべての顧客データは USD に変換されます。つまり、プログラム全体を再開発せずに、この Eurocalculation をシステムに「挿入」する方法はありません。しかし、EuroCalc を組み込むには、アダプターが必要です。ちょうど、適合するアダプターを見つけるのと同じです。ヨーロッパのソケットでは、システムがユーロを使用できるようにするアダプターを作成できます。幸いなことに、クラス アダプターはこのような状況向けに設計されており、このクラス図では、インターフェイスは ITarget のみを作成する必要があります。 1 つのメソッド requester()。requester() は抽象メソッドであり、このメソッドを実装するのはインターフェイスの具体的な実装次第です。

ITarget.php

<?php
class EuroCalc
{
    private $euro;
    private $product;
    private $service;
    public $rate = 1;
    public function requestCalc($productNow, $serviceNow)
    {
        $this->product = $productNow;
        $this->service = $serviceNow;
        $this->euro = $this->product + $this->service;
        return $this->requestTotal();
    }
    public function requestTotal()
    {
        $this->euro *= $this->rate;
        return $this->euro;
    }
}
ログイン後にコピー

これで、開発者はリクエスターを実装できます。

継承を使用するアダプター設計パターンでは、アダプター (アダプター) は ITarget インターフェースの実装だけでなく、具象クラス EuroCalc の実装にも参加します。 EuroAdapter の作成にはそれほど多くの作業は必要ありません。ほとんどの作業は EuroCal クラスで完了しているため、ここで行う必要があるのは、ドル値をユーロ値に変換できるように request() メソッドを実装することです。

EuroAdapter.php

<?php
interface ITarget
{
    public function requester();
}
ログイン後にコピー

クラス適応パターンでは、具象クラスが別の具象クラスを継承します。この構造を持つデザイン パターンはほとんどありません。ほとんどのデザイン パターンでは、抽象クラスがほぼ継承され、クラスは必要に応じてその抽象メソッドとプロパティを実装します。 . つまり、継承に関しては、一般的には抽象クラスを継承します。

インターフェイスの実装とクラスの拡張の両方を行うため、EuroAdapter クラスはインターフェイスと具象クラスのインターフェイスの両方を持ちます。 . requester() メソッドを使用することで、EuroAdapter クラスはレート値 (為替レート) を設定できるため、変更を加えずに適応されたオブジェクトの機能を使用できます

EuroAdapter からリクエストを行うために以下の Client クラスを定義します。 DollarCalc クラス。ご覧のとおり、元の DollarCalc は引き続き正常に動作しますが、

Client.php

<?php
include_once('EuroCalc.php');
include_once('ITarget.php');
class EuroAdapter extends EuroCalc implements ITarget
{
    public function __construct()
    {
        $this->requester();
    }
    public function requester()
    {
        $this->rate = 0.8111;
        return $this->rate;
    }
}
ログイン後にコピー

実行結果は次のとおりです。

ご覧のとおり、米ドルとユーロの両方を処理できます。これはアダプター モードの便利な点です。

この計算は非常に簡単です。より複雑な計算の場合は、継承によって必要なインターフェイスと特定の実装が提供されるはずです。クラスアダプターのターゲットインターフェースの

結合されたアダプターパターンを使用します

      对象适配器模式使用组合而不是继承, 不过它也会完成同样的目标. 通过比较这两个版本的适配器模式, 可以看出它们各自的优缺点. 采用类适配器模式时,适配器可以继承它需要的大多数功能, 只是通过接口稍微调. 在对象适配器模式中 适配器(Adapter)参与使用被适配者(Adaptee), 并实现Target接口. 在类适配器模式中, 适配器(Adapter)则是一个被适配者(Adaptee), 并实现Target接口.

示例: 从桌面环境转向移动环境

PHP程序员经常会遇到这样一个问题:需要适应移动环境而做出调整.不久之前,你可能只需要考虑提供一个网站来适应多种不同的桌面环境. 大多数桌面都使用一个布局, 再由设计人员让它更美观. 对于移动设备, 设计人员和开发人员不仅需要重新考虑桌面和移动环境中页面显示的设计元素, 还要考虑如何从一个环境切换到另一个环境.

首先来看桌面端的类Desktop(它将需要一个适配器). 这个类使用了一个简单但很宽松的接口:

IFormat.php

<?php
interface IFormat
{
    public function formatCSS();
    public function formatGraphics();
    public function horizontalLayout();
}
ログイン後にコピー

它支持css和图片选择, 不过其中一个方法指示一种水平布局, 我们知道这种布局并不适用小的移动设备.下面给出实现这个接口的Desktop类

Desktop.php

<?php
include_once('IFormat.php');
class Desktop implements IFormat
{
    public function formatCSS()
    {
        echo "引用desktop.css<br />";
    }
    public function formatGraphics()
    {
        echo "引用desktop.png图片<br />";
    }
    public function horizontalLayout()
    {
        echo '桌面:水平布局';
    }
}
ログイン後にコピー

问题来了, 这个布局对于小的移动设备来说太宽了. 所以我们的目标是仍采用同样的内容, 但调整为一种移动设计.

下面来看移动端的类Mobile

首先移动端有一个移动端的接口

IMobileFormat

<?php
interface IMobileFormat
{
    public function formatCSS();
    public function formatGraphics();
    public function verticalLayout();
}
ログイン後にコピー

可以看到, IMobileFormat接口和IFormat接口是不一样的,也就是不兼容的, 一个包含了方法horizontalLayout(), 另一个包含方法verticalLaout(), 它们的差别很小, 最主要的区别是: 桌面设计可以采用水平的多栏布局, 而移动设计要使用垂直布局,而适配器就是要解决这个问题

下面给出一个实现了IMoibleFormat接口的Mobile类

Mobile.php

<?php
include_once('IMobileFormat.php');
class Mobile implements IMobileFormat
{
    public function formatCSS()
    {
        echo "引用mobile.css<br />";
    }
    public function formatGraphics()
    {
        echo "引用mobile.png图片<br />";
    }
    public function verticalLayout()
    {
        echo '移动端:垂直布局';
    }
}
ログイン後にコピー

Mobile类和Desktop类非常相似, 不过是图片和CSS引用不同

接下来,我们需要一个适配器,将Desktop和Mobile类结合在一起

MobileAdapter.php

<?php
include_once('IFormat.php');
include_once('Mobile.php');
class MobileAdapter implements IFormat
{
    private $mobile;
    public function __construct(IMobileFormat $mobileNow)
    {
        $this->mobile = $mobileNow;
    }
    public function formatCSS()
    {
        $this->mobile->formatCSS();
    }
    public function formatGraphics()
    {
        $this->mobile->formatGraphics();
    }
    public function horizontalLayout()
    {
        $this->mobile->verticalLayout();
    }
}
ログイン後にコピー

可以看到,MobileAdapter实例化时要提供一个Mobile对象实例.还要注意 ,类型提示中使用了IMobileFormat, 确保参数是一个Mobile对象.有意思的是, Adapter参与者通过实现horizontalLayout()方法来包含verticalLayout()方法.实际上, 所有MobileAdapter方法都包装了一个Mobile方法.碰巧的是, 适配器参与者中的一个方法并不在适配器接口中(verticalLayout());它们可能完全不同, 适配器只是把它们包装在适配器接口(IFormat)的某一方法中.

客户调用(Client)

Client.php

<?php
include_once('Mobile.php');
include_once('MobileAdapter.php');
class Client
{
    private $mobile;
    private $mobileAdapter;
    public function __construct()
    {
        $this->mobile = new Mobile();
        $this->mobileAdapter = new MobileAdapter($this->mobile);
        $this->mobileAdapter->formatCSS();
        $this->mobileAdapter->formatGraphics();
        $this->mobileAdapter->horizontalLayout();
    }
}
$worker = new Client();
ログイン後にコピー

适配器模式中的Client类必须包装Adaptee(Mobile)的一个实例, 以便集成到Adapter本身.实例化Adapter时, Client使用Adatee作为参数来完成Adapter的实例化.所以客户必须首先创建一个Adapter对象(new Mobile()), 然后创建一个Adapter((new MobileAdapter($this->mobile)).

Client类的大多数请求都是通过MobileAdapter发出的. 不过这个代码的最后他使用了Mobile类的实例.

适配器和变化

      PHP程序员要即该面对变化.不同版本的PHP会变化, 可能增加新的功能, 另外还可能取消一些功能.而且随着PHP的大大小小的变化,MySQL也在改变.例如, mysql的扩展包升级为mysqli, PHP开发人员需要相应调整, 要改为使用mysqli中的新API.这里适合采用适配器模式吗?可能不适合.适配器可能适用, 可能不适用,这取决于你的程序如何配置.当然可以重写所有连接和交互代码, 不过这可不是适配器模式的本意, 这就像是重新安装USB连接头, 想把它插进标准的墙上插座一样. 不过, 如果所有原来的mysql代码都在模块中, 你可以修改这个模块(类),换入一个有相同接口的新模块.只是要使用mysqli而不是mysql.我不认为交换等同于适配器, 不过道理是一样的, 在适配器模式中, 原来的代码没有任何改变, 有变化的只是适配器.

如果需要结合使用两个不兼容的接口, 这种情况下, 适配器模式最适用.适配器可以完成接口的"联姻".可以把适配器看作是一个婚姻顾问;通过创建一个公共接口来克服双方的差异.利用 这种设计模式, 可以促成二者的合作,而避免完全重写某一部分.

以上就介绍了设计模式之:适配器模式,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート