<C++> 类的‘’实现‘’与‘’接口‘’分离
2017-06-25 17:58
1431 查看
C++这门语言囊括了多种语言范式,并不是严格的
在C++的某个
要实现这么一个类,一般的做法就是在所在声明的头文件中
我们可以借鉴类似Java中的做法,声明一个类时,出现的是一个指向该类型的指针,只要分配给该指针足够的空间,我们就可以隐藏其“实现”了。我打一个可能不是那么形象的例子,原始版本就是一本书,我们是这本书的使用者,书中的内容有改动时,出版社就需要重新印刷一份,现在我们不用拿着书了,我们手中只有一个目录,我们需要资料的时候直接按照目录向出版社拿内容,出版社可以随时更新他手头的资料内容,而我们只要专注于“索要”资料就行了。
但是这样子分离的还不够彻底,我们把
把具体的实现放在
在PersonImpl类和Person类中,有着同名的成员函数,接口也是完全一样的
这样的设计,Person类的使用者就完全的与Person/Date/Address三种类的实现细节分离开了,只要专注于接口的使用就OK了。
并且在这样的设计下,如果Date,Address之类的Class有变动,Person类的使用者也不需要重新编译。
另一种方法也许更常见,就是把接口
正常的使用就是在派生类中具体实现:
除此之外,抽象类也是
当然两种方法都是很好很实用的剥离“实现”与“接口”的实践,减少类与类之间的耦合度。
他们分别叫做
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函数来实现,当然别忘了,为了满足多态的要求,返回的应该是一个[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++中接口与实现分离的技术
- C++ 接口与实现分离技术---如何将文件间的编译关系降至最低
- C++接口与实现分离的2种方法
- C++第九周【任务二】定义Time类中的<<和>>运算符重载,实现时间的输入输出
- C++中接口与实现分离的技术
- 体验C++中接口与实现分离的技术
- C++接口与实现分离
- 从信息隐藏的一个需求看C++接口与实现的分离
- C++中接口与实现分离的技术
- C++第九周【任务一】定义Complex类中的<<和>>运算符的重载,实现输入和输出
- 体验C++中接口与实现分离的技术
- C++接口与实现分离
- C++中接口与实现分离的技术
- C++中接口与实现分离的技术
- C++中接口与实现分离的技术
- C++中接口与实现分离的技术
- C++中接口与实现分离的技术
- C++——接口与实现分离技术
- C++中接口与实现分离的技术
- 第九周 C++任务一。定义Complex类中的<<和>>运算符的重载,实现输入和输出