如何设计类,可以使得文件间的编译依存关系降至最低

方法一:使用pimpl idiom(pointer to implementation)的类,常被称为 Handle classes.
将类分割为两个类,一个只提供接口,另一个负责实现该接口。分离的关键在于以“声明的依存性”替换“定义的依存性”。

以Person类为例,假设负责实现的那个所谓implementation class取名为PersonImpl, Person将定义如下:

#include<string>    //标准程序库使用预编译头文件。
#include<memory>    //为了智能指针tr1::shared_ptr而含入

class PersonImpl;   //Person实现类的前置声明
class Date;         //Person接口用到的class的前置声明
class Address;

class Person{
public:
	Person(const std::string& name,const Date& birtthday,const Address& addr);
	std::string name() const;
	std::string birthDate() const;
	std::string address() const;
private:
	std::tr1::shared_ptr<PersonImpl> pImpl; //指针,指向实现物;
}
这里Person类中只内含一个指针成员,指向其实现类。这种设计常被称为pimpl idiom(指针实现)。
这样的设计下,Person的客户就完全与Date,Address以及PersonImpl分离了。这些类的任何修改都不需要Person客户端重新编译。
编译依存性最小化的本质就是:让头文件能够自我满足,万一做不到,再让它与其他文件内的声明式相依。
真正用的时候,将所有的函数转交给相应的实现类,并由实现类完成实际工作。

下面是Person类的两个成员函数的实现:

#include "Person.h"   //这里需要包含Person class定义式。
#include "PersonImpl.h"  //也必须包含PersonImpl class定义式。

Person::Person(const std::string& name, const Date& birthday, const Address& addr)
:pImpl(new PersonImpl(name,birthday,addr))
{}

std::string Person::name() const
{
	return pImpl->name;
}

在上方成员函数实现中,Person构造函数以new调用PersonImpl构造函数,

在Person::name()函数内调用PersonImpl::name。这种设计方法就将类的声明和实现相分离,从而最大化的减小了编译依存关系。

对于Handle classess这种方法,成员函数必须通过指针实现取得对象数据。这种形式会为每一次访问增加一层间接性。

而每一个对象消耗的内存数量必须增加实现指针PersonImpl的大小。并且,这个实现指针PersonImpl必须在Handle classe构造函数内初始化,并指向一个动态分配得来的实现对象。所以这种操作方法要将蒙受因动态内存分配而带来的额外开销以及bad_alloc异常(内存不足)的可能性。

方法二:令Person成为一个特殊的抽象基类。称为Interface class。

这种类的目的是详细描述派生类的接口,因此它通常不带成员变量,也没有构造函数,只有一个虚析构函数以及一组纯虚函数,用来叙述整个接口。

Interface class为这种class创建新对象会采用工厂函数或虚构造函数来实现。这些函数返回指针(通常为智能指针),

指向动态分配所得的对象,而该对象支持Interface class的接口。这样的函数往往在Interface class内被声明为static。

实现代码如下:

class Person{
public:
	virtual ~Person();
	virtual std::string name() const = 0;
	virtual std::string birthDate() const = 0;
	virtual std::string address() const = 0;

	static std::tr1::shared_ptr<Person>   //返回一个tr1::shared_ptr,指向一个新的Person,并以给定之参数初始化。
		create(const std::string& name,
			const Date& birthday,
			const Address& addr);

};

可以这样使用它们:、

std::string name;
Date dateOfBirth;
Address address;

//创建一个对象,支持Person接口。
std::tr1::shared_ptr<Person> pp(Person::create(name,dateOfBirth,address));

std::cout<<pp->name()<<pp->birthDate()<<pp->address();

实现Interface class有两个常见机制,这里采用的是从Interface class(Person)继承接口规格,然后实现出接口所覆盖的函数。

当然,支持Interface class接口的那个具象类必须被定义出来,而且真正的构造函数必须被调用。

假设Interface class Person有个具象的派生类 RealPerson,后者提供继承而来的虚函数的实现:

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

有了RealPerson之后,写出Person::create的实现代码:

Person::create(const std::string& name, const Date& birthday, const Address& addr)
{
	return std::tr1::shared_ptr<Person>(new RealPerson(name,birthday,addr));
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章