设计模式之工厂模式

设计模式之工厂模式,第1张

目录
  • 前言
  • Factor Method
    • 1.模式动机
    • 2.示例
    • 3.模式定义
    • 4.模式结构
    • 5.要点总结

前言

在学习侯捷老师的有关设计模式的课程(李建忠老师主讲)中,老师对23种设计模式的有自己的划分,如下。所以老师讲解是按照这种顺序讲解。

对象创建:

  • 通过“对象创建” 模式绕开new,来避免对象创建(new)过程
    中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它
    是接口抽象之后的第一步工作。
Factor Method 1.模式动机
  • 在软件系统中,经常面临着创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化。

白话点说就是可能在客户程序的某一步,我们需要new某个类,但是不同的使用场景,这个类会有某些变化比如这次是苹果,下次可能是梨。

还是有点抽象,请看下面的例子:

class Apple
{
};
class MainForm
{
public:
	void Button1_Click()//点击图形界面上的分割按钮时的回调函数
	{
		//...其他事情
		Apple* apple = new Apple;//或者Apple apple;
		//...其他事情
	}
};

这里的Apple* apple = new Apple;就是上述描述的new某个对象。试想一下,一旦后面我不是做Apple ,而是做比如Pear 呢?

这就是客户程序和“具体对象创建工作”的紧耦合,我们需要避免它,用一种动态的new *** 作的方式。请看下面的实例。

2.示例

下面我们以介绍观察者模式时的一个文件分割进度显示的例子为背景,介绍Factor Method的使用场景。先给代码,如下:

注:为了能聚焦要介绍的Factor Method,下面只给出文件分割进度显示的部分代码,并把观察者模式的使用痕迹删除(可以不用去看或理解观察者模式,若想了解,可参考设计模式之观察者模式(Observer))。

#include 
#include 
#include
using namespace std;

//文件分割类
class FileSplitter
{
private:
	string m_filePath;
	int m_fileNumber;
public:
	FileSplitter(const string& filePath, int fileNumber) 
		:m_filePath(filePath),m_fileNumber(fileNumber)
	{}

	void split()
	{
		//1.读取大文件
		cout << "读取文件大小....." << endl;

		//2.分批次向小文件中写入
		cout << "拆分文件,写入小文件大小....." << endl;
		for (int i = 0; i < m_fileNumber; i++){
			float progressValue = m_fileNumber;
			progressValue = (i + 1) / progressValue;
			cout << "拆分成功" << progressValue << endl;
		}
	}
};
//模拟窗口程序
class MainForm
{
	string filePath;
	int FileNumber;
public:
	void Button1_Click(){//点击图形界面上的分割按钮时的回调函数

		string filePath = "/C::";//模拟文件路径
		int FileNumber = 10;//模拟文件数量

		FileSplitter splitter(filePath, FileNumber);//1.生成文件分割器

		//2.开始分割文件
		splitter.split();//分割文件
	}
};

int main()
{
	MainForm mf;//生成窗口
	mf.Button1_Click();//模拟窗口点击

	system("pause");
	return 0;
}

以上面的程序为例,它很简单,就是模拟了一个窗口程序,当点击窗口上的分割文件按钮时,会调用Button1_Click()函数。
在Button1_Click()函数中,我们必定要生成一个文件分割FileSplitter 类的对象,然后使用其splite()成员函数分割文件。

我们下面介绍的Factor Method就是针对FileSplitter 类对象的生成展开。

在例子中,我们**静态**的生成了一个对象FileSplitter splitter(filePath, FileNumber)。这就意味着扩展没有了,是死的。如果将来我做的文件分割是另外一种拆分方式,势必就需要修改FileSplitter类的splite()方法,这不符合开闭原则。

为了解决这个问题,我们很容易想到使用多态,代码如下,这里把之前的FileSplitter类更名为BinarySplitter

//分割文件接口
class ISplitter{
public:
	virtual void split() = 0;
	virtual ~ISplitter(){}
};

class BinarySplitter : public ISplitter
{
private:
	string m_filePath;
	int m_fileNumber;
public:
	BinarySplitter(){}
	BinarySplitter(const string& filePath, int fileNumber)
		:m_filePath(filePath), m_fileNumber(fileNumber)
	{}

	virtual void split()
	{
		//1.读取大文件
		cout << "读取文件大小....." << endl;

		//2.分批次向小文件中写入
		cout << "拆分文件,写入小文件大小....." << endl;
		for (int i = 0; i < m_fileNumber; i++){
			float progressValue = m_fileNumber;
			progressValue = (i + 1) / progressValue;
			cout << "拆分成功" << progressValue << endl;
		}
	}
};

class TxtSplitter : public ISplitter{
public:
	virtual void split()
	{
		cout << "文本分割" << endl;
	}
};

class PictureSplitter : public ISplitter{
public:
	virtual void split()
	{
		cout << "图片分割" << endl;
	}
};

class VideoSplitter : public ISplitter{
public:
	virtual void split()
	{
		cout << "视频分割" << endl;
	}
};

这样的话,下面两行就可以替换。

原来的如下:

FileSplitter splitter(filePath, FileNumber);//1.生成文件分割器
splitter.split();//分割文件

替换后:

ISplitter* splitter = new BinarySplitter(filePath, FileNumber);//1.生成文件分割器
splitter->split();//分割文件

但是还没有解决问题,因为new BinarySplitter存在,这还是**静态**的。

Factor Method就可以解决这个问题,通过工厂模式来返回具体的文件分割类(如BinarySplitter)对象。代码如下:

