5. “对象创建”类模式
通过“对象创建”类模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。
5.1 工厂方法
动机:
在软件系统中,经常面临着创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化。
如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“具体对象创建工作”的紧耦合?
代码示例:
仍然考虑文件分割器案例,不考虑与创建对象无关的代码部分,暂时忽略内存管理,专注于工厂方法模式的应用。
在实现代码1中,不同类型的文件分割器类(File,Pic,Video等)继承文件分割器抽象基类。
在MainForm中,MainForm类依赖(编译时依赖)了具体的类BinarySpitter,违背了依赖倒置原则。此时即出现了动机中说明的创建对象过程中导致的紧耦合。
采用工厂方法模式的解决方案见代码2,考虑过程如下:
1.利用new方法创建的对象必须依赖于具体类,不可取;所以考虑创建一个类,利用其中方法的返回值作为创建对象的结果;
2.然而单纯的返回一个具体类的对象仍然没有解决依赖倒置的问题,所以考虑将该类设计为一个抽象类,具体的返回由其子类来确定。
3.依照上述思想创建工厂基类SplitterFactory(抽象类),具体工厂(BinarySplitterFactory等)返回具体的对象。
4.MainForm使用过程中,声明一个工厂字段,在具体创建对象过程中采用统一的依赖于抽象的创建方法,即
ISplitter * splitter= factory->CreateSplitter(); //相当于多态new
而factory具体指向的对象类型由MainForm构造函数通过外界传入。
注:可以从上述分析和代码中看出,工厂方法模式的使用,并不是为了消除变化(事实上变化不可能消除),而是将变化排除在MainForm之外(类比将变化赶到一个小范围),使其更加可控,从而使文件分割器的设计和使用满足面向对象设计原则,在应对变化时表现出优势。
1 //FileSpiltter1.cpp 2 class ISplitter{ 3 public: 4 virtual void split()=0; 5 virtual ~ISplitter(){} 6 }; 7 8 class BinarySplitter : public ISplitter{ 9 10 }; 11 12 class TxtSplitter: public ISplitter{ 13 14 }; 15 16 class PictureSplitter: public ISplitter{ 17 18 }; 19 20 class VideoSplitter: public ISplitter{ 21 22 }; 23 24 //MaiForm1.cpp 25 class MainForm : public Form 26 { 27 TextBox* txtFilePath; 28 TextBox* txtFileNumber; 29 ProgressBar* progressBar; 30 31 public: 32 void Button1_Click(){ 33 34 35 36 ISplitter * splitter= 37 new BinarySplitter();//依赖具体类 38 39 splitter->split(); 40 41 } 42 };
1 //ISpiltterFactory.cpp 2 3 //抽象类 4 class ISplitter{ 5 public: 6 virtual void split()=0; 7 virtual ~ISplitter(){} 8 }; 9 10 11 //工厂基类 12 class SplitterFactory{ 13 public: 14 virtual ISplitter* CreateSplitter()=0; 15 virtual ~SplitterFactory(){} 16 }; 17 18 //FileSpiltter2.cpp 19 20 //具体类 21 class BinarySplitter : public ISplitter{ 22 23 }; 24 25 class TxtSplitter: public ISplitter{ 26 27 }; 28 29 class PictureSplitter: public ISplitter{ 30 31 }; 32 33 class VideoSplitter: public ISplitter{ 34 35 }; 36 37 //具体工厂 38 class BinarySplitterFactory: public SplitterFactory{ 39 public: 40 virtual ISplitter* CreateSplitter(){ 41 return new BinarySplitter(); 42 } 43 }; 44 45 class TxtSplitterFactory: public SplitterFactory{ 46 public: 47 virtual ISplitter* CreateSplitter(){ 48 return new TxtSplitter(); 49 } 50 }; 51 52 class PictureSplitterFactory: public SplitterFactory{ 53 public: 54 virtual ISplitter* CreateSplitter(){ 55 return new PictureSplitter(); 56 } 57 }; 58 59 class VideoSplitterFactory: public SplitterFactory{ 60 public: 61 virtual ISplitter* CreateSplitter(){ 62 return new VideoSplitter(); 63 } 64 }; 65 66 //MainForm2.cpp 67 class MainForm : public Form 68 { 69 SplitterFactory* factory;//工厂 70 71 public: 72 73 MainForm(SplitterFactory* factory){ 74 this->factory=factory; 75 } 76 77 void Button1_Click(){ 78 79 80 ISplitter * splitter= 81 factory->CreateSplitter(); //多态new 82 83 splitter->split(); 84 85 } 86 };
模式定义:
定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使得一个类的实例化延迟(目的:解耦 ,手段:虚函数)到子类。
类图:
总结:
Factory Method模式用于隔离类对象的使用者与具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱。
Factory Method模式通过面向对象的手法,将所有创建的具体对象延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
Factory Method模式解决“单个对象”的需求变化。缺点在于要求创建方法/参数相同。