Interface(接口)模式

本文介绍了一种面向对象的设计模式——接口模式,通过一个采购管理软件的例子,详细解释了如何利用接口来减少类之间的耦合,提高代码的复用性和灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 
梗概

用一个类使用其他类的实例提供的数据和服务。由于“使用服务的类”通过一个接口来访问这些“提供服务的实例”,所以它可以与这些实例所属的类保持互不依赖。

场景

假 设你正在为一家公司编写采购管理软件。在你的程序中需要下列实体的信息:生产厂商、货运公司、收货地址、交款地点……这些实体的一个共同就是:它们都有一 条“街道地址”信息。这条信息将在用户界面的不同部分出现,所以你希望用一个类来显示、编辑街道地址。这样,每当用户界面中出现街道地址时,你可以复用这 个类。我们把这个类称为AddressPanel类。

你希望AddressPanel对象可以从一个独立的数据对象中接收、设置地址信息。但是,对于这些数据对 象,AddressPanel对象应该把它们当作什么类的实例呢?很明显你应该用不同的类来表示生产厂商、货运公司……等等的实体。如果你用C++这样支 持多重继承的语言编程,你可以让“AddressPanel使用的数据对象所属的类”在继承其他类的同时还继承一个地址类。但是如果你使用的编程语言象 JAVA这样只支持单继承,你就应该再想想其他的解决方案。

在JAVA中你可以用接口(interface)来代替C++中的多重继承。这样AddressPanel类 就只要求数据对象实现地址接口。你也可以通过这个接口访问或设置数据对象中的地址信息。通过这个接口,AddressPanel对象可以在不知道对象确切 类型的前提下调用数据对象的方法。图1的类图表示出了这种关系。


图1 通过地址接口进行间接访问

约束
  • 如果一个类的实例必须使用另一个对象、而这个对象又属于一个特定的类,那么复用性会受到损害。
  • 如果“使用”类只需要“被使用”类的某些方法、而不是要求“被使用”类与“使用”类有“is-a”的关系,就可以考虑让“被使用”类实现一个接口、“使用”类通过这个接口来使用需要的方法,从而限制了类之间的依赖。
解决方案

为了避免类之间因为彼此使用而造成的耦合,让它们通过接口间接使用。图2表示出这种组织结构。

图2中的角色如下:

  • Client(客户)——使用实现了IndirectionIF的其他类。
  • InterdirectionIF(间接接口)——提供间接性,保证Client与Service之间的独立性。
  • 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月。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值