class SplitterIFactor//工厂基类
{
public:
	virtual ISplitter* creatSplitter() = 0;//返回值要接收具体的文件分割类
	virtual ~SplitterIFactor(){}
};
//下面是具体文件分割类的工厂
class BinarySplitterFactor : public SplitterIFactor
{
public:
	virtual ISplitter* creatSplitter()
	{
		return new BinarySplitter;
	}
};
class TxtSplitterFactor : public SplitterIFactor
{
public:
	virtual ISplitter* creatSplitter()
	{
		return new TxtSplitter;
	}
};
class PictureSplitterFactor : public SplitterIFactor
{
public:
	virtual ISplitter* creatSplitter()
	{
		return new PictureSplitter;
	}
};
class VideoSplitterFactor : public SplitterIFactor
{
public:
	virtual ISplitter* creatSplitter()
	{
		return new VideoSplitter;
	}
};

这样上面就可替换成:

SplitterIFactor* sIf;//作为一个数据成员
ISplitter* splitter = sIf->creatSplitter();//1.生成文件分割器,动态,到底是那种分割器在创建窗口MainForm时确认
//ISplitter* splitter = new BinarySplitter(filePath, FileNumber);//1.生成文件分割器,还是静态

这里说明一下,我们一般把工厂基类SplitterIFactor* sIf作为类(指MainForm)的数据成员,然后在生成类时初始化它。

完整的代码如下:

#include 
#include 
#include
using namespace std;

//分割文件接口
class ISplitter{
public:
	virtual void split() = 0;
	virtual ~ISplitter(){}
};

class BinarySplitter : public ISplitter
{
private:
	string m_filePath;
	int m_fileNumber;
public:
	BinarySplitter(){}
	BinarySplitter(const string& filePath, int fileNumber)
		:m_filePath(filePath), m_fileNumber(fileNumber)
	{}

	virtual void split()
	{
		//1.读取大文件
		cout << "读取文件大小....." << endl;

		//2.分批次向小文件中写入
		cout << "拆分文件,写入小文件大小....." << endl;
		for (int i = 0; i < m_fileNumber; i++){
			float progressValue = m_fileNumber;
			progressValue = (i + 1) / progressValue;
			cout << "拆分成功" << progressValue << endl;
		}
	}
};

class TxtSplitter : public ISplitter{
public:
	virtual void split()
	{
		cout << "文本分割" << endl;
	}
};

class PictureSplitter : public ISplitter{
public:
	virtual void split()
	{
		cout << "图片分割" << endl;
	}
};

class VideoSplitter : public ISplitter{
public:
	virtual void split()
	{
		cout << "视频分割" << endl;
	}
};

/***************工厂模式介入*****************/
class SplitterIFactor//工厂基类
{
public:
	virtual ISplitter* creatSplitter() = 0;
	virtual ~SplitterIFactor(){}
};

class BinarySplitterFactor : public SplitterIFactor
{	
public:
	virtual ISplitter* creatSplitter()
	{
		return new BinarySplitter();
	}
};
class TxtSplitterFactor : public SplitterIFactor
{
public:
	virtual ISplitter* creatSplitter()
	{
		return new TxtSplitter;
	}
};
class PictureSplitterFactor : public SplitterIFactor
{
public:
	virtual ISplitter* creatSplitter()
	{
		return new PictureSplitter;
	}
};
class VideoSplitterFactor : public SplitterIFactor
{
public:
	virtual ISplitter* creatSplitter()
	{
		return new VideoSplitter;
	}
};
/****************************************/


//模拟窗口程序
class MainForm
{
	string filePath;
	int FileNumber;
	SplitterIFactor* sIf;//分割器工厂基类
public:
	MainForm(SplitterIFactor* _sIf) :sIf(_sIf){}

	void Button1_Click(){//点击图形界面上的分割按钮时的回调函数

		string filePath = "/C::";//模拟文件路径
		int FileNumber = 10;//模拟文件数量


		//ISplitter* splitter = new BinarySplitter(filePath, FileNumber);//1.生成文件分割器,还是静态
		ISplitter* splitter = sIf->creatSplitter();//1.生成文件分割器,动态,到底是那种分割器在创建窗口MainForm时确认

		//2.开始分割文件
		splitter->split();//分割文件
	}
};


int main()
{
	MainForm mf(new PictureSplitterFactor);//生成窗口
	mf.Button1_Click();//模拟窗口点击

	system("pause");
	return 0;
}
3.模式定义

  • “创建对象的接口”如例子中的SplitterIFactor类;
  • “类的实例化延迟”指的是我在做 *** 作ISplitter* splitter = sIf->creatSplitter();时,并没有指定具体使用哪个文件分割器,而是在生成窗口MainForm mf(new PictureSplitterFactor);时指定用哪种文件分割器。
4.模式结构


例子中的MainForm 就是依赖结构中的ProductCreator。其中,ISplitter就是ProductSplitterIFactor就是Creator

5.要点总结
  1. Factory Method模式用于隔离类对象的使用者(MainForm)和具体类型(BinarySplitter、TxtSplitter、PictureSplitter、VideoSplitter)之间的耦合关系(直接用new创建具体类型)。
  2. Factory Method模式解决耦合关系的方法是通过面向对象的手法,将所要创建的具体对象工作延迟到子类(对应的4种工厂BinarySplitterFactor、TxtSplitterFactor 、PictureSplitterFactor 、VideoSplitterFactor ),从而实现一种扩展(而非更改)的策略。
  3. Factory Method模式解决“单个对象”的需求变化。缺点在于要求创建方法/参数相同(比如示例中的工厂创建方法creatSplitter(),就要求4种具体工厂的creatSplitter的参数完全相同)。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/873197.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存