分类: C/C++
2012-12-27 01:50:02
创建型设计模式是我们经常需要用到的设计模式之一,顾名思义,这种设计模式是用来创建对象的设计模式。如果你不熟悉,也许会问,我们已经有了构造函数,为什么还需要这种设计模式?为什么我们需要一个中间层专门用来创建对象呢?
我们这里先来设计一个场景,然后来看看为什么我们需要创建型设计模式。
场景:
一般来说,在constructor中,我们有些事情是不可以做的,比如把正在创建的类对象添加到一个global的数据结构中去,比如调用自身的虚函数。如果我们正好遇到这些场景,我们一般会想到利用两段式方法来创建这种对象,在constructor中初步初始化对象成员,然后用另外一个init函数来做进一步的对象初始化,只有在执行完init函数后,这个对象才可以被正确的使用。
那么问题来了,类的用户就必须做两次函数调用才能创建一个对象,这多麻烦,很多用户不愿意这么做,而我们做为设计者,也不应该因为这种原因增加用户的负担。
这样,我们就必须考虑如何解决这个问题。嗯..................
解决方案:
1. 想到了没有,你可以提供一个函数把constructor和init包起来,提供一个统一的接口给用户,这样用户就只需要调用一次就可以得到一个可用的对象了。这.......就是传说中的工厂方法(模式)
2. 问题解决了?似乎是。让我们再来看看,如果你要处理的是一组继承体系的类,你也许想为每个实体类定义一个工厂方法,可以的,你可以使用这种方式。但是好吗?客户端必须预先知道他要创建的对象类型,而且没办法更改。这时,我们想到了简单工厂和抽象工厂。它们俩都可以用来解决这个问题,但是它们俩各有优缺点,我不想在这里讨论它们的优缺点。因为你可以在网上找到很多这方面的文章。
那么我想谈点什么呢?谈点我们在做设计时很容易犯的错误,网上大部分讲述工厂模式的例子都有这样的问题。
经典实现:
class animal {
public:
animal();
virtual ~animal();
};
class dog: public animal{
public:
dog();
~dog();
};
class cat: public animal {
public:
cat();
~cat();
};
class animalFactory{
public:
static animal* buildAnimal(...);
};
我不贴实现了,因为这里主要讲设计方面的东西,这些代码已经足够了。
有谁看出这里的问题?...............................没有问题, 你说没有问题?
是的,从编码的角度说,这些代码没有问题,但是从设计的角度来说,这是一个有问题的设计。
既然你提供了一个工厂给类的使用者来创建这些“animal”,而你又把这些animal的构造函数定义在public域,也就是说,即使你提供了animal factory,用户并不是必须通过它才能得到一个animal 对象。如果你到现在还在说没有问题,我就要说你有问题了。
为了代码的可维护性,一般来说,我们只为某一个功能提供一个入口,比如这里的创建功能。你不能让用户既可以使用工厂创建animal对象,也可以使用constructor创建,这样当你想修改你的创建流程是,你所要付出的代价是以提供功能入口数量的指数增长的。
所以一个好的设计,应该是这样:
class animal {
protected:
animal();
virtual ~animal();
};
class dog: public animal{
friend class animalFactory;
private:
dog();
~dog();
};
class cat: public animal {
friend class animalFactory;
private:
cat();
~cat();
};
class animalFactory{
public:
static animal* buildAnimal(...);
};
看到区别了吗?把你不想用户做的事情,明确的用关键字告诉编译器,让它帮你限制用户的行为。
这样,用户只能用你期望的方式来做事情了。因为你屏蔽了其它的途径。所以编译器是是你的朋友,而且是一个非常好的朋友,你必须和他做朋友。
这里讲述的内容适应于所有的创建型设计模式。