在这个有没有对象都要高呼“面向对象”的年代,掌握面向对象会给我们带来意想不到的方便。学编程的小伙伴从开始能写几行代码实现简单功能到后来懂得将一些重复的操作组合起来形成一个“函数”,再到后来将“函数”和属性组合起来形成一个“类”。一步步走来,我们在考虑着机器运行代码效率的提高的同时也在考虑减轻程序员的工作量。 那么我们今天讲到的适配器模型更着重考虑的是什么呢?是程序员工作量。

  什么时候会用到适配器模式?

  其实最简单的例子是当我们引用一个第三方类库。这个类库随着版本的改变,它提供的API也可能会改变。如果很不幸的是,你的应用里引用的某个API已经发生改变的时候,除了在心中默默地骂“wocao”之外,你还得去硬着头皮去改大量的代码。

  难道真的一定要如此吗?按照套路来说,我会回答“不是的”。我们有适配器模式啊~~

  当接口发生改变时,适配器模式就派上了用场。

举个栗子

  如果通过上面的简单描述,你都能懂,那在下只能佩服你的领悟能力超群了。一般人一定还是不知所云。为了方便理解,我引用一位博友的例子。原文地址

  一开始的和谐

  黑枣玩具公司专门生产玩具,生产的玩具不限于狗、猫、狮子,鱼等动物。每个玩具都可以进行“张嘴”与“闭嘴”操作,分别调用了openMouth与closeMouth方法。

  在这个时候,我们很容易想到可以第一定义一个抽象类Toy,甚至是接口Toy,这些问题不大,其他的类去继承父类,实现父类的方法。一片和谐,信心向荣。

  平衡的破坏

为了扩大业务,现在黑枣玩具公司与红枣遥控公司合作,红枣遥控公司可以使用遥控设备对动物进行嘴巴控制。不过红枣遥控公司的遥控设备是调用的动物的doMouthOpen及doMouthClose方法。黑枣玩具公司的程序员现在必须要做的是对Toy系列类进行升级改造,使Toy能调用doMouthOpendoMouthClose方法。

  考虑实现的方法时,我们很直接地想到,你需要的话我再在我的父类子类里给你添加这么两个方法就好啦。当你一次又一次在父类子类里面重复添加着这两个方法的时候,总会想着如此重复的工作,难道不能解决么?当有数百个子类的时候,程序员会改疯的。程序员往往比的是谁在不影响效率的时候更会“偷懒”。这样做下去程序员会觉得自己很傻。(其实我经常当这样的傻子)

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();
}
}
}

  在这个时候,程序员必须要动脑子想办法了,就算自己勤快,万一哪天紫枣青枣黄枣山枣这些遥控公司全来的时候,忽略自己不断增多的工作量不说,这个Toy类可是越来越大,总有一天程序员不崩溃,系统也会崩溃。

   问题在出在哪里呢?

  像上面那样编写代码,代码实现违反了“开-闭”原则,一个软件实体应当对扩展开放,对修改关闭。即在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展。也就是说每个尸体都是一个小王国,你让我参与你的事情这个可以,但你不能修改我的内部,除非我的内部代码确实可以优化。

  在这种想法下,我们懂得了如何去用继承,如何利用多态,甚至如何实现“高内聚,低耦合”。

  回到这个问题,我们现在面临这么一个问题,新的接口方法我要实现,旧的接口(Toy抽象类)也不能动,那么总得有个解决方法吧。那就是引入一个新的类--我们本文的主角--适配器。  适配器要完成的功能很明确,引用现有接口的方法实现新的接口的方法。更像它名字描述的那样,你的接口不改的话,我就利用现有接口和你对接一下吧。

  到此,解决方法已经呼之欲出了,下面贴上代码。

  

<?php
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();
}
}
} 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();

  最后的结果就是,Toy类及其子类在不改变自身的情况下,通过适配器实现了不同的接口。

  最后总结

  将一个类的接口转换成客户希望的另外一个接口,使用原本不兼容的而不能在一起工作的那些类可以在一起工作.

  适配器模式核心思想:把对某些相似的类的操作转化为一个统一的“接口”(这里是比喻的说话)--适配器,或者比喻为一个“界面”,统一或屏蔽了那些类的细节。适配器模式还构造了一种“机制”,使“适配”的类可以很容易的增减,而不用修改与适配器交互的代码,符合“减少代码间耦合”的设计原则。

 以上

系列文章:

      php模式设计之 单例模式

    php模式设计之 工厂模式

   php模式设计之 注册树模式

    php模式设计之 适配器模式

          php模式设计之 观察者模式

