PHP设计模式之适配器模式定义与用法详解

本文实例讲述了PHP设计模式之适配器模式定义与用法。分享给大家供大家参考,具体如下:

适配器很容易理解, 大多数人家庭都有手机转接器, 用来为移动电话充电,这就是一种适配器. 如果只有USB接头, 就无法将移动电话插到标准插座上. 实际上, 必须使用一个适配器, 一端接USB插头, 一端接插座. 当然, 你可以拿出电气工具,改装USB连接头, 或者重新安装插座, 不过这样会带来很多额外的工作, 而且可能会把连接头或插座弄坏. 所以, 最可取的方法就是找一个适配器. 软件开发也是如此.

类适配器模式(使用继承)

类适配器模式很简单, 不过与对象适配器模式相比, 类适配器模式的灵活性弱些, 类适配器简单的原因在于 , 适配器(Adapter)会从被适配者(Adaptee)继承功能, 所以适配模式中需要编写的代码比较少.

由于类适配器模式包含双重继承, 但是PHP并不支持双重继承, 不过幸运的是,PHP可以用接口来模拟双重继承, 下面是一个正确的结构, 不仅继承了一个类, 同时还继承了一个接口

class ChildClass extends ParentClass implements ISomeAdapter
{
}

实现类适配器模式时, 参与者必须包括一个PHP接口

下面以一个货币兑换为例来演示:

假设有一个企业网站在同时销售软件服务和软件产品, 目前, 所有交易都在美国进行, 所以完全可以用美元来完成所有计算.现在开发人员希望能有一个转换器能处理美元和欧元的兑换, 而不改变原来按美元交易额的类.通过增加一个适配器, 现在程序即可以用美元计算也可以用欧元计算.

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,实际上总金额无需再乖以兑换率, 不过如果要为客户提供折扣或者要增加额外服务或产品的附加费, $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类中. 不过,因为客户的所有数据都是按美元计算的.换句话说, 如果不重新开发整个程序, 就无法在系统中"插入"这个欧元计算. 但是你不想这么做. 为了加入EuroCalc, 你需要一个适配器: 就像找一个适配器来适应欧洲的插座一样, 可以创建一个适配器, 使你的系统能够使用欧元. 幸运的是, 类适配器正是为这样的情况设计的.首先需要创建一个接口. 在这个类图中, 这个接口名为ITarget. 它只有一个方法requester(). 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;
 }
}

类适配模式中, 一个具体类会继承另一个具体类, 有这种结构的设计模式很少见, 大多数设计模式中, 几乎都是继承一个抽象类, 并由类根据需要实现其抽象方法和属性. 换句话说, 一般谈到继承时, 都是具体类继承抽象类.

由于既实现了一个接口又扩展了一个类, 所以EuroAdapter类同时拥有该接口和具体类的接口. 通过使用requester()方法, EuroAdapter类可以设置rate值(兑换率), 从而能使用被适配者的功能, 而元而做任何改变.

下面定义一个Client类, 从EuroAdapter和DollarCalc类发出请求. 可以看到,原来的DollarCalc仍能很好地工作, 不过它没有ITarget接口.

Client.php

