【案例】
黑枣玩具公司专门生产玩具,生产的玩具不限于狗、猫、狮子,鱼等动物。每个玩具都可以进行“张嘴”与“闭嘴”操作,分别调用了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();
}
}
后面如果还有其他栆类公司来,增加的方法就会不断的变多。
【分析OOA】
我们可以开发,Toy系列类是越来也庞大。紫枣青枣黄枣山枣这些遥控公司全来的时候,总有一天系统会崩溃。
问题出在哪里。代码实现违反了“开-闭”原则,一个软件实体应当对扩展开放,对修改关闭。即在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展。
重新设计代码,使用适配器模式来实现。
适配器模式核心思想:把对某些相似的类的操作转化为一个统一的“接口”(这里是比喻的说话)--适配器,或者比喻为一个“界面”,统一或屏蔽了那些类的细节。适配器模式还构造了一种“机制”,使“适配”的类可以很容易的增减,而不用修改与适配器交互的代码,符合“减少代码间耦合”的设计原则。
【设计OOD】
<UML>
<说明>
- 目标(Target)角色:定义客户端使用的与特定领域相关的接口,这也就是我们所期待得到的
这里的RedTarget及GreenTarget分别为红枣公司及绿枣公司要求实现的相关接口。我们可以看到RedTarget必须实现doMouthOpen跟doMouthClose两个方法,GreenTarget必须实现opertateMouth一个方法就行了
源(Adaptee)角色:需要进行适配的接口
这里的源角色是黑枣公司的Toy实例
适配器(Adapter)角色:对Adaptee的接口与Target接口进行适配;适配器是本模式的核心,适配器把源接口转换成目标接口,此角色为具体类
RedAdapter及GreenAdapter分别实现了RedTarget及GreenTarget接口,创建时接收一个Toy实例。
<代码>
- 源(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";
}
}
- 目标(Target)角色接口
//目标角色:红枣遥控公司
interface RedTarget
{
public function doMouthOpen();
public function doMouthClose();
}
//目标角色:绿枣遥控公司及
interface GreenTarget
{
public function operateMouth($type = 0);
}
3.适配器角色代码实现
//类适配器角色:红枣遥控公司
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();