设计模式之(六)------适配器模式

【摘自 您的设计模式】这个模式也很简单,你笔记本上的那个拖在外面的黑盒子就是个适配器,一般你在中国能用,在日本也能用,

虽然两个国家的的电源电压不同,中国是 220V,日本是 110V,但是这个适配器能够把这些不同的电压转换

为你需要的 36V 电压,保证你的笔记本能够正常运行,那我们在设计模式中引入这个适配器模式是不是也

是这个意思呢?是的,一样的作用,两个不同接口,有不同的实现,但是某一天突然上帝命令你把 B 接口

转换为 A 接口,怎么办?继承,能解决,但是比较傻,而且还违背了 OCP 原则,怎么办?好在我们还有适

配器模式。

我在 2004 年的时候带了一个项目,做一个人力资源管理,该项目是我们总公司发起的项目,公司一共

有 700 多号人,包括子公司,这个项目还是比较简单的,分为三大模块:人员信息管理,薪酬管理,职位

管理,其中人员管理这块就用到了适配器模式,是怎么回事呢?当时开发时明确的指明:人员信息简管理

的对象是所有员工的所有信息,然后我们就这样设计了一个类图:

还是比较简单的,有一个对象 UserInfo 存储用户的所有信息(实际系统上还有很多子类,不多说了),

也就是 BO(Business Object),这个对象设计为贫血对象(Thin Business Object),不需要存储状态以及

相关的关系,而且我也是反对使用充血对象(Rich Business Object),这里说了两个名词贫血对象和充血

对象,这两个名词很简单,在领域模型中分别叫做贫血领域模型和充血领域模型,有什么区别呢?在一个

对象中不存储实体状态以及对象之间的关系的就叫做贫血对象,上升到领域模型中就是贫血领域模型,有

实体状态和对象关系的模型的就是充血领域模型,是不是太技术化了?这个看不懂没关系,都是糊弄人的

东西,属于专用名词,这本书写完了,我再折腾本领域模型的文章,揭露领域模型中糊弄人的专有名词,

这个绝对是专有名词的堆砌,呵呵。扯远了,我们继续说适配器模式,这个 UserInfo 对象,在系统中很多

地方使用,你可以查看自己的信息,也可以做修改,当然这个对象是有 setter 方法的,我们这里用不到就

隐藏掉了。

这个项目是 04 年年底投产的,运行到 05 年年底还是比较平稳的,中间修修补补也很正常,05 年年底

不知道是那股风吹的,很多公司开始使用借聘人员的方式招聘人员,我们公司也不例外,从一个人力资源

公司借用了一大批的低技术、低工资的人员,分配到各个子公司,总共有将近 200 号人,然后就找我们部

门老大谈判,说要增加一个功能借用人员管理,老大一看有钱赚呀,一拍大腿,做!

我带人过去一调研,不是这么简单,人力资源公司有一套自己的人员管理系统,我们公司需要把我们

使用到的人员信息传输到我们的系统中,系统之间的传输使用 RMI(Remote Method Invocation,远程对象

调用)的方式,但是有一个问题人力资源公司的人员对象和我们系统的对象不相同呀,他们的对象是这样

的:

人员资源公司是把人的信息分为了三部分:基本信息,办公信息和个人家庭信息,并且都放到了 HashMap

中,比如人员的姓名放到 BaseInfo 信息中,家庭地址放到 HomeInfo 中,这咱不好说他们系统设计的不好,

那问题是咱的系统要和他们系统有交互,怎么办?使用适配器模式,类图如下:

大家可能会问,这两个对象都不在一个系统中,你如何使用呢?简单!RMI 已经帮我们做了这件事情,

只要有接口,就可以把远程的对象当成本地的对象使用,这个大家有时间可以去看一下 RMI 文档,不多说

了。通过适配器,把 OuterUser 伪装成我们系统中一个 IUserInfo 对象,这样,我们的系统基本不用修改

什么程序员,所有的人员查询、调用跟本地一样样的,说的口干舌燥,那下边我们来看具体的代码实现:

首先看 IUserInfo.java 的代码:

package com.cbf4life;
/**
* @author cbf4Life [email protected] com
* I‘m glad to share my knowledge with you all.
* 用户信息对象
*/
public interface IUserInfo {
//获得用户姓名
public String getUserName();
//获得家庭地址
public String getHomeAddress();
//手机号码,这个太重要,手机泛滥呀
public String getMobileNumber();
//办公电话,一般式座机
public String getOfficeTelNumber();
//这个人的职位是啥
public String getJobPosition();
//获得家庭电话,这个有点缺德,我是不喜欢打家庭电话讨论工作
public String getHomeTelNumber();
}

