设计模式---对象创建模式之工厂方法模式(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设计模式理论与实战:简单工厂模 ...
随机推荐
- Spring整合SpringMVC
整合:把在springMVC配置文件中的spring提取出来整合为另一份配置文件 希望: 1).Spring的配置文件只是用来配置和业务逻辑有关的功能(数据源.事务控制.切面....) 2).Spri ...
- 毕业设计心得与整理-APP-主题切换
1.定义主体颜色: 在style自定义了三个属性: <item name="textLight">@android:color/white</item> & ...
- [转帖]Linux内核为大规模支持100Gb/s网卡准备好了吗?并没有
Linux内核为大规模支持100Gb/s网卡准备好了吗?并没有 之前用 千兆的机器 下载速度 一般只能到 50MB 左右 没法更高 万兆的话 可能也就是 200MB左右的速度 很难更高 不知道后续的服 ...
- CentOS 离线安装Gitlab-ce
1. 上gtilab的官网,找了一下安装说明.. 首先安装 依赖的包 sudo yum install -y curl policycoreutils-python openssh-server cr ...
- rsync实现数据同步
希望两台机器指定目录的数据保持一致 192.168.19.252(master) 192.168.19.251(slave) /cache 拉复制 ...
- codeforces1045B
CF1045B 自己瞎鸡巴yy了一下,可知若一个数X不能被表示出来,那么X所有的表示方法都在A集合中,如a1,a2,a3······an-1,an-2中若a1+ai不能被表示出来,那么如果a1到ai是 ...
- BZOJ4516 SDOI2016生成魔咒(后缀数组+平衡树)
一个字符串本质不同的子串数量显然是总子串数减去所有height值.如果一个个往里加字符的话,每次都会改动所有后缀完全没法做.但发现如果从后往前加的话,每次只会添加一个后缀.于是我们把字符串倒过来,每次 ...
- AOP 获取 RequestContextHolder
转载: http://blog.csdn.net/lexang1/article/details/52619215 在使用spring时,经常需要在普通类中获取session,request等对像. ...
- 数据库左右连接on后的限制条件问题
测试环境: MySQL 5.7.19 HeidiSQL 9.3 数据库界面连接工具(挺好用的) 碰到的问题是: Select * from t1 left outer join t2 on t1.id ...
- bzoj 4552 [Tjoi2016&Heoi2016]排序 (二分答案 线段树)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4552 题意: 给你一个1-n的全排列,m次操作,操作由两种:1.将[l,r]升序排序,2 ...