【案例】
黑枣玩具公司专门生产玩具,生产的玩具不限于狗、猫、狮子,鱼等动物。每个玩具都可以进行“张嘴”与“闭嘴”操作,分别调用了openMouth与closeMouth方法。
<代码实现>
abstract class Toy
{
public abstract function openMouth();
public abstract function closeMouth();
}
class Dog extends Toy
{
public function openMouth()
{
echo "Dog open Mouth\n";
}
public function closeMouth()
{
echo "Dog open Mouth\n";
}
}
class Cat extends Toy
{
public function openMouth()
{
echo "Cat open Mouth\n";
}
public function closeMouth()
{
echo "Cat open Mouth\n";
}
}
<红枣遥控公司来了>
黑枣玩具公司与红枣遥控公司合作,红枣遥控公司可以使用遥控设备对动物进行嘴巴控制。不过红枣遥控公司的遥控设备是调用的动物的doMouthOpen及doMouthClose方法。黑枣玩具公司的程序员现在必须要做的是对Toy系列类进行升级改造,使Toy能调用doMouthOpen及doMouthClose方法.
<实现分析>
这不是相当简单么,为Toy再增加doMouthOpen及doMouthClose方法,修改Dog类、Cat类等并实现doMouthOpen及doMouthClose方法就可以了。虽然有点小繁琐,工程量还不大。
<程序猿的代码实现红枣遥控公司需求>
abstract class Toy
{
public abstract function openMouth();
public abstract function closeMouth();
//为红枣遥控公司控制接口增加doMouthOpen方法
public abstract function doMouthOpen();
//为红枣遥控公司控制接口增加doMouthClose方法
public abstract function doMouthClose();
}
class Dog extends Toy
{
public function openMouth()
{
echo "Dog open Mouth\n";
}
public function closeMouth()
{
echo "Dog open Mouth\n";
}
//增加的方法
public function doMouthOpen()
{
$this->doMouthOpen();
}
//增加的方法
public function doMouthClose()
{
$this->closeMouth();
}
}
class Cat extends Toy
{
public function openMouth()
{
echo "Cat open Mouth\n";
}
public function closeMouth()
{
echo "Cat open Mouth\n";
}
//增加的方法
public function doMouthOpen()
{
$this->doMouthOpen();
}
//增加的方法
public function doMouthClose()
{
$this->closeMouth();
}
}
<绿枣遥控公司也来了>
黑枣玩具公司也要与绿枣遥控公司合作,因为绿枣遥控公司遥控设备更便宜稳定。不过绿枣遥控公司的遥控设备是调用的动物的operMouth($type)方法来实现嘴巴控制。如果$type为0则“闭嘴”,反之张嘴
黑枣玩具公司的程序员现在必须又要对Toy系列类进行升级改造,使Toy能调用operMouth()方法.
<程序猿的代码实现绿枣遥控公司需求>
abstract class Toy
{
public abstract function openMouth();
public abstract function closeMouth();
public abstract function doMouthOpen();
public abstract function doMouthClose();
//为绿枣遥控公司控制接口增加doMouthClose方法
public abstract function operateMouth($type = 0);
}
class Dog extends Toy
{
public function openMouth()
{
echo "Dog open Mouth\n";
}
public function closeMouth()
{
echo "Dog open Mouth\n";
}
public function doMouthOpen()
{
$this->doMouthOpen();
}
public function doMouthClose()
{
$this->closeMouth();
}
public function operateMouth($type = 0)
{
if ($type == 0) {
$this->closeMouth();
} else {
$this->operateMouth();
}
}
}
class Cat extends Toy
{
public function openMouth()
{
echo "Cat open Mouth\n";
}
public function closeMouth()
{
echo "Cat open Mouth\n";
}
public function doMouthOpen()
{
$this->doMouthOpen();
}
public function doMouthClose()
{
$this->closeMouth();
}
public function operateMouth($type = 0)
{
if ($type == 0) {
$this->closeMouth();
} else {
$this->operateMouth();
}
}
}
【分析OOA】
我们可以开发,Toy系列类是越来也庞大。紫枣青枣黄枣山枣这些遥控公司全来的时候,总有一天系统会崩溃。
问题出在哪里。代码实现违反了“开-闭”原则,一个软件实体应当对扩展开放,对修改关闭。即在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展。
重新设计代码,使用适配器模式来实现。
适配器模式核心思想:把对某些相似的类的操作转化为一个统一的“接口”(这里是比喻的说话)--适配器,或者比喻为一个“界面”,统一或屏蔽了那些类的细节。适配器模式还构造了一种“机制”,使“适配”的类可以很容易的增减,而不用修改与适配器交互的代码,符合“减少代码间耦合”的设计原则。
【设计OOD】
<说明>
1. 目标(Target)角色:定义客户端使用的与特定领域相关的接口,这也就是我们所期待得到的
这里的RedTarget及GreenTarget分别为红枣公司及绿枣公司要求实现的相关接口。我们可以看到RedTarget必须实现doMouthOpen跟doMouthClose两个方法,GreenTarget必须实现opertateMouth一个方法就行了
2. 源(Adaptee)角色:需要进行适配的接口
这里的源角色是黑枣公司的Toy实例
3. 适配器(Adapter)角色:对Adaptee的接口与Target接口进行适配;适配器是本模式的核心,适配器把源接口转换成目标接口,此角色为具体类
RedAdapter及GreenAdapter分别实现了RedTarget及GreenTarget接口,创建时接收一个Toy实例。
<代码>
1. 源(Adaptee)角色:Toy系列类代码保持不变
abstract class Toy
{
public abstract function openMouth();
public abstract function closeMouth();
}
class Dog extends Toy
{
public function openMouth()
{
echo "Dog open Mouth\n";
}
public function closeMouth()
{
echo "Dog close Mouth\n";
}
}
class Cat extends Toy
{
public function openMouth()
{
echo "Cat open Mouth\n";
}
public function closeMouth()
{
echo "Cat close Mouth\n";
}
}
//目标角色:红枣遥控公司
interface RedTarget
{
public function doMouthOpen();
public function doMouthClose();
}
//目标角色:绿枣遥控公司及
interface GreenTarget
{
public function operateMouth($type = 0);
}
//类适配器角色:红枣遥控公司
class RedAdapter implements RedTarget
{
private $adaptee;
function __construct(Toy $adaptee)
{
$this->adaptee = $adaptee;
}
//委派调用Adaptee的sampleMethod1方法
public function doMouthOpen()
{
$this->adaptee->openMouth();
}
public function doMouthClose()
{
$this->adaptee->closeMouth();
}
}
//类适配器角色:绿枣遥控公司
class GreenAdapter implements GreenTarget
{
private $adaptee;
function __construct(Toy $adaptee)
{
$this->adaptee = $adaptee;
}
//委派调用Adaptee:GreenTarget的operateMouth方法
public function operateMouth($type = 0)
{
if ($type) {
$this->adaptee->openMouth();
} else {
$this->adaptee->closeMouth();
}
}
}
测试用例Test Case】
<代码>
class testDriver
{
public function run()
{
//实例化一只狗玩具
$adaptee_dog = new Dog();
echo "给狗套上红枣适配器\n";
$adapter_red = new RedAdapter($adaptee_dog);
//张嘴
$adapter_red->doMouthOpen();
//闭嘴
$adapter_red->doMouthClose();
echo "给狗套上绿枣适配器\n";
$adapter_green = new GreenAdapter($adaptee_dog);
//张嘴
$adapter_green->operateMouth(1);
//闭嘴
$adapter_green->operateMouth(0);
}
}
$test = new testDriver();
$test->run();
<输出】
【小结】
【适配器模式流程】:
1. 客户通过目标接口调用适配器的方法对适配器发出请求
2. 适配器使用被适配者接口把请求转换成被适配者一个或多个调用接口
3. 客户接收到调用的结果,但并未察觉这一切是适配器在起转换作用。
【常用情境】:
1.系统需要使用现有的类,而此类的接口不符合系统的需要。
2.想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。这些源类不一定有很复杂的接口。
3.(对对象适配器而言)在设计里,需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。
Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。
Adapter模式有对象适配器和类适配器两种形式的实现结构,但是类适配器采用“多继承”的实现方式,带来了不良的高耦合,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神。
********************************************
* 作者:叶文涛
* 标题:Php设计模式之【适配器模式 Adapter Pattern】
* 参考:
*《设计模式:可复用面向对象软件基础 》(美)Erich Gamma 等著
*《Head First设计模式》Eric Freeman等著
*《PHP设计模式》Aaron Saray等著,梁志敏等译(PS:翻译的是狗屁水平)
******************转载请注明网址来源 ***************