设计模式——0_0 工厂方法(Factory Method)
文章目录
定义
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到了子类
图纸
一个例子:去找一支笔
几乎在所有的面向对象的编程语言里,我们可以通过如下方式新建一个对象:
A a = new A();
行话管这种写法叫 new一个对象 出来
比如,我的程序中有一个用于表示笔
的类簇,就像这样:
Pen 拥有写(write)
和画(draw)
两种能力,用返回值说明绘制的结果是否成功。现在规定FountainPen(钢笔)
要用来写字,而Pencil(铅笔)
要用来画画。
我觉得你也不希望每次 new Pen的对象的时候得翻一下规定,于是我们为 Pen 制定了一个工厂类,就像这样:
public interface BrushPot{
//创建一支笔
Pen createPen();
}
public class WritingPenBrushPot implements BrushPot{
//新建一支用于写字的笔
public Pen createPen(){
return new FountainPen();//新建一支钢笔
}
}
public class BrushBrushPot implements BrushPot{
//新建一支用于画画的笔
public Pen createPen(){
return new Pencil();//新建一支铅笔
}
}
这样一来,我的程序中就再也不会用new的方式去新建一个Pen的子类了,而是通过BrushPot来获取一个Pen的对象。这就实现了对笔创建时的 统一管理。此时,这个 BrushPot 事实上就担任了 Pen 的工厂类。到这里,工厂方法模式的例子其实就已经完成了
但是现在这个例子并没有展示工厂方法存在的意义,调用工厂方法来获取对象和直接new他并没有什么区别,这就意味着每次拿笔还要去买一支新的
作为一个钱镶在肾上的IT民工,这种浪费是可耻的
所以我要先去现有的笔里找一找,就像这样:
//笔筒的单例工厂实现
public interface BrushPot{
//创建一支笔
Pen createPen();
}
public class WritingPenBrushPot implements BrushPot{
private static volatile Pen fountainPen;
//新建一支用于写字的笔
public Pen createPen(){
if(fountainPen == null){
fountainPen = new FountainPen();
}
return fountainPen;//返还钢笔
}
}
public class BrushBrushPot implements BrushPot{
private static volatile Pen pencil;
//新建一支用于画画的笔
public Pen createPen(){
if(pencil == null){
pencil = new Pencil();
}
return pencil;//返还铅笔
}
}
现在,我们实现了整个程序都公用一支写字的钢笔,一支画画用的铅笔。物尽其用,功德圆满。虽然门口收废品的老大爷咬牙切齿,但是问题不大
笔和耐久度
但是我们知道笔这种东西总是不能无限制的使用的
钢笔有墨水,铅笔有笔芯
墨水用完了要加水,铅笔削完了要再买个新的
所以我们的Pen变成了这样:
我们在Pen中加入了durability(耐久度)
的概念,并规定当isExhausted(是否是耗尽)
返还true的时候,对 write 和 draw 的调用都是无效的
而在钢笔中,新增了addInk(添加墨水)
方法,这个方法可以增加当前钢笔对象的耐久度值
于是乎,我们在笔筒中获取笔的时候,则会变成这样:
//笔筒的享元工厂实现
public interface BrushPot{
//创建一支笔
Pen createPen();
}
public class WritingPenBrushPot implements BrushPot{
private static volatile Pen fountainPen;
//新建一支用于写字的笔
public Pen createPen(){
if(fountainPen == null){
fountainPen = new FountainPen();
}else if(fountainPen.isExhausted()){
fountainPen.addInk();//给钢笔添加墨水
}
return fountainPen;//返还钢笔
}
}
public class BrushBrushPot implements BrushPot{
private static volatile Pen pencil;
//新建一支用于画画的笔
public Pen createPen(){
if(pencil == null || pencil.isExhausted()){
pencil = new Pencil();
}
return pencil;//返还铅笔
}
}
对于调用代码来说,他依然是只使用 createWritingPen 和 createBrush 方法去获取写字笔和画笔,不需要因为笔上增加了耐久度做任何响应
这就是对对象的创建进行统一管理的意义,我们可以随意改变产出对象的策略,而调用代码对此一无所知
写在后面的碎碎念
平行类层次
当一个类将他的一些职责委托给另一个独立的类的时候,就产生了平行类层次
——《设计模式》
打个比方——手机和充电器
想必各位道友的手机都不会跟着一个固定的充电器,而是有一个接口用于充电。
而充电又是手机一个必要的功能。
很显然,你的手机将这个功能 委托
给了充电器
于是乎,手机和充电器之间,就形成了一个平行类层次,就像这样:
这跟工厂模式有什么关系?
从 工厂模式 和 去找一支笔 的UML中,我们都可以看到 产品根类
和 工厂根类
之间存在一种逻辑上的关联
而 具体的某个产品和工厂之间,他们存在 1-1 的实际关系(一个工厂只生产一种产品)
那么最后你俯视 产品类簇
和 工厂类簇
之间的关系的时候,就会得出一个类似 手机和充电器 那样的图纸
也就是说这个时候,产品类和工厂类之间,形成了平行类层次
作为平行类层次,产品类委托出去了什么能力呢?
答:产品类将创建自身的能力委托给了工厂方法
静态的工厂方法实现
由于具体的产品和工厂之间是一对一的,那么工厂方法也常常以一种在产品类上的静态方法来实现他;你还可以用这个工厂方法来替代构造方法
比如说现在有一个类叫 A,就像这样:
public class A{}
现在我想要一个专门用于生产 A 的工厂方法,这时是不需要特地在新增一个 AFactory 这样的类来专门生产 A的。而是通过在 A 类型上新增一个 static 类型的方法,来实现工厂方法,就像这样:
public class A{ public static A createA(){ return new A(); } //私有化构造方法,这样一来就没有人可以从外部通过new A的方式创建A了 private A(){} }
为什么要用静态工厂方法代替构造方法
-
静态工厂方法可以自定义方法名,构造方法的名字必须和类名一致
自定义的方法名可以告知调用者很多信息,让看代码的人知道自己在产出什么状态的对象
比如 Java 中的线程池
ExecutorService e1 = Executors.newCachedThreadPool();//缓存型线程池 ExecutorService e2 = Executors.newFixedThreadPool(5);//定长线程池 ExecutorService e3 = Executors.newScheduledThreadPool(5);//周期性线程池 ExecutorService e4 = Executors.newSingleThreadExecutor();//单线程线程池 ……
-
静态工厂方法可以返还多类型的结果,而构造方法只能返还当前类的对象
比如说
- 在用户传入异常的参数值的时候,静态工厂方法可以通过 返还null/其它值 的方式通知调用者,构造方法只能抛出异常
- 静态工厂方法可以根据需要返还当前类的子类对象,而构造方法只能返还当前类的对象
参数化工厂
通过传入不同的参数要求工厂方法返还不同的具体产品
比如说在上文的 笔筒工厂 里面如果还要管理 笔 的 颜色 属性的话,那么在 createWritingPen 和 createBrush 中恐怕就要添加 颜色 作为参数,再根据不同的颜色生成对应的笔了
工厂方法和简单工厂模式
先说结论,在23种基础设计模式里是没有简单工厂模式这个模式的,简单工厂顶多算是一种编程风格
工厂方法已经涵盖了简单工厂在里面,没有必要把简单工厂单独拿出来讲
为什么我们需要工厂模式,是new不好用吗?
有这样几个问题:
- 一个对象应该在什么地方被创建?
- 一个对象应该由谁来创建?
- 一个对象应该如何创建?
- 一个对象应该何时被创建?
怎么回答这样的问题,就是诸如 工厂方法、单例、原型 这样的创造型设计模式
存在的意义
他们负责实例化并管理对象,同时把当前系统所使用的具体信息封装起来。在看不到源码的人眼里,他只是在使用顶层的产品接口
就像我知道我的笔记本电脑里一定有内存条,但是如果我不查,我永远不会知道他用了什么颗粒
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!