php模式设计之 适配器模式的更多相关文章

  1. PHP模式设计之单例模式、工厂模式、注册树模式、适配器模式、观察者模式

    php模式设计之单例模式 什么是单例模式? 单例模式是指在整个应用中只有一个实例对象的设计模式 为什么要用单例模式? php经常要链接数据库,如果在一个项目中频繁建立连接数据库,会造成服务器资源的很大 ...

  2. php模式设计之 观察者模式

    这是我写的<php模式设计>的第五篇.前面的四篇在不断学习不断加深认识,到了今天再看观察者模式,觉得非常容易理解.这也许就是我们积少成多的结果吧.希望还是能够不断进步. 开篇还是从名字说起 ...

  3. php模式设计之 注册树模式

    在前两篇单例模式和工厂模式后,终于迎来了最后一个基础的设计模式--注册树模式. 什么是注册树模式? 注册树模式当然也叫注册模式,注册器模式.之所以我在这里矫情一下它的名称,是因为我感觉注册树这个名称更 ...

  4. php模式设计之 工厂模式

    承接上篇php模式设计之 单例模式,(虽然好像关系不大).今天讲述第二种基础的模式设计——工厂模式. 那么何为工厂模式? 从名字来看,似乎看不出什么端倪.工厂模式,和生产有关?还是和生产流程有关?难道 ...

  5. php模式设计之 单例模式

    模式设计是什么?初学者一开始会被这高大上的名称给唬住.而对于有丰富编程经验的老鸟来说,模式设计又是无处不在.很多接触的框架就是基于各种模式设计形成的. 简单说,在写代码的过程中一开始往往接触的是面向过 ...

  6. 分享基于Entity Framework的Repository模式设计(附源码)

    关于Repository模式,在这篇文章中有介绍,Entity Framework返回IEnumerable还是IQueryable? 这篇文章介绍的是使用Entity Framework实现的Rep ...

  7. JavaScript高级---门面模式设计

    门面模式 两个作用: 1.简化类的接口 2.消除类与使用它的客户代码之间的耦合 门面模式常常是开发人员最亲密的朋友.它几乎是所有javascript库的核心原则 门面模式的目的是为了让开发人员用更简单 ...

  8. JavaScript高级---组合模式设计

    一.设计模式 javascript里面给我们提供了很多种设计模式: 工厂.桥.组合.门面.适配器.装饰者.享元.代理.观察者.命令.责任链 在前面我们实现了工厂模式和桥模式 工厂模式 : 核心:为了生 ...

  9. JavaScript高级---桥模式设计

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...

随机推荐

  1. HTML5 datalist 标签

    以前需要用JS写一个自动完成组件(Suggest),很费劲.HTML5时代则不用了,直接使用datalist标签,直接减少了工作量.如下 <!DOCTYPE html> <html& ...

  2. 01_蚂蚁感冒(第五届蓝桥预赛本科B组第8题 nyoj 990)

    问题来源:第五届蓝桥预赛本科B组第8题 问题描述:有在一条定长(100cm)的直杆上有n(1<n<50)只蚂蚁(每只蚂蚁的起点都不一样),他们都以相同的速度(1cm/s)向左或者向右爬,  ...

  3. java charset detector

    https://code.google.com/p/juniversalchardet/downloads/list java移植mozilla的编码自动检测库(源码为c++),准确率高. 通过svn ...

  4. 《TCP/IP 详解 卷一》读书笔记 -----第四章 ARP

    1.一个物理层的网络,例如以太网,可以同时被多个不同的网络层所使用.例如网络中的一些主机使用TCP/IP协议,其他主机使用其他的网络协议. 2.设备驱动软件从不关心IP数据报中的目的IP地址.这也是为 ...

  5. 【Android UI设计与开发】2.引导界面(二)使用ViewPager实现欢迎引导页面

    1.实现的效果 2.编码前的准备工作 ViewPager是Android3.0之后提供的新特性,所以要想让你的应用向下兼容就必须要android-support-v4.jar这个包的支持,这是一个来自 ...

  6. JS练习题-Harshad numbers

    在Codewars上面练习,现在到6级的题目了(数字越小越难) 这道题叫Harshad or Niven numbers, 原题http://www.codewars.com/kata/54a0689 ...

  7. Spring AOP 注解和xml实现 --转载

    AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程.可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. ...

  8. java 静态方法和实例方法的区别

    转自 java 静态方法和实例方法的区别 静态方法和实例方法的区别主要体现在两个方面:   在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法 ...

  9. 关于 app测试工具

    1. 腾讯的GT 2. testin 云测 3. monkey的自动化测试 4. 纯手工的功能测试.

  10. iOS学习资料

    1. UI整理 http://www.cocoachina.com/ios/20151110/14067.html. 2. iOS学习路径 http://www.cocoachina.com/ios/ ...