通过“对象创建”模式,绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。他是接口抽象之后的第一步工作
- 典型模式
- Factory Method
- Abstract Factory
- Prototype
- Builder
动机:
- 在软件系统中,经常面临着创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化,
- 如何应对这种变化?如何绕过常规的new,提供一种“封装机制”来避免客户程序和这种“具体对象创建工作”的紧耦合?
代码示例:
文件分割器中的部分代码
class MainForm:public Form
{
public:
void Button1_Click()
{
FileSpliter *spliter = new FileSpliter();
spliter->split();
}
}
上面的New就是和具体类耦合了,没有支持未来的变化,导致后期的扩展很困难。
比如,上面时二进制文件的分割,我们想要支持txt文件的分割,图像文件的分割...
拿大概的代码:
class ISpliter
{
virtual void split() = 0;
}
class BinSpliter : public ISpliter
{
}
class TxtSpliter : public ISpliter
{
}
class PictureSliter : public ISpliter
{
}
class MainForm:public Form
{
public:
void Button1_Click()
{
ISpliter *spliter = new BinSpliter(); // 依赖具体类
spliter->split();
}
}
上述代码中的new之前的ISpliter从具体依赖改变程了抽象依赖,是我们想要的,但是后面的new的具体类,仍然是细节依赖,所以仍然不行,不符合编译时依赖,即上述代码编译时,需要BinSpliter存在才能编译,违背依赖导致原则。
那上面的new是避免不了的,那有什么方法可以替代?
可以使用一种方法来返回一个对象。
VER1:
class SpliterFactory
{
public:
ISpliter* CreateSpliter()
{
return new BinSpliter();
}
};
class MainForm:public Form
{
public:
void Button1_Click()
{
SpliterFactory factory;
ISpliter *spliter = factory.CreateSpliter();
spliter->split();
}
}
那如果按照上面的做法,解决了问题吗?其实没有。
因为SpliterFactory依赖BinSpliter,MainForm依赖SpliterFactory,那MainForm还是会依赖BinSpliter...(编译时依赖)
有什么方法可以将编译时依赖转为运行时依赖:virtual方法!!!
VER2:
class SpliterFactory
{
public:
virtual ISpliter* CreateSpliter() = 0;
};
class MainForm:public Form
{
public:
void Button1_Click()
{
SpliterFactory *factory;
ISpliter *spliter = factory->CreateSpliter();
spliter->split();
}
}
按照上面代码,其实基本理顺了,上面返回的spliter是个多态的创建,现在返回的是ISpliter,真正的创建,交给SpliterFactory的未来。
// 具体类
class BinSpliter : public ISpliter
{
}
class TxtSpliter : public ISpliter
{
}
class PictureSliter : public ISpliter
{
}
// 具体工厂
class BinSpliterFactory : public SpliterFactory
{
virtual ISpliter* CreateSpliter()
{
return new BinSpliter();
}
}
class TxtSpliterFactory : public SpliterFactory
{
virtual ISpliter* CreateSpliter()
{
return new TxtSpliter();
}
}
class PictureSliterFactory : public SpliterFactory
{
virtual ISpliter* CreateSpliter()
{
return new PictureSliter();
}
}
通过上面代码可以看到,每个具体的类,都对应一个具体的工厂,那工厂类怎么初始化呢?
class SpliterFactory
{
public:
virtual ISpliter* CreateSpliter() = 0;
};
class MainForm:public Form
{
public:
void Button1_Click()
{
SpliterFactory *factory; // ????
ISpliter *spliter = factory->CreateSpliter();
spliter->split();
}
}
肯定不能就地New的,一般做法:
class MainForm:public Form
{
SpliterFactory *factory;
public:
MainForm(SpliterFactory *factory)
: this->factory = factory;
{
}
void Button1_Click()
{
ISpliter *spliter = factory->CreateSpliter(); // 多台New
spliter->split();
}
}
一定会有人问,虽然这个位置没有具体去创建具体的factory,那在别的位置,不还是要创建,那不是还是会产生依赖?
答案:会产生依赖
但是,我们当前考虑的是对MainForm的影响,会不会产生依赖,通过上面的修改,Mainform是没有具体类的依赖的。
我们并不是要把变化(对具体类的依赖)给消灭掉,而是将它赶到某个局部的地方,比如将一只猫关到笼子里,而不是让它在真个房间跳来跳去。
模式定义
定义一个用于创建对象的接口(工厂类创建的虚接口),让子类(具体工厂类)决定实例化哪一个类。FactoryMethod使得一个类的示例化延迟(目的:解耦,手段:虚函数)到子类
--《设计模式》GOF
结构
MainForm依赖的是稳定的部分,而不是变化的部分。
要点总结
- Factory Method模式用于隔离类对象的使用者和具体类型之前的耦合关系。面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱
- Factory Method模式通过面向对象的手段,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好的解决了这种紧耦合的关系。
- Factory Method模式解决“单个对象”的需求变化,缺点在于要求创建方法/参数相同