然后看这个接口的实现类:

package com.cbf4life;
/**
* @author cbf4Life [email protected] com
* I‘m glad to share my knowledge with you all.
*/
public class UserInfo implements IUserInfo {
/*
 * 获得家庭地址,下属送礼也可以找到地方
 * */
public String getHomeAddress() {
 System. out.println(" 这里是员工的家庭地址....");
 return null;
 }
/*
 * 获得家庭电话号码
 */
public String getHomeTelNumber() {
 System. out.println(" 员工的家庭电话是....");
 return null;
 }
/*
 * 员工的职位,是部门经理还是小兵
 */
public String getJobPosition() {
 System. out.println(" 这个人的职位是BOSS....");
 return null;
 }
/*
 * 手机号码
 */
public String getMobileNumber() {
 System. out.println(" 这个人的手机号码是0000....");
 return null;
 }
/*
 * 办公室电话,烦躁的时候最好“不小心”把电话线踢掉,我经常这么干,对己对人都有好处
 */
public String getOfficeTelNumber() {
 System. out.println(" 办公室电话是....");
 return null;
 }
/*
 * 姓名了,这个老重要了
 */
public String getUserName() {
 System. out.println(" 姓名叫做...");
 return null;

 }
}

可能有人要问了,为什么要把电话号码、手机号码都设置成 String 类型,而不是 int 类型,大家觉的

呢?题外话,这个绝对应该是 String 类型,包括数据库也应该是 varchar 类型的,手机号码有小灵通带区

号的,比如 02100001,这个你用数字怎么表示?有些人要在手机号码前加上 0086 后再保存,比如我们公司

的印度阿三就是这样,喜欢在手机号码前 0086 保存下来,呵呵,我是想到啥就说啥,啰嗦了点。继续看我

们的代码,下面看我们系统的应用如何调用 UserInfo 的信息:

package com.cbf4life;
/**
* @author cbf4Life [email protected] com
* I‘m glad to share my knowledge with you all.
* 这就是我们具体的应用了,比如老板要查所有的20-30的女性信息
*/
public class App {
public static void main(String[] args) {
 //没有与外系统连接的时候,是这样写的
 IUserInfo youngGirl = new UserInfo();
 //从数据库中查到101个
 for(int i=0;i<101;i++){
 youngGirl.getMobileNumber();
 }
 }
}

这老板,比较那个,为什么是 101,是男生都应该知道吧,111 代表男生,101 代表女生,呵呵,是不

是比较色呀。从数据库中生成了 101 个 UserInfo 对象,直接打印出来就成了。那然后增加了外系统的人员

信息,怎么处理呢?下面是 IOuterUser.java 的源代码:

public interface IOuterUser {
//基本信息,比如名称,性别,手机号码了等
public Map getUserBaseInfo();
//工作区域信息
public Map getUserOfficeInfo();
//用户的家庭信息
public Map getUserHomeInfo();
}
我们再看看外系统的用户信息的具体实现类:
package com.cbf4life;
import java.util.HashMap;
import java.util.Map;
/**
* @author cbf4Life [email protected] com
* I‘m glad to share my knowledge with you all.
* 外系统的用户信息的实现类
*/
@SuppressWarnings("all")
public class OuterUser implements IOuterUser {
/*
 * 用户的基本信息
 */
public Map getUserBaseInfo() {
 HashMap baseInfoMap = new HashMap();
 baseInfoMap.put("userName", " 这个员工叫混世魔王....");
 baseInfoMap.put("mobileNumber", " 这个员工电话是....");
 return baseInfoMap;
 }
/*
 * 员工的家庭信息
 */
public Map getUserHomeInfo() {
 HashMap homeInfo = new HashMap();
 homeInfo.put("homeTelNumbner", " 员工的家庭电话是....");
 homeInfo.put("homeAddress", " 员工的家庭地址是....");
 return homeInfo;
 }
/*
 * 员工的工作信息,比如职位了等
 */
public Map getUserOfficeInfo() {
 HashMap officeInfo = new HashMap();
 officeInfo.put("jobPosition", " 这个人的职位是BOSS...");
 officeInfo.put("officeTelNumber", " 员工的办公电话是....");
 return officeInfo;
 }
}

