Service(服务者)——实现IndirectionIF,为Client提供服务。
图2 类之间通过接口解耦
效果
让调用者通过接口间接使用服务者”,这是面向对象设计的基础——多态性正是从这样的设计中产生的。
使用Interface模式,可以保证需要服务的类不与任何提供服务的类发生耦合。
和其他任何间接性一样,Interface模式会让程序变得更加难以理解。
使用Interface模式同样有可能造成对继承的滥用。但是,通过Interface模式让你可以从接口的角度考虑设计思想。这是面向对象设计的三大原则之一:“针对接口编程,而不是针对实现编程”[GoF95,P12]。(另外两条原则是:“优先使用对象组合,而不是类继承”[GoF95,P13]和“发现并封装变化点”[GoF95,P20])
实现
实现Interface非常直接:定义提供服务的接口,让客户类通过该接口访问服务者对象,并让服务者类实现该接口。
Interface模式在JAVA中得到了直接的支持:interface关键字。超越语言本身,实际上在C++和JAVA中,你都可以用同样直接的方式实现Interface模式——C++尽管不提供interface关键字,但我们也通常把纯虚类称为“接口”,并用多重继承来“实现”接口。在用C++实现COM规范时,我们也是用纯虚类来作为接口的。
同时,Interface模式还有不那么直接的实现方式。图2中的Client和IndirectionIF之间的“使用”关系可能不那么直接;Client和Service都可能不是一个类而是一组类甚至一个应用程序;IndirectionIF也可能不是一个接口(或抽象类)而是一个具体类;IndirectionIF和Service之间也可能不是实现(继承)的关系而是使用的关系……重要的是,应该随时想到“针对接口编程”的原则。
C++应用
正如前面所说的,C++(当然也包括其他所有的面向对象语言)是依靠Interface模式来实现多态性的。这种比较接近细节的应用,不提也罢,实在太多了!
当我们实现COM规范时,我们就使用了Interface模式:客户程序只知道接口信息,并用CoCreateInstance函数来获取适当的服务者对象。但是,三个角色之间的关系更加复杂。(如果读者对此有兴趣,请参阅[PAN99])
代码示例
我将就“场景”一节中讨论过的问题给出一个简略的示例代码。单是代码本身不能完整的表示Interface模式的精妙,但愿能起到抛砖引玉之效,让读者能理解“针对接口编程”的思想。
首先是代替AddressPanel出现的Client类:
#include "AddressIF.h"
class Client
{
public:
Client(){
m_pAddress = NULL;
};
string GetAddress(){
if(m_pAddress!=NULL)
return m_pAddress->getAddress();
else
return string();
};
bool SetAddress(string & strAddress){
if(m_pAddress!=NULL){
m_pAddress->setAddress(strAddress);
return true;
}
else
return false;
};
void SetAddressIF(AddressIF * pAddress){
m_pAddress = pAddress;
};
private:
AddressIF * m_pAddress;
};
然后是本模式的核心:提供间接性的“接口”。我们用一个纯虚类AddressIF来实现它:
#include
using std::string;
class AddressIF
{
public:
virtual void setAddress(const string & strAddress) =0;
virtual string getAddress() =0;
};
AddressIF只是提供需要的方法,由服务者类来实现它。
最后是服务者──DataClass类的代码。它通过继承来“实现”AddressIF“接口”。
#include "AddressIF.h"
// ……
// 其他头文件。
class DataClass :
// ……继承其他类
public AddressIF
{
public:
// ……其他方法
virtual string getAddress(){
return m_strAddress;
};
virtual void setAddress(const string & strAddress){
m_strAddress = strAddress;
};
private:
// ……其他成员数据。
string m_strAddress;
};
主控程序可能是这样:
#include
#include "DataClass.h"
#include "Client.h"
void main()
{
Client client;
DataClass dc1, dc2;
dc1.setAddress("Beijing");
dc2.setAddress("Shanghai");
client.SetAddressIF(&dc1);
cout << client.GetAddress().c_str() << endl;
client.SetAddressIF(&dc2);
cout << client.GetAddress().c_str() << endl;
}
相关模式
Delegation模式[GRAND98]——Delegation模式和Interface模式经常在一起使用。
另外有很多模式使用了Interface模式。
参考书目
[GoF95] Erich Gamma etc., Design Patterns: Elements of Reusable
Object-Oriented Software. Addison-Wesley 1995. 中文版:《设计模式:可复用面向对象软件的基础》,李英军等译,机械工业出版社2000年9月。
[GRAND98] Mark Grand, Patterns in Java (volume 1), Wiley computer
publishing 1998.
[PAN99] 潘爱民,《COM原理与应用》,清华大学出版社1999年11月。