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

来源:互联网 发布:js 判断event 编辑:程序博客网 时间:2024/04/25 09:56

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;};


要实现这么一个类,一般的做法就是在所在声明的头文件中includeDate和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函数来实现,当然别忘了,为了满足多态的要求,返回的应该是一个指针(*)或引用(&)

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 classinterface class,可以从名字形象的推测出它们的含义。

原创粉丝点击