<?php
include_once('EuroAdapter.php');
include_once('DollarCalc.php');
class Client
{
 public function __construct()
 {
  $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

可以看到,美元和欧元都可以处理, 这就是适配器模式的方便之处.

这个计算很简单, 如果是针对更为复杂的计算, 继承要提供建立类适配器的Target接口的必要接口和具体实现

使用组合的适配器模式

对象适配器模式使用组合而不是继承, 不过它也会完成同样的目标. 通过比较这两个版本的适配器模式, 可以看出它们各自的优缺点. 采用类适配器模式时,适配器可以继承它需要的大多数功能, 只是通过接口稍微调. 在对象适配器模式中 适配器(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面向对象程序设计入门教程》、《PHP基本语法入门教程》、《PHP数组(Array)操作技巧大全》、《php字符串(string)用法总结》、《php+mysql数据库操作入门教程》及《php常见数据库操作技巧汇总》

希望本文所述对大家PHP程序设计有所帮助。

您可能感兴趣的文章:

  • php设计模式之简单工厂模式详解
  • PHP最常用的2种设计模式工厂模式和单例模式介绍
  • php设计模式 Factory(工厂模式)
  • php基础设计模式大全(注册树模式、工厂模式、单列模式)
  • 基于php设计模式中工厂模式详细介绍
  • PHP设计模式之工厂模式与单例模式
  • PHP实现设计模式中的抽象工厂模式详解
  • PHP设计模式之原型模式定义与用法详解
  • PHP设计模式之装饰器模式定义与用法详解
  • PHP设计模式之状态模式定义与用法详解
  • PHP设计模式之工厂模式定义与用法详解
时间: 2018-04-01

PHP设计模式之状态模式定义与用法详解

本文实例讲述了PHP设计模式之状态模式定义与用法.分享给大家供大家参考,具体如下: 什么是状态设计模式 当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化. 什么时候使用状态模式 对象中频繁改变非常依赖于条件语句. 就其自身来说, 条件语句本身没有什么问题(如switch语句或带else子句的语句),不过, 如果选项太多, 以到程序开

php基础设计模式大全(注册树模式、工厂模式、单列模式)

废话不多说了,先给大家介绍注册树模式然后介绍工厂模式最后给大家介绍单列模式,本文写的很详细,一起来学习吧. php注册树模式 什么是注册树模式? 注册树模式当然也叫注册模式,注册器模式.之所以我在这里矫情一下它的名称,是因为我感觉注册树这个名称更容易让人理解.像前两篇一样,我们这篇依旧是从名字入手.注册树模式通过将对象实例注册到一棵全局的对象树上,需要的时候从对象树上采摘的模式设计方法.   这让我想起了小时候买糖葫芦,卖糖葫芦的将糖葫芦插在一个大的杆子上,人们买的时候就取下来.不同的是,注册树

php设计模式 Factory(工厂模式)

复制代码 代码如下: <?php /** * 工厂方法模式 * * 定义一个用于创建对象的接口,让子类决定将哪一个类实例化,使用一个类的实例化延迟到其子类 */ /* class DBFactory { public static function create($type) { swtich($type) { case "Mysql": return new MysqlDB(); break; case "Postgre": return new Postg

PHP设计模式之工厂模式与单例模式

本文实例讲述了PHP设计模式之工厂模式与单例模式实现方法.分享给大家供大家参考,具体如下: 设计模式简单说应对某类问题而设计的解决方式 工厂模式:应对需求创建相应的对象 class factory{ function __construct($name){ if(file_exists('./'.$name.'.class.php')){ return new $name; }else{ die('not exist'); } } } 单例模式:只创建一个对象的实例,不允许再创建实例,节约资源(

php设计模式之简单工厂模式详解

本文以实例形式较为详细的介绍了PHP设计模式的简单工厂模式,对于进行PHP程序设计来说有很好的借鉴作用.具体如下: 一.概念 简单工厂模式 [静态工厂方法模式](Static Factory Method) 是类的创建模式 工厂模式的几种形态: 1.简单工厂模式(Simple Factory)又叫做 静态工厂方法模式(Static Factory Method) 2.工厂方法模式(Factory Method)又叫做 多态性工厂模式(Polymorphic Factory) 3.抽象工厂模式(A

PHP设计模式之装饰器模式定义与用法详解

本文实例讲述了PHP设计模式之装饰器模式定义与用法.分享给大家供大家参考,具体如下: 什么是装饰器模式 作为一种结构型模式, 装饰器(Decorator)模式就是对一个已有结构增加"装饰". 适配器模式, 是为现在有结构增加的是一个适配器类,.将一个类的接口,转换成客户期望的另外一个接口.适配器让原本接口不兼容的类可以很好的合作. 装饰器模式是将一个对象包装起来以增强新的行为和责任.装饰器也称为包装器(类似于适配器) 有些设计设计模式包含一个抽象类,而且该抽象类还继承了另一个抽象类,这

PHP设计模式之工厂模式定义与用法详解

本文实例讲述了PHP设计模式之工厂模式定义与用法.分享给大家供大家参考,具体如下: 工厂模式(Factory Design Pattern)作为一种创建型设计模式, 遵循了开放-封闭原则, 对修改封闭, 对扩展开放. 工厂方法(Factory Method)模式就是要创建"某种东西". 对于工厂方法模式, 要创建的"东西"是一个产品,这个产品与创建它的类之间不存在绑定.实际上,为了保持这种松耦合,客户会通过一个工厂发出请求. 再由工厂创建所请求的产品.也可以换种方式

PHP实现设计模式中的抽象工厂模式详解

抽象工厂模式(Abstact Factory)是一种常见的软件设计模式.该模式为一个产品族提供了统一的创建接口.当需要这个产品族的某一系列的时候,可以为此系列的产品族创建一个 具体的工厂类. [意图] 抽象工厂模式提供一个创建一系统相关或相互依赖对象的接口,而无需指定它们具体的类[GOF95] [抽象工厂模式结构图] [抽象工厂模式中主要角色] 抽象工厂(Abstract Factory)角色:它声明一个创建抽象产品对象的接口.通常以接口或抽象类实现,所有的具体工厂类必须实现这个接口或继承这个类

PHP设计模式之原型模式定义与用法详解

本文实例讲述了PHP设计模式之原型模式定义与用法.分享给大家供大家参考,具体如下: 原型设计模式(Prototype Design Pattern)很有意思, 因为它使用了一种克隆技术来复制实例化的对象. 新对象是通过复制原型实例来创建的. 在这里, 实例是批实例化的具体类.原型设计模式的目的是通过使用克隆来减少实例化对象的开销.与其从一个类实例化新对象, 完全可以使用一个已有实例的克隆. 克隆函数 PHP中使用原型设计模式的关键是要了解如何使用内置函数__clone(). <?php abst

基于php设计模式中工厂模式详细介绍

工厂模式:由工厂类根据参数来决定创建出哪一种产片类的实例工厂类:一个专门用来创建其他对象的方法类.即按需分配,传入参数进行选择,返回具体的类作用:对象创建的封装.简化创建对象的操作,即调用工厂类的一个方法来得到需要的类补充:1.主要角色:抽象产品(Product).具体产品(Concrete Product).抽象工厂角色(Creator)2.优缺点    优点:工厂方法模式可以允许系统在不修改工厂角色的情况下引进心产品    缺点:客户可能仅仅为了创建一个特定的Concrete Product

PHP最常用的2种设计模式工厂模式和单例模式介绍

1.工厂模式 主要作用是降低耦合度. 复制代码 代码如下: abstract class Operation{ abstract public function getValue($num1,$num2); public function getAttr(){ return 1; } } class Add extends Operation{ public function getValue($num1, $num2){ return $num1+$num2; } } class Sub ex

PHP中常用的三种设计模式详解【单例模式、工厂模式、观察者模式】

本文实例讲述了PHP中常用的三种设计模式.分享给大家供大家参考,具体如下: PHP中常用的三种设计模式:单例模式.工厂模式.观察者模式 1.单例模式 为何要使用PHP单例模式? 多数人都是从单例模式的字面上的意思来理解它的用途, 认为这是对系统资源的节省, 可以避免重复实例化, 是一种"计划生育". 而PHP每次执行完页面都是会从内存中清理掉所有的资源. 因而PHP中的单例实际每次运行都是需要重新实例化的, 这样就失去了单例重复实例化的意义了. 单单从这个方面来说, PHP的单例的确有

PHP常用的三种设计模式汇总

本篇文章是学习PHP中常用的三种设计模式的笔记及总结,不管采用哪一门语言开发什么,几乎都会使用到设计模式,我们为什么需要设计模式呢?它的诞生对于我们开发人员来说有什么样的作用与意义呢? 相信做iOS开发的人员对设计模式也会挺熟悉吧?比如单例设计模式.工厂设计模式.观察者模式.MVC框架结构设计模式等. 接下来我们一起来学习PHP中最常用的三种设计模式:单例设计模式.工厂设计模式和观察者设计模式. 单例设计模式 所谓单例模式,即在应用程序中最多只有该类的一个实例存在,一旦创建,就会一直存在于内存中

js面向对象之常见创建对象的几种方式(工厂模式、构造函数模式、原型模式)

在上篇文章给大家介绍了javascript面向对象基础,本篇文章继续深入学习javascript面向对象,JS的语法非常灵活,简单的对象创建就有好几种不同的方法.这些过于灵活的地方有时候确实很让人迷惑,那么今天我们就来梳理一下JS中常用的创建对象的几种方法吧. 前言 虽然使用 Object构造函数 或者使用 对象字面量 可以很方便的用来创建一个对象,但这种方式有一个明显的缺点:使用一个接口创建多个对象会产生很多冗余的代码.因此为了解决这个问题,人们开始使用以下几种方式来常见对象. 工厂模式 该模

javascript设计模式 – 工厂模式原理与应用实例分析

本文实例讲述了javascript设计模式 – 工厂模式原理与应用.分享给大家供大家参考,具体如下: 介绍:前面我们介绍了简单工厂模式,简单工厂模式存在一个严重的问题:当需要扩展时必定要修改工厂类的源代码.我们虽然在第二个demo中做了一些优化,但是我们需在使用时明确指定执行方法的名字,这无疑提高了使用成本.那如何实现增加新产品而不影响已有代码?工厂模式应运而生. 定义:定义一个用于创建对象的接口,让子类决定将哪一个类实例化.工厂模式让一个类的实例化延迟到其子类.工厂模式又称为工厂方法模式,又可

PHP工厂模式、单例模式与注册树模式实例详解

本文实例讲述了PHP工厂模式.单例模式与注册树模式.分享给大家供大家参考,具体如下: 三种基本设计模式 1.工厂模式:工厂方法或者类生成对象,而不是在代码中直接new 2.单例模式:使某个类的对象仅允许创建一个 3.注册模式:全局共享和交换对象 工厂模式: <?php namespace IMooc; class Factory { static function createDatabase() { $db = new Database(); return $db; } } $db = IMo

最常用的12种设计模式小结

1.策略模式(Strategy): 定义了算法家族, 分别封装起来, 让它们之间可以互相替换. 比如Collections.sort(List list, Comparator c); 可以通过实现多个Comparator接口来达到多种排序的目的. 2.装饰着模式(Decorator): 动态的给一个对象添加一些额外的职责. 比如java.io包. BufferedInputStream封装了FileInputStream, 它们都实现了InputStream接口, 但前者实现了readLine

JavaScript 模式之工厂模式(Factory)应用介绍

工厂模式也是对象创建模式之一,它通常在类或类的静态方法中去实现.构造对象的一种方式是使用new操作符,但使用new时正是针对实现编程,会造成"耦合"问题,与具体的类关系紧密.导致代码更脆弱,缺乏弹性,在复杂逻辑的项目中建议是面向接口编程. 先看简单工厂模式 复制代码 代码如下: Person(name, age) { var obj = {} obj.name = name obj.age = age return obj } var p1 = Person('jack', 25) v

PHP常用的三种设计模式

一.首先来看,单例模式 所谓单例模式,即在应用程序中只会有这个类的一个实例存在. 通常单例模式用在仅允许数据库访问对象的实例中,从而防止打开多个数据库连接. 一个单例类应包括以下几点: 和普通类不同,单例类不能被直接实例化,只能是由自身实例化.因此,要获得这样的限制效果,构造函数必须标记为private. 要让单例类不被直接实例化而能起到作用,就必须为其提供这样的一个实例.因此,就必须要让单例类拥有一个能保存类的实例的私有静态成员变量和对应的一个能访问到实例的公共静态方法. 在PHP中,为防止对