定义:
将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够一起工作。
示例一:适配器模式(通用版)
1. 类图19-4
2. 类图说明
- Target 目标角色。该角色定义把其他类转换为何种接口,也就是我们期望的接口。
- Adaptee 源角色。想把谁转换成目标角色,这个谁就是源角色,它是已经存在的、运行良好的类或对象,通过适配器角色的包装,会成为新的角色。
- Adapter 适配器角色。适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色需要新建立,它的职责很简单:把源角色转换为目标转换,通过集成或是类关联的方式。
3. 代码清单19-4
////////// ********** 4.适配器模式(通用版),代码清单19-4:***************//
//目标角色
class Target
{
public:
virtual void request() = 0;
};
//目标角色实现类
class ConcreteTarget:public Target
{
public:
virtual void request(){qDebug() << "request";}
};
//源角色
class Adaptee
{
public:
virtual void doSomething(){qDebug() << "doSomething";}
};
//适配器角色
class Adapter:public Target, public Adaptee
{
public:
virtual void request(){Adaptee::doSomething();}
};
int main()
{
Target *target = new ConcreteTarget();
target->request();
Target *target2 = new Adapter();
target2->request();
return 0;
}
示例二:人力资源管理
1.需求分析
项目分为三个模块:人员信息管理、薪酬管理、职位管理,管理对象为所有在职员工。
2. 类图19-1
3. 类图说明
一个对象UserInfo存储用户的所有信息
4.代码清单19-1(我公司人员信息)
//////////////// ********** 1.人力资源管理 ,代码清单19-1:***************//
//员工信息接口
class IUserInfo
{
public:
virtual QString getUserName() = 0;
virtual QString getHomeAddress() = 0;
virtual QString getMobileNumber() = 0;
virtual QString getOfficeTelNumber() = 0;
virtual QString getJobPosition() = 0;
virtual QString getHomeTelNumber() = 0;
};
//实现类
class UserInfo:public IUserInfo
{
public:
virtual QString getUserName() {return "UserName";}
virtual QString getHomeAddress() {return "HomeAddress";}
virtual QString getMobileNumber() {return "MobileNumber";}
virtual QString getOfficeTelNumber() {return "OfficeTelNumber";}
virtual QString getJobPosition() {return "JobPosition";}
virtual QString getHomeTelNumber() {return "HomeTelNumber";}
};
5.增加需求
加入劳务公司人员,但出现的问题是劳务公司的人员对象和我们系统的对象不相同
6.劳务公司的人员信息,类图19-2
7.类图说明
人员信息分为了三部分:基本信息、办公信息、个人家庭信息,并都放入了HashMap中,比如人员姓名放到BaseInfo中,家庭地址放到HomeInfo中
8.代码清单19-2(劳务公司人员信息)
//////// ********** 2.人力资源管理 (劳务服务公司),代码清单19-2:***************//
//劳务公司人员信息接口
class IOuterUser
{
public:
virtual QHash<QString, QString> getUserBaseInfo() = 0;
virtual QHash<QString, QString> getUserOfficeInfo() = 0;
virtual QHash<QString, QString> getUserHomeInfo() = 0;
};
//劳务公司人员实现
class OuberUser
{
public:
virtual QHash<QString, QString> getUserBaseInfo()
{
QHash<QString, QString> hash1;
hash1.insert("userName", "Jack");
hash1.insert("mobileNumber", "111");
return hash1;
}
virtual QHash<QString, QString> getUserOfficeInfo()
{
QHash<QString, QString> hash1;
hash1.insert("jobPosition", "cc");
hash1.insert("officeTelNumber", "111");
return hash1;
}
virtual QHash<QString, QString> getUserHomeInfo()
{
QHash<QString, QString> hash1;
hash1.insert("homeTellNumbner", "111");
hash1.insert("homeAddress", "aaaaa");
return hash1;
}
};
9.问题:
我方公司系统要和劳务公司系统进行交互,为了不给我方系统进行大改动,可以增加转化。先拿到对方的数据对象,然后转化为我们自己的数据对象,中间加一层转化层
10.完整版(加入适配器),类图19-3
11.代码清单19-3(添加转化)
////////// ********** 3.人力资源管理 (增加转换层),代码清单19-3:***************//
//员工信息接口
class IUserInfo
{
public:
virtual QString getUserName() = 0;
virtual QString getHomeAddress() = 0;
virtual QString getMobileNumber() = 0;
virtual QString getOfficeTelNumber() = 0;
virtual QString getJobPosition() = 0;
virtual QString getHomeTelNumber() = 0;
};
//实现类
class UserInfo:public IUserInfo
{
public:
virtual QString getUserName() {return "UserName";}
virtual QString getHomeAddress() {return "HomeAddress";}
virtual QString getMobileNumber() {return "MobileNumber";}
virtual QString getOfficeTelNumber() {return "OfficeTelNumber";}
virtual QString getJobPosition() {return "JobPosition";}
virtual QString getHomeTelNumber() {return "HomeTelNumber";}
};
//劳务公司人员信息接口
class IOuterUser
{
public:
virtual QHash<QString, QString> getUserBaseInfo() = 0;
virtual QHash<QString, QString> getUserOfficeInfo() = 0;
virtual QHash<QString, QString> getUserHomeInfo() = 0;
};
//劳务公司人员实现
class OuberUser
{
public:
virtual QHash<QString, QString> getUserBaseInfo()
{
QHash<QString, QString> hash1;
hash1.insert("userName", "userName");
hash1.insert("mobileNumber", "mobileNumber");
return hash1;
}
virtual QHash<QString, QString> getUserOfficeInfo()
{
QHash<QString, QString> hash1;
hash1.insert("jobPosition", "jobPosition");
hash1.insert("officeTelNumber", "officeTelNumber");
return hash1;
}
virtual QHash<QString, QString> getUserHomeInfo()
{
QHash<QString, QString> hash1;
hash1.insert("homeTellNumbner", "homeTellNumbner");
hash1.insert("homeAddress", "homeAddress");
return hash1;
}
};
class OuterUserInfo:public OuberUser,public IUserInfo
{
public:
OuterUserInfo()
{
this->m_baseInfo = OuberUser::getUserBaseInfo();
this->m_homeInfo = OuberUser::getUserHomeInfo();
this->m_officeInfo = OuberUser::getUserOfficeInfo();
}
virtual QString getHomeAddress()
{
QString homeAddress = this->m_homeInfo.value("homeAddress");
return homeAddress;
}
virtual QString getHomeTelNumber()
{
QString homeTelNumber = this->m_homeInfo.value("homeTellNumbner");
return homeTelNumber;
}
virtual QString getJobPosition()
{
QString jobPosition = this->m_officeInfo.value("jobPosition");
return jobPosition;
}
virtual QString getOfficeTelNumber()
{
QString officeTelNumber = this->m_officeInfo.value("officeTelNumber");
return officeTelNumber;
}
virtual QString getUserName()
{
QString userName = this->m_baseInfo.value("userName");
return userName;
}
virtual QString getMobileNumber()
{
QString mobileNumber = this->m_baseInfo.value("mobileNumber");
return mobileNumber;
}
private:
QHash<QString, QString> m_baseInfo;
QHash<QString, QString> m_homeInfo;
QHash<QString, QString> m_officeInfo;
};
int main()
{
IUserInfo *user = new UserInfo();
for (int i = 0; i < 5; ++i)
{
qDebug() << user->getMobileNumber();
}
IUserInfo *user2 = new OuterUserInfo();
qDebug() << user2->getMobileNumber();
return 0;
}
12. 代码说明:我们通过尽量少的修改,通过扩展的方式解决了该问题
三、适配器模式的应用
1. 优点:
- 适配器模式可以让两个没有任何关系的类在一起运行,只要适配器这个角色能够搞定他们就成。
- 增加了类的透明性。当我们访问的Target目标角色,但是具体的实现都委托给了源角色,而这些对高层次模块是透明的,也是它不需要关心的。
- 提高了类的复用度。源角色在原有的系统中还是可以正常使用,而在目标角色中也可以充当新的演员。
- 灵活性非常好。当不想要适配器时,删除掉这个适配器就可以,其他的代码都不用修改,基本上类似一个灵活的构件,想用就用,不想就卸载。
2. 使用场景:
适配器应用场景记住一点即可:当有动机修改一个已经投产中的接口时,适配器模式可能是你最合适的模式。比如系统扩展了,需要使用一个已有或新建立的类,但这个类又不符合系统的接口。
3. 注意事项:
适配器模式最好在详细设计阶段不要好绿他,他不是为了解决还处在开发阶段的问题,而是解决正在服役的项目问题,没有一个系统分析师会在做详细设计的时候考虑使用适配器模式,这个模式使用的主要场景是扩展应用中,当系统扩展了,不符合原有设计时候才考虑通过适配器模式减少代码修改带来的风险。
参考文献《秦小波. 设计模式之禅》(第2版) (华章原创精品) 机械工业出版社