那怎么把外系统的用户信息包装成我们公司的人员信息呢?看下面的 OuterUserInfo 类源码,也就是

我们的适配器:

public class OuterUserInfo extends OuterUser implements IUserInfo {
private Map baseInfo = super.getUserBaseInfo(); //员工的基本信息
private Map homeInfo = super.getUserHomeInfo(); //员工的家庭 信息
private Map officeInfo = super.getUserOfficeInfo(); //工作信息
/*
 * 家庭地址
 */
public String getHomeAddress() {
 String homeAddress = (String) this. homeInfo.get("homeAddress");
 System. out.println(homeAddress);
 return homeAddress;
 }
/*
 * 家庭电话号码
 */
public String getHomeTelNumber() {
 String homeTelNumber = (String) this. homeInfo.get("homeTelNumber");
 System. out.println(homeTelNumber);
 return homeTelNumber;
 }
/*
 *职位信息
 */
public String getJobPosition() {
 String jobPosition = (String) this. officeInfo.get("jobPosition");
 System. out.println(jobPosition);
 return jobPosition;
 }
/*
 * 手机号码
 */
public String getMobileNumber() {
 String mobileNumber = (String) this. baseInfo.get("mobileNumber");
 System. out.println(mobileNumber);
 return mobileNumber;
 }
/*
 * 办公电话
 */
public String getOfficeTelNumber() {
 String officeTelNumber = (String) this. officeInfo.get("officeTelNumber");
 System. out.println(officeTelNumber);
 return officeTelNumber;
 }
/*
 * 员工的名称
 */
public String getUserName() {
 String userName = (String) this. baseInfo.get("userName");
 System. out.println(userName);
 return userName;
 }
}

这个适配器的作用就是做接口的转换,

那然后我们再来看看我们的业务是怎么调用的:

public class App {
public static void main(String[] args) {
 //没有与外系统连接的时候,是这样写的
 //IUserInfo youngGirl = new UserInfo();
 //老板一想不对呀,兔子不吃窝边草,还是找人力资源的员工好点
 IUserInfo youngGirl = new OuterUserInfo(); //我们只修改了这一句好
 //从数据库中查到101个
 for(int i=0;i<101;i++){
 youngGirl.getMobileNumber();
 }
 }
}

大家看,使用了适配器模式只修改了一句话,其他的业务逻辑都不用修改就解决了系统对接的问题,

而且在我们实际系统中只是增加了一个业务类的继承,就实现了可以查本公司的员工信息,也可以查人力

资源公司的员工信息,尽量少的修改,通过扩展的方式解决了该问题。

适配器模式分为类适配器和对象适配器,这个区别不大,上边的例子就是类适配器,那对象适配器是

什么样子呢?对象适配器的类图是这个样子滴:

看到没?和上边的类图就一个箭头的图形的差异,一个是继承,一个是关联,就这么多区别,只要把

我们上面的程序稍微修改一下就成了类适配器,这个大家自己考虑一下,简单的很。

适配器模式不适合在系统设计阶段采用,没有一个系统分析师会在做详设的时候考虑使用适配器模式,

这个模式使用的主要场景是扩展应用中,就像我们上面的那个例子一样,系统扩展了,不符合原有设计的

时候才考虑通过适配器模式减少代码修改带来的风险。

时间: 2024-10-03 13:47:07

设计模式之(六)------适配器模式的相关文章

Java设计模式(六)合成模式 享元模式

