您的位置:首页 > 编程语言 > C语言/C++

<C++> 类的‘’实现‘’与‘’接口‘’分离

2017-06-25 17:58 1431 查看
C++这门语言囊括了多种语言范式,并不是严格的
OOP
语言,所以在“实现与接口的分离”这一方面做得并不算好。 这里简述
C++
的类设计中把实现与接口分离的方法.

在C++的某个
class
的定义中,不仅声明了接口,还可以看到实现的具体细节,当然这个视角是针对类内部的,通常是在私有域
private
中,比如下面这个类:

class Person {
public:
Person(const std::string& name, cpnst Date& birthday,
const Address& addr);
std::string name() const;
std::string birthDate() const;
std::string address() const;
private:
//实现的细节
std::string theName;
Date theB工rthDate;
Address theAddress;
};


要实现这么一个类,一般的做法就是在所在声明的头文件中
include
Date和Address两个类的声明文件
include"date.h"/include"address.h"
,如果Date或者Address类发生了改动,那么连带Person类及包含Person头文件的后续文件都要重新编译。这样子的话Person类的使用者就会有极大的不便,Person/Date/Address中的任何一个类改动,Person客户端都必须重新编译。

我们可以借鉴类似Java中的做法,声明一个类时,出现的是一个指向该类型的指针,只要分配给该指针足够的空间,我们就可以隐藏其“实现”了。我打一个可能不是那么形象的例子,原始版本就是一本书,我们是这本书的使用者,书中的内容有改动时,出版社就需要重新印刷一份,现在我们不用拿着书了,我们手中只有一个目录,我们需要资料的时候直接按照目录向出版社拿内容,出版社可以随时更新他手头的资料内容,而我们只要专注于“索要”资料就行了。

但是这样子分离的还不够彻底,我们把
Person
类分离为两部分,纯粹的两部分,一部分叫实现类(定义为PersonImpl [Person Implementation]),另一部分叫接口类(即 Person)。

把具体的实现放在
PersonImpl
类中,然后通过
Person
类管理指针形式的
PersonImpl
对象,然后把接口暴露给客户,实现分离。

#include <string>
#include <memory>

class Personlmpl;  //实现类的声明
class Date;     //接口中需要用到的其他类的声明
class Address;

class Person {
public:
Person(const std::string& name, const Date& birthday,
const Address& addr);
std::string name() const;
std::string birthDate() const;
std::string address() const;
private:
std::shared_ptr<PersonImpl> plmpl;  //指针的形式,且遵循以类管理资源的原则
};


在PersonImpl类和Person类中,有着同名的成员函数,接口也是完全一样的

这样的设计,Person类的使用者就完全的与Person/Date/Address三种类的实现细节分离开了,只要专注于接口的使用就OK了。

并且在这样的设计下,如果Date,Address之类的Class有变动,Person类的使用者也不需要重新编译。

另一种方法也许更常见,就是把接口
(Interface)
都在一个接口类(抽象类)中描述,然后由它的派生类来具体实现,有着纯虚函数的类就是抽象类,抽象类是一种不能实例化的特殊类,所以经常可以用来描述接口:

class Person {
public:
virtual -Person();
virtual std::string name() const = 0;  // =0后缀,即为纯虚函数
virtual std::string birthDate() canst = 0;
virtual std::string address() const = 0;
};


正常的使用就是在派生类中具体实现:

class RealPerson: public Person {
public:
RealPerson(const std::string& name, const Date& birthday,
const Address& addr)
theName(name) , theBirthDate(birthday) , theAddress(addr)
{}
virtual -RealPerson() { )
std::string name() const;
std::string birthDate() const;
std::string address() const;
private:
std::string theName;
Date theBirthDate;
Address theAddress;
}


除此之外,抽象类也是
“工厂设计模式”
的一种比较优良的实践,我们可以通过一个在抽象类中定义一个
create函数
(通常是静态方法,用
static
关键字来修饰),用来动态生成继承体系中的各种实例,都可以通过一个
create函数
来实现,当然别忘了,为了满足多态的要求,返回的应该是一个[b]指针(*)或引用(&)。

class Person {
public:
static std::shared_ptr<Person> create(const std::string& name, const Date& birthday, const Address& addr)
{
return std::shared_ptr<Person>(new RealPerson(name, birthday,addr);  //此例中只展示了生成RealPerson实例,更真实的实践中通常取决于参数,环境等等
}

};


当然两种方法都是很好很实用的剥离“实现”与“接口”的实践,减少类与类之间的耦合度。

他们分别叫做
handle class
interface class
,可以从名字形象的推测出它们的含义。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ oop