> 백엔드 개발 > PHP 튜토리얼 > 디자인 패턴: 어댑터 패턴

디자인 패턴: 어댑터 패턴

WBOY
풀어 주다: 2016-07-30 13:29:25
원래의
828명이 탐색했습니다.

어댑터는 이해하기 쉽습니다. 대부분의 사람들은 휴대폰을 충전하는 데 사용하는 휴대폰 어댑터를 가지고 있습니다. USB 커넥터만 있으면 휴대폰을 표준에 연결할 수 없습니다. 실제로 휴대폰을 표준 소켓에 꽂는 것은 불가능하며, 한쪽 끝은 USB 플러그에 연결하고 다른 쪽 끝은 소켓에 연결한 어댑터를 사용해야 합니다. 전기 도구를 사용하여 USB 커넥터를 수정하거나 소켓을 다시 설치하면 추가 작업이 많이 발생하고 커넥터가 손상될 수 있으므로 가장 바람직한 방법은 어댑터를 찾는 것입니다. 소프트웨어 개발용.

클래스 어댑터 패턴(상속 사용)

클래스 어댑터 패턴은 매우 간단하지만 객체 어댑터 패턴에 비해 , 클래스 어댑터 패턴이 덜 유연합니다. 클래스 어댑터가 단순한 이유는 어댑터(Adapter)가 어댑터(Adaptee)의 기능을 상속하기 때문입니다. 따라서 어댑터 Less 코드는 구성 모드에서 작성해야 합니다. >

클래스 어댑터 모드에는 이중 상속이 포함되어 있으므로 PHP는 이중 상속을 지원하지 않습니다. 다행스럽게도 PHP는 인터페이스를 사용하여 이중 상속을 시뮬레이션할 수 있습니다. 올바른 구조는 클래스를 상속할 뿐만 아니라 인터페이스도 상속합니다. 🎜>

class ChildClass extends ParentClass implements ISomeAdapter
{
    
}
로그인 후 복사
클래스 어댑터 패턴을 구현할 때 참가자는 PHP 인터페이스를 포함해야 합니다

다음은 시연하기 위한 환전 예시입니다.

소프트웨어 서비스와 소프트웨어 제품을 동시에 판매하는 기업 웹사이트가 있다고 가정해 보겠습니다. 현재 모든 거래가 미국에서 이루어지므로 모든 계산이 이루어집니다. 이제 개발자는 미국 달러로 표시된 거래 금액의 원래 클래스를 변경하지 않고 미국 달러와 유로의 변환을 처리할 수 있는 변환기를 원합니다. 이제 프로그램은 달러 또는 유로로 계산할 수 있습니다. euros.

DollarCalc.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;
    }
}
로그인 후 복사
이 클래스를 보면 $rate 속성이 있음을 알 수 있습니다. requestTotal() 메소드는 $rate를 사용합니다. 이번 버전에서는 이 값을 1로 설정했습니다. 실제로 총액을 환율에 따라 계산할 필요는 없지만 고객에게 할인을 제공하거나 추가 서비스를 추가하려는 경우 products 추가 비용을 지불하면 $rate 변수가 유용할 것입니다. 이 클래스는 어댑터 패턴의 일부는 아니지만 시작점입니다.

요구 사항 변경

이제 고객의 회사는 유럽에서 개발하기를 원하므로 동일한 유로 계산을 완료할 수 있는 애플리케이션을 개발해야 합니다. 이 유로 계산을 DollarCalc와 같이 하려면 변수 이름만 변경하면 됩니다.

EuroCalc.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;
    }
}
로그인 후 복사
다음으로 나머지 애플리케이션을 EuroCalc 클래스에 연결합니다. 즉, 모든 고객 데이터가 USD로 되어 있기 때문입니다. , 전체 프로그램을 재개발하지 않으면 이 EuroCalc를 시스템에 "연결"할 수 없습니다. 그러나 EuroCalc를 연결하려면 어댑터가 필요합니다. 유럽 ​​소켓에 맞게 어댑터를 만들면 시스템에서 유로를 사용할 수 있게 됩니다. 다행히도 클래스 어댑터는 이와 같은 상황에 맞게 설계되었습니다. 먼저 이 클래스 다이어그램에서 이 인터페이스를 ITarget이라고 합니다. . 하나의 메소드만 있습니다. requester()는 추상 메소드이며, 이 메소드는 인터페이스의 특정 구현으로 구현되어야 합니다.

ITarget.php

<?php
interface ITarget
{
    public function requester();
}
로그인 후 복사

이제 개발자는 달러 대신 유로를 요청하는 requester() 메서드를 구현할 수 있습니다.

상속을 사용하는 어댑터 디자인 패턴에서 어댑터( Adapter) 참가자는 ITarget 인터페이스를 구현하고 EuroCalc 콘크리트 클래스도 구현합니다. 만들기 EuroAdapter는 대부분의 작업이 이미 EuroCal 클래스에서 완료되었기 때문에 많은 작업을 수행할 필요가 없습니다. 달러 값을 유로 값으로 변환할 수 있도록 request() 메소드를 구현합니다.

EuroAdapter.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;
    }
}
로그인 후 복사
클래스 적응 패턴에서 구체적인 클래스는 또 다른 구체적인 클래스를 상속합니다. 이 구조를 사용하는 디자인 패턴은 거의 없습니다. 대부분의 디자인 패턴에서는 거의 모두 추상 클래스를 상속하며 클래스는 필요에 따라 추상 메서드와 속성을 구현합니다. 구상 클래스는 추상 클래스를 상속합니다.

인터페이스를 구현하고 A 클래스를 확장하므로 EuroAdapter 클래스는 requester() 메서드를 사용하여 이 인터페이스와 콘크리트 클래스의 인터페이스를 모두 갖습니다. EuroAdapter 클래스는 환율 값(환율)을 설정할 수 있으므로 적응된 기능을 사용하고 아무 작업도 수행할 수 없습니다.

EuroAdapter 및 DollarCalc 클래스에서 요청하려면 아래 클라이언트 클래스를 정의하세요. 보시다시피 원래 DollarCalc는 여전히 잘 작동하지만 ITarget 인터페이스가 없습니다.

Client.php

<?php
include_once('EuroAdapter.php');
include_once('DollarCalc.php');
class Client
{
    public function __construct()
    {
        $euro = '&euro;';
        echo "区元: $euro" . $this->makeApapterRequest(new EuroAdapter()) . '<br />';
        echo "美元: $: " . $this->makeDollarRequest(new DollarCalc()) . '<br />';
    }
    private function makeApapterRequest(ITarget $req)
    {
        return $req->requestCalc(40,50);
    }
    private function makeDollarRequest(DollarCalc $req)
    {
        return $req->requestCalc(40,50);
    }
}
$woker = new Client();
로그인 후 복사
실행 결과는 다음과 같습니다.

Euros: €72.999
Dollars: $: 90
로그인 후 복사
달러와 유로를 처리할 수 있는 것을 볼 수 있는데 이것이 어댑터 패턴의 편리성입니다.

이 계산은 매우 간단합니다. 더 복잡한 계산의 경우 상속은 클래스 어댑터의 대상 인터페이스와 특정 구현을 설정하는 데 필요한 인터페이스를 제공해야 합니다

결합된 어댑터 패턴 사용

      对象适配器模式使用组合而不是继承, 不过它也会完成同样的目标. 通过比较这两个版本的适配器模式, 可以看出它们各自的优缺点. 采用类适配器模式时,适配器可以继承它需要的大多数功能, 只是通过接口稍微调. 在对象适配器模式中 适配器(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으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