设计模式---对象创建模式之工厂方法模式(Factory Method)
前提:“对象创建”模式
通过“对象创建”模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。
典型模式(表现最为突出)
工厂方法模式:Factory Method
抽象工厂模式:Abstract Factory
原型模式:Prototype
创建者模式:Builder
一:工厂方法模式
(一)概念
工厂方法模式又被称为多态工厂模式 。工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。
核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。
(二)动机
在软件系统中,经常面临这创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化。
如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“具体对象创建工作”的紧耦合。
(三)代码讲解(同文件分割)问题提出
1.原代码
class FileSplitter
{
public:
void split(){
//...
}
};
class MainForm : public Form
{
TextBox* txtFilePath; //文件路径
TextBox* txtFileNumber; //希望分割的个数
ProgressBar* progressBar; public:
void Button1_Click(){
//收集到用户输入的参数信息
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());
//传递给FileSplitter,让该类去分割文件
FileSplitter splitter(filePath, number, progressBar);
//进行分割
splitter.split(); }
};
动态看待问题,上面使用了具体细节类,是静态特质,定死了,我们应该去判断业务有没有需求的变化,是不是只需要文件分割。比如我们这里有变化,支持二进制,图片,视频,文本分割...,此时我们应该将他声明为抽象基类来使用
2.改进为抽象基类
class ISplitter{
public:
virtual void split()=;
virtual ~ISplitter(){}
};
class BinarySplitter : public ISplitter{
};
class TxtSplitter: public ISplitter{
};
class PictureSplitter: public ISplitter{
};
class VideoSplitter: public ISplitter{
};
class MainForm : public Form
{
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar; public:
void Button1_Click(){ ISplitter * splitter=
new BinarySplitter();//依赖具体类 splitter->split(); }
};
依赖倒置原则:应该去依赖抽象,而不是依赖实现细节
ISplitter * splitter= //依赖抽象
new BinarySplitter();//依赖具体类,依赖细节
代码当中哪怕只出现一处细节依赖,其他都是抽象依赖也解决不了问题,将该依赖倒置原则打破了,所以在编译时还是要依赖BinarySplitter才能编译通过
通过“对象创建”模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。
(三)代码讲解(同文件分割)问题解决
1.改进一
我们可以使用一个方法来返回一个对象,避免直接使用new创建
class SplitterFactory : public ISplitter{
public:
ISplitter* CreateSplitter()
{
return new BinarySplitter(); //这里还是个依赖(都是编译时)
}
};
class MainForm : public Form
{
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar; public:
void Button1_Click(){ SplitterFatory factory; //这里是个依赖
ISplitter * splitter=
factory.CreateSplitter();
splitter->split(); }
};
没有从根本解决问题,间接依赖关系,还是依赖,没有绕开这个问题。
我们可以将编译时依赖转运行时依赖,如何做:找虚函数
2.改进二:使用虚函数
class ISplitter{
public:
virtual void split()=;
virtual ~ISplitter(){}
virtaul ISplitter* CreateSplitter()=;
};
SplitterFatory* factory; //未来?从哪来?看下面3,可以是下面具体工厂
ISplitter * splitter=
factory->CreateSplitter(); //交给未来
3改进三:将工厂基类和抽象类解耦
//工厂基类
class SplitterFactory{
public:
virtual ISplitter* CreateSplitter()=;
virtual ~SplitterFactory(){}
};
4.根据具体类创建一些类的具体工厂
//具体类
class BinarySplitter : public ISplitter{ }; class TxtSplitter: public ISplitter{ }; class PictureSplitter: public ISplitter{ }; class VideoSplitter: public ISplitter{ }; //具体工厂
class BinarySplitterFactory: public SplitterFactory{
public:
virtual ISplitter* CreateSplitter(){
return new BinarySplitter();
}
}; class TxtSplitterFactory: public SplitterFactory{
public:
virtual ISplitter* CreateSplitter(){
return new TxtSplitter();
}
}; class PictureSplitterFactory: public SplitterFactory{
public:
virtual ISplitter* CreateSplitter(){
return new PictureSplitter();
}
}; class VideoSplitterFactory: public SplitterFactory{
public:
virtual ISplitter* CreateSplitter(){
return new VideoSplitter();
}
};
每个具体的类都有对应的具体工厂,所以我们上面的未来就有着落了
5.改进四:将未来变为字段,可以实现构造从外界传递数据修改未来
class MainForm : public Form
{
SplitterFactory* factory;//工厂,抽象基类 public: MainForm(SplitterFactory* factory){
this->factory=factory;
} void Button1_Click(){ ISplitter * splitter=
factory->CreateSplitter(); //多态new,通过虚函数 splitter->split(); }
};
MainForm 没有具体类的依赖了,将变化赶出去到每一个局部区域
5.程序调用:将变化赶到调用处
void proceed()
{
TxtSplitterFactory* fact=new TxtSplitterFactory();
MainForm* mf =new MainForm(fact);
}
(四)代码分析
class MainForm : public Form
{
SplitterFactory* factory;//工厂 public: MainForm(SplitterFactory* factory){
this->factory=factory;
} void Button1_Click(){ ISplitter * splitter=
factory->CreateSplitter(); //多态new splitter->split(); }
};
MainForm
//抽象类
class ISplitter{
public:
virtual void split()=;
virtual ~ISplitter(){}
}; //工厂基类
class SplitterFactory{
public:
virtual ISplitter* CreateSplitter()=;
virtual ~SplitterFactory(){}
};
抽象类和工厂基类
//具体类
class BinarySplitter : public ISplitter{ }; class TxtSplitter: public ISplitter{ }; class PictureSplitter: public ISplitter{ }; class VideoSplitter: public ISplitter{ }; //具体工厂
class BinarySplitterFactory: public SplitterFactory{
public:
virtual ISplitter* CreateSplitter(){
return new BinarySplitter();
}
}; class TxtSplitterFactory: public SplitterFactory{
public:
virtual ISplitter* CreateSplitter(){
return new TxtSplitter();
}
}; class PictureSplitterFactory: public SplitterFactory{
public:
virtual ISplitter* CreateSplitter(){
return new PictureSplitter();
}
}; class VideoSplitterFactory: public SplitterFactory{
public:
virtual ISplitter* CreateSplitter(){
return new VideoSplitter();
}
};
具体类和具体工厂
依赖关系:
MainForm只依赖抽象类和工厂基类,抽象的
而不去依赖具体类和具体工厂,这些具体实例会被隔离到初始化方法中,像是main方法
(五)模式定义
定义一个用于创建对象的接口,让子类决定实例化哪一个类。
Factory Method使得一个类的实例化延迟(目的:解耦,手段:虚函数)到子类。
--《设计模式》Gof
(六)类图(结构)

(七)要点总结
1.Factory Method 模式用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱。
2.Factory Method模式通过面向对象的手法(多态),将所要创建的对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
原来我们需要不断修改new对象:更改
ISplitter * splitter=
new BinarySplitter();//依赖具体类
现在我们只需要扩展子类和子类工厂即可,而MainForm不动,是稳定的:不更改可扩展
ISplitter * splitter=
factory->CreateSplitter(); //多态new,通过虚函数
3.Factory Method模式解决“单个对象”的需求变化。缺点在于要求创建方法/参数相同。
在软件中,可以通过将new封装到工厂类的create函数中,并且将工厂类抽象出一个基类,实现new的多态性。这样可以保证对象构建函数的复用,实现将变化集中的目的。
(八)案例实现:工厂生产水果
1.抽象类和抽象工厂实现
class Fruit
{
public:
virtual void sayName() = ;
}; class FruitFactory
{
public:
virtual Fruit* getFruit() = ;
};
2.具体类和具体工厂实现
class Apple :public Fruit
{
public:
void sayName()
{
cout << "an apple you get" << endl;
}
}; class Banana :public Fruit
{
public:
void sayName()
{
cout << "an banana you get" << endl;
}
}; class AppleFactory :public FruitFactory
{
public:
virtual Fruit* getFruit()
{
return new Apple();
}
}; class BananaFractory :public FruitFactory
{
public:
virtual Fruit* getFruit()
{
return new Banana();
}
};
3.结果测试
void main()
{
Fruit* f = NULL;
FruitFactory* ff = new AppleFactory();
f = ff->getFruit();
f->sayName();
system("pause");
return;
}

设计模式---对象创建模式之工厂方法模式(Factory Method)的更多相关文章
- PYTHON设计模式,创建型之工厂方法模式
我感觉和上一个差不多,可能不要动最要的地方吧... #!/usr/bin/evn python #coding:utf8 class Pizza(object): def prepare(self, ...
- 浅谈C++设计模式之工厂方法(Factory Method)
为什么要用设计模式?根本原因是为了代码复用,增加可维护性. 面向对象设计坚持的原则:开闭原则(Open Closed Principle,OCP).里氏代换原则(Liskov Substitution ...
- Java设计模式(2)——创建型模式之工厂方法模式(Factory Method)
一.概述 上一节[简单工厂模式]介绍了通过工厂创建对象以及简单的利弊分析:这一节来看看工厂方法模式对类的创建 工厂方法模式: 工厂方法与简单工厂的不同,主要体现在简单工厂的缺点的改进: 工厂类不再负责 ...
- JAVA设计模式(01):创建型-工厂模式【工厂方法模式】(Factory Method)
简单工厂模式尽管简单,但存在一个非常严重的问题.当系统中须要引入新产品时,因为静态工厂方法通过所传入參数的不同来创建不同的产品,这必然要改动工厂类的源码,将违背"开闭原则".怎样实 ...
- 设计模式学习之工厂方法(Factory Method,创建型模式)(2)
接着上一讲中的简单工厂继续讲解,假如我们有了需要采集新的水果梨子,如果我们使用简单工厂中的方式的话,就会新增一个Pear类,然后实现Fruit类,然后修改FruitFactory类中获取实例的方法 g ...
- 设计模式的征途—3.工厂方法(Factory Method)模式
上一篇的简单工厂模式虽然简单,但是存在一个很严重的问题:当系统中需要引入新产品时,由于静态工厂方法通过所传入参数的不同来创建不同的产品,这必定要修改工厂类的源代码,将违背开闭原则.如何实现新增新产品而 ...
- Java设计模式之【工厂模式】(简单工厂模式,工厂方法模式,抽象工厂模式)
Java设计模式之[工厂模式](简单工厂模式,工厂方法模式,抽象工厂模式) 工厂模式出现的原因 在java中,创建一个对象最简单的方法就是使用new关键字.但在一些复杂的业务逻辑中,创建一个对象不只需 ...
- Java设计模式学习记录-简单工厂模式、工厂方法模式
前言 之前介绍了设计模式的原则和分类等概述.今天开启设计模式的学习,首先要介绍的就是工厂模式,在介绍工厂模式前会先介绍一下简单工厂模式,这样由浅入深来介绍. 简单工厂模式 做法:创建一个工厂(方法或类 ...
- Javascript设计模式理论与实战:工厂方法模式
本文从简单工厂模式的缺点说起,引入工厂方法模式,介绍的工厂方法模式的基本知识,实现要点和应用场景,最后举例进行说明工厂方法模式的应用.在之前的<Javascript设计模式理论与实战:简单工厂模 ...
随机推荐
- [转] Linux有问必答:如何修复“sshd error: could not load host key”
编译自:http://ask.xmodulo.com/sshd-error-could-not-load-host-key.html作者: GOLinux 本文地址:https://linux.cn/ ...
- MySQL存储和获取数据
---恢复内容开始--- 一.MySQL存储引擎? 1.引擎定义 存储引擎其实就是如何存取数据,如何为存储的数据建立索引和如何更新.查询数据等技术的实现方法.因为在关系型数据苦中数据是以表格的形式,所 ...
- SLAM中的变换(旋转与位移)表示方法
1.旋转矩阵 注:旋转矩阵标题下涉及到的SLAM均不包含位移. 根据同一点P在不同坐标系下e(e1,e2,e3)e'(e1',e2',e3')的坐标a(a1,a2,a3)a'(a1',a2',a3') ...
- 解决因为本地代码和远程代码冲突,导致git pull无法拉取远程代码的问题
一.问题 当本地代码和远程代码有冲突的时候,执行git pull操作的时候,会提示有冲突,然后直接终止本次pull,查了些资料没有找到强制pull的方式,但是可以使用如下方式解决. 二.解决思路 可以 ...
- laravel 在nginx服务器上除了首页其余都是404的问题
nginx对应站点的.conf配置文件添加如下代码 location / { try_files $uri $uri/ /index.php$is_args$query_string; #语法: tr ...
- 关于python requests 包跑ssl的设置 和 charles相关抓包的问题
由于在测试服务器上测试东西都是https,然后最近又在和大神一起开发openapi,api写好当然是要测试的 python上测试接口最好用的莫过于requests模块了.但是 我还从来没有用reque ...
- 设计模式笔记:策略模式(Strategy)
1. 策略模式简介 1.1 定义 策略是为达到某一目的而采取的手段或方法,策略模式的本质是目标与手段的分离,手段不同而最终达成的目标一致.客户只关心目标而不在意具体的实现方法,实现方法要根据具体的环境 ...
- 信息安全与Linux系统
相信很多小伙伴都看过黑客帝国里面的那些由代码组成的神奇界面,也有很多人也向往着有一天能做一个黑客,当然不是为了做坏事,只是想和电影里面的黑客一样拉风,我就是这么其中一个(假如有一天能实现这个愿望我想我 ...
- BZOJ5018[Snoi2017]英雄联盟——DP
题目描述 正在上大学的小皮球热爱英雄联盟这款游戏,而且打的很菜,被网友们戏称为「小学生」.现在,小皮球终于受不 了网友们的嘲讽,决定变强了,他变强的方法就是:买皮肤!小皮球只会玩N个英雄,因此,他也只 ...
- VMWare 安装 Linux
参考 : http://www.aboutyun.com/thread-6780-1-1.html 这的是很详细.赞一下 我这里就简化一下. 1 下载: VMWare : https://d ...