工厂模式之工厂方法模式思疑自释
在学习简单工厂模式和工厂方法模式时,我如同大多数朋友一样发出了以下质疑:
- 工厂方法模式真的解决了简单工厂中违背开闭原则(OCP)的缺点?
- 继承工厂父类或实现了工厂接口的子类,在后期新增的时候,使用方不还是需要修改代码,才能新增一个种类?
经过一番思考后,我决定用一个比较梦幻的例子来说明我对工厂方法模式的理解。希望通过这个例子,能够给大家带来一些启发!
工厂方法模式之梦幻的猫工厂
我们都知道,无论什么东西,放久了都会长猫(伪)( ̄y▽ ̄)*捂嘴偷笑
其实每一个会长猫的东西,都是继承了梦幻猫工厂的力量(伪)( ̄y▽ ̄)*继续捂嘴偷笑(下次不笑了,笑的太丑会被打!)
先见识一下梦幻的猫工厂:
public interface CatFactory {
Cat growCat();
}
public abstract CatFactory {
abstract Cat growCat();
}
长得并不梦幻,但重要的是它有梦幻的力量:长猫。
继承这种梦幻能力的圣物种类当然不多,但众所周知,猫盒子和猫架子继承了这种力量。
梦幻的猫盒子:
public class CatBox implements CatFactory{
public CatBox(){
System.out.println("您摆了一个猫盒子~很久以后~");
}
@Override
public Cat growCat() {
return new Ragdoll();
}
}
梦幻的猫架子:
public class CatShelf implements CatFactory {
public CatShelf(){
System.out.println("您放了一件猫架子~很久以后~");
}
@Override
public Cat growCat() {
return new NorwegianForestCat();
}
}
布偶猫和森林猫,它们都是喵星人~
喵星人种族:
public abstract class Cat {
public abstract void mewing();
public abstract void purring();
}
布偶猫:
public class Ragdoll extends Cat {
public Ragdoll(){
System.out.println("叮叮叮~您收获了一只小仙女布偶猫!");
}
@Override
public void mewing() {
System.out.println("小仙女一直很安静,偶尔喵喵喵~");
}
@Override
public void purring() {
System.out.println("小仙女一直很安静,偶尔呼呼呼~");
}
}
森林猫:
public class NorwegianForestCat extends Cat {
public NorwegianForestCat(){
System.out.println("咚咚咚~您收获了一只挪威妖精森林猫!");
}
@Override
public void mewing() {
System.out.println("森林里的妖精,发出喵喵的空音~");
}
@Override
public void purring() {
System.out.println("外表是妖精,打呼却如同中年大叔一般,呼噜呼噜~");
}
}
某一天,我在野外打Boss,掉落了圣物猫盒子,并将其带回了家,摆在家中:
public class MineCat {
public static void main(String[] args) {
CatFactory catFactory = new CatBox();
Cat cat = catFactory.growCat();
cat.mewing();
cat.purring();
}
然后:
您摆了一个猫盒子~很久以后~
叮叮叮~您收获了一只小仙女布偶猫!
小仙女一直很安静,偶尔喵喵喵~
小仙女一直很安静,偶尔呼呼呼~
又一天,我在地下城打Boss,掉落了圣物猫架子子,并将其带回了家,放置在家中:
public class MineCat {
public static void main(String[] args) {
CatFactory catFactory = new CatBox();
Cat cat = catFactory.growCat();
cat.mewing();
cat.purring();
System.out.println("\n-------------这只是一个分割线-----------\n");
catFactory = new CatShelf();
cat = catFactory.growCat();
cat.mewing();
cat.purring();
}
}
然后:
您摆了一个猫盒子~很久以后~
叮叮叮~您收获了一只小仙女布偶猫!
小仙女一直很安静,偶尔喵喵喵~
小仙女一直很安静,偶尔呼呼呼~
-------------这只是一个分割线-----------
您放了一件猫架子~很久以后~
咚咚咚~您收获了一只挪威妖精森林猫!
森林里的妖精,发出喵喵的空音~
外表是妖精,打呼却如同中年大叔一般,呼噜呼噜~
-------------这只是一个分割线-----------
从上面的两件圣物可以看出,梦幻猫工厂的能力本身,是不知道会长出什么猫的,也不会去长猫。
猫的实例化被推迟到了,继承了梦幻猫工厂能力的猫盒子和猫架子上。
如此这般,便完成了工厂方法模式的实现。
但是,有一天,我在天空城又打到了一件具有梦幻猫工厂能力的圣物:猫树苗。
而猫树苗的能力是长出狸花猫。
梦幻的猫树苗:
public class CatTree implements CatFactory {
public CatTree(){
System.out.println("您种了一棵猫树苗~很久以后~");
}
@Override
public Cat growCat() {
return new DragenLi();
}
}
狸花猫:
public class DragenLi extends Cat {
public DragenLi(){
System.out.println("哒哒哒~您收获了一只中华龙狸狸花猫!");
}
@Override
public void mewing() {
System.out.println("虽然被叫做龙狸,但撒娇时还是喵喵喵~");
}
@Override
public void purring() {
System.out.println("打呼的呼噜声,才发现不大的身体内隐藏着龙魂,吼呼呼~吼呼呼~");
}
}
这时候,我还是将猫树苗带回了家:
public class MineCat {
public static void main(String[] args) {
CatFactory catFactory = new CatBox();
Cat cat = catFactory.growCat();
cat.mewing();
cat.purring();
System.out.println("\n-------------这只是一个分割线-----------\n");
catFactory = new CatShelf();
cat = catFactory.growCat();
cat.mewing();
cat.purring();
System.out.println("\n-------------这只是一个分割线-----------\n");
catFactory = new CatTree();
cat = catFactory.growCat();
cat.mewing();
cat.purring();
}
}
然后:
您摆了一个猫盒子~很久以后~
叮叮叮~您收获了一只小仙女布偶猫!
小仙女一直很安静,偶尔喵喵喵~
小仙女一直很安静,偶尔呼呼呼~
-------------这只是一个分割线-----------
您放了一件猫架子~很久以后~
咚咚咚~您收获了一只挪威妖精森林猫!
森林里的妖精,发出喵喵的空音~
外表是妖精,打呼却如同中年大叔一般,呼噜呼噜~
-------------这只是一个分割线-----------
您种了一棵猫树苗~很久以后~
哒哒哒~您收获了一只中华龙狸狸花猫!
虽然被叫做龙狸,但撒娇时还是喵喵喵~
打呼的呼噜声,才发现不大的身体内隐藏着龙魂,吼呼呼~吼呼呼~
这时候,开头的两个疑问便产生了。
不是不违背开闭原则吗?
不是不需要修改代码吗?
其实,我觉得大多数人,是被main方法这个例子误导了!
正真重要的是下面三行代码之后所包含的设计思想:
cat = catFactory.growCat();
cat.mewing();
cat.purring();
Cat类和CatFactory并没发生源代码的修改,我们只是扩展了它们的DragenLi子类和CatTree子类。
而至于猫盒子、猫架子、猫树苗,我是如何知道要给梦幻猫工厂的?
答:这里有一个本质的思想误区。不是我知道他们有长猫的能力,而是因为他们本身就有长猫的能力。
以Web开发的思想,来说明:
页面上原本有猫盒子和猫架子两个按钮,这两个按钮是通过后台数据库配置的。
某一天,梦幻世界的大神——数据库管理员——决定追加一个梦幻道具,猫树苗。
这时候,浏览器没有重装,HTML页面代码没有修改,但页面上却已经显示了第三个圣物。
而main方法则替换为请求调用的方法:
@ResponseBody
@RequestMapping("/holyThings")
public String holyThings(CatFactory catFactory){
Cat cat = catFactory.growCat();
cat.mewing();
cat.purring();
return "得到一只猫";
}
这样,无论是通过拦截器还是反射来实现catFactory对象的传入,最终结果就是,当我点击某个按钮时,后台便已经知道它需要的是CatFactory类的哪个子类。
至此,关于工厂方法类的思疑自释便梦幻的结束了!
顺便一提:工厂方法模式最大的缺点就是,需要新增许多的工厂子类才能完成新的工厂产物管理。系统会耗费大量的资源去管理各种工厂子类,造成资源的耗费。
笔者话:知识的理解深度,随着每一位朋友对知识的运用而有所不同。达者为师!若在下所说内容中有缺漏或错误,还望指正!由衷感谢!
互促互进,常学常新!