(十一)合成模式 Composite 合成模式是一组对象的组合,这些对象可以是容器对象,也可以是单对象.组对象允许包含单对象,也可以包含其他组对象,要为组合对象和单对象定义共同的行为.合成模式的意义是 保证客户端调用单对象与组合对象的一致性. class TreeNode{ private String name; private TreeNode parent; private Vector<TreeNode> children = new Vector<TreeNode>();

IOS设计模式之三(适配器模式,观察者模式)

本文原文请见:http://www.raywenderlich.com/46988/ios-design-patterns. 由 @krq_tiger(http://weibo.com/xmuzyq)翻译,如果你发现有什么错误,请与我联系谢谢. 适配器(Adapter)模式 适配器可以让一些接口不兼容的类一起工作.它包装一个对象然后暴漏一个标准的交互接口. 如果你熟悉适配器设计模式,苹果通过一个稍微不同的方式来实现它-苹果使用了协议的方式来实现.你可能已经熟悉UITableViewDelegat

设计模式(六):Singleton 单件模式 -- 创建型模式

1.定义 当需要控制一个类的实例数量且调用者可以从一个公共的访问点访问时. 2.适用场景 1. 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时. 2. 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时. 3.评价 优点: 1. 对唯一实例的受控访问, 因为Singleton类封装它的唯一实例,所以它可以严格的控制客户怎样以及何时访问它. 2. 缩小名空间,Singleton模式是对全局变量的一种改进.它避免了那些存储唯一实例的全局变量污染名空

设计模式 ( 十六 ): Mediator中介者模式 -- 行为型

1.概述 在面向对象的软件设计与开发过程中,根据“单一职责原则”,我们应该尽量将对象细化,使其只负责或呈现单一的职责,即将行为分布到各个对象中. 对于一个模块或者系统,可能由很多对象构成,而且这些对象之间可能存在相互的引用,在最坏的情况下,每一个对象都知道其他所有的对象,这无疑复杂化了对象之间的联系.虽然将一个系统分割成许多对象通常可以增强可复用性,但是对象间相互连接的激增又会降低其可复用性,大量的相互连接使得一个对象似乎不太可能在没有其他对象的支持下工作,系统表现为一个不可分割的整体,而且对系

云计算设计模式(六)——命令和查询职责分离(CQRS)模式

云计算设计模式(六)--命令和查询职责分离(CQRS)模式 隔离,通过使用不同的接口,从操作读取数据更新数据的操作.这种模式可以最大限度地提高性能,可扩展性和安全性;支持系统在通过较高的灵活性,时间的演变;防止更新命令,从造成合并在域级别上的冲突. 背景和问题 在传统的数据管理系统中,这两个命令(更新数据)和查询(请求数据),针对在一个单一的数据存储库中的相同的一组实体的执行.这些实体可以是在关系数据库中的一个或多个表,如SQL Server的行的子集. 典型地,在这些系统中,所有的创建,读取,

设计模式第六讲:单例模式(附优化方案)

单例模式 一:模式定义 一个类有且仅有一个实例,并且自行实例化向整个系统提供. 二:模式特点 通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源. 三:使用场景 希望在系统中某个类的对象只能存在一个. 四:具体实现 单例模式有两种写法: 1.懒汉式 2.饿汉式 懒汉式实现: public class Single { private static Single instance; private Singleton (){} public

设计模式(六) 适配器模式(Adapter)

开发当中我们经常遇到这样的情况:有一些功能自己开发需要花费巨大的成本,但是现存的可供使用的第三方库还不太成熟,我们可以暂时使用这些库.但是,一旦需要修改依赖库的时候,源码也需要大规模地修改,有没有什么方法能够最大限度地降低修改的幅度呢?这时候我们可以考虑使用adapter模式. 一.定义 适配器模式是一种结构型模式.它的目的是使一个类的接口转换成客户希望的另一种接口,适配器模式使得原本由于接口不兼容而不能一起工作的类可以一起工作. 二.结构 适配器模式有两种类型 1.对象适配器模式 在这种模式中

Java程序设计模式系列之适配器模式

理解适配器设计模式需要从生活中的场景进行联系,在生活当中有那些东西能够称为适配器呢?从字面上理解,"适配"的意思就是让一个东西和另一个东西配对,能够让他们一起工作,比如大家用的笔记本电脑就需要用到电源适配器,转换插口和电流到笔记本上给笔记本电脑进行供电.再比如在国内所有的电器插座都是两口或三口的国内标准插座,但如果到中国大陆以外的地区旅行,想要使用电源给电子产品供电,就需要转换插口了.比如王老师到了香港,看到的电源插座是这样的: 很明显,从国内带去的手机就无法充电了. 那么这个问题怎么

设计模式学习笔记-适配器模式(对象适配器)

一.概述 将一个类的接口转换为客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作: 二.模式中的角色 Target:定义客户端使用的与特定领域相关的接口: Clinet:与符合Target接口的对象协同: Adpatee:定义一个已经存在的接口,这个接口需要适配: Adpater:对Adpatee的接口与Target接口进行匹配: 三.UML类图 四.代码实现 /// <summary> /// 客户端期待的类 /// </summary&

设计模式指六-Adapter模式

适配器模式,使用之处比较特殊,不属于常规设计模式,主要用于不同系统之间的处理.是将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 系统内部的实体接口 //IUserInfo.h// #pragma once #include <iostream> using std::string; class IUserInfo { public: IUserInfo(void) { } virtual ~IUserInfo(void)