5

简单工厂不简单

 3 years ago
source link: http://zhangyi.xyz/simple-factory-is-not-simple/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

简单工厂不简单

2020-12-08

| 蔡了成长记

| 51

cover.jpeg

马丁花背着灰色的双肩背包走进了公司写字楼,浮现在眼前的是排成长蛇状的一群上班族,正按照单双层各自分成两队等候着电梯。低楼层的童鞋等不及了,甩开双腿开始爬楼梯,就当是减肥锻炼身体了,不着急的依旧排在队伍里,低头看着手机,玩着游戏或者刷着朋友圈。手机扫去了等待的急躁与不安,一个个安静如花,只待电梯像殷勤的主人把他们一批一批接走。

终于踏进了电梯,马丁花被挤到了角落里,电梯里的人互不相识,肃穆像开追悼会,只听得电梯不断发出“叮”的一声响,电子合成声开始汇报多少层到了,门打开,随着不断有人走出,空间变得逐渐开阔起来。

“十八层到了!”自从新搬到这栋写字楼后,马丁花每次听到呆板而不带感情的电子合成声汇报所在楼层到达时,都忍不住翻一下白眼,心里不由脑补出“地狱”两个字眼儿。摇摇头,缓缓走出电梯。

刷卡。自动门刚一打开,就瞥见一道身影正要到正前方拐角处的茶水间倒水,看见自己来了,又赶紧退了回去,似乎故意躲着马丁花。这道身影再熟悉不过,自然躲不过马丁花敏锐的法眼,赶紧叫到:“蔡了!”

“啊……这么倒霉,一上班就被头儿盯住了,昨天布置的作业没完成,怎么办!”蔡了内心不由哀嚎,听到马丁花的声音,只得停下来,无奈装出一副笑脸,言不由衷地说道:“老大,早上好啊!”

“躲着我啊!昨天的作业没做,对吧?”马丁花看到她一副强颜欢笑的样子,哪还不知道古怪灵精的她打的甚么主意,即刻开启了严师模式。

“哼,勤奋如我怎么会没有完成作业呢?”蔡了赶紧辩解,“——只是,我虽然搞清楚了静态工厂实际用的是简单工厂模式,却不知道它能给设计带来什么好处?查了好多资料,发现居然有各种各样的工厂模式,不看还好,越看越糊涂了!”

“嗯,看来你还是做了些功夫的。一会开完站会,到我工位来,我给你简单讲一讲吧!”

蔡了点点头,谢过马丁花,径直到茶水间倒水去了。

站会一开完,蔡了就规规矩矩前来受教。马丁花开门见山,直截了当地说到:“工厂在设计模式中,其实就是一种比喻,目的自然是为了更好地创建对象。既然在Java中已有构造函数能够担负实例化的职责,为何还需要引入工厂呢?要明白工厂的价值,你就需要先明确构造函数的缺陷,对吧?”蔡了不由得点头称是,似乎得到了一丝启发。

“你想想,Java语法中构造函数是怎么定义的?它有哪些语法上的限制?”马丁花循循善诱地提出了问题。

蔡了在脑海中搜索着这段时间学习的Java语法,心里组织着语言,回答道:“类的构造函数与类名必须保持一致;构造函数支持方法重载,但不允许出现相同方法签名;每个具体类的构造函数都只能创建类自身;如果定义了带参构造函数,且又需要无参构造函数,还需要显式定义无参的构造函数。”

“不错!语法记得很清楚。那么,根据这些语法限制,你想想:如果需要创建代码给出清晰的创建目的,构造函数能不能做到?如果创建的对象有可能发生变化,构造函数能不能做到?如果希望控制对象的创建逻辑,构造函数能不能做到?”

“好像是这么回事,不过大叔啊,你可否给我个例子,这样枯燥的理论讲解让人很难理解呢。”小姑娘有些得寸进尺了。

“好吧。”马丁花看着她一脸困惑的样子,无奈地摇摇头,打开IntelliJ,找到JDK中的Optioanal<T>类,展示给蔡了:

sf01.png

“你看JDK 8提供的Optional<T>类,它就定义了诸如empty()of()ofNullable()这几个简单工厂,它们创建的虽然都是Optional<T>对象,可相较于直接调用构造函数,它们有什么价值?”

蔡了陷入沉思中。马丁花提醒道:“还是昨天那句话,你要学会站在调用者的角度看待API设计。”这么一说,小姑娘就回过味来,说道:“对啊,有了这些简单工厂,就能更加清晰地告诉调用者究竟创建了什么样的Optional对象,尤其那个empty(),实在太传神了!这实际上就是可读性的要求嘛,很好理解哟。”马丁花看着她一副不屑一顾的样子,想到她刚才一脸茫然抱怨不好理解,真恨不得给她脑门上来一记头粟!

蔡了倒是没来得及察言观色,继续想着老马刚才问的几个问题,想不通,只得问道:“嘿,大叔,你刚才说控制对象的创建逻辑,我有些不清楚,构造函数就是方法啊,难道还不能控制创建的逻辑?”

“如果创建逻辑只是验证、组装、计算等逻辑,构造函数确实办得到;但是,你注意到没有,Java类的构造函数是没有return的,如果你希望控制对象创建的次数,又或者希望引入缓存提升创建对象的性能,构造函数还能做到吗?更不用说,在有些情况下,如果需要通过反射创建对象,就更需要引入静态工厂方法来封装对象创建的逻辑了。”

“例如有Composer继承体系。”马丁花绘制出如下类图:

sf02.png

“考虑Composer的扩展性,需要通过反射创建各个Composer实现类,类名遵循格式:文件扩展名+Composer,你想想看,可以通过YamlComposer或其他实现类的构造函数来完成吗?”

蔡了使劲摇头!马丁花把一段示例代码打开,指着代码继续讲道:“这个时候,就需要引入一个专门的工厂类,为其定义一个静态工厂方法,用来封装创建逻辑。”

sf03.png

“大叔!”蔡了认真看着这个案例,忽然灵机一动,想到一点,赶紧指出来:“那你说的创建对象有可能发生变化,是不是也属于这种情况啊?”

“孺子可教!”马丁花很高兴看到蔡了能够触类旁通,继续讲道:“刚才说到的Composer对象的创建之所以引入反射,确实考虑到了创建对象的变化,不过,在多数情况下,不一定要使用反射来解决这一问题。只要你希望调用者无需了解产品对象创建变化的逻辑,在工厂方法返回父类产品的前提下,你都可以使用简单工厂。例如JDK中的Collections类,就定义了许多静态工厂方法,用于创建Collection<T>对象。创建的这些对象可能是不同的集合类型,但对于调用者而言,可以不必知道产品对象的具体类型。”马丁花一边说着,一边打开Collections类的定义,将光标快速移到unmodifiableCollection()方法上:

sf04.png

“你看!unmodifiableCollection()方法表面上返回的是一个Collection<T>对象,实际上,在其内部定义了一个内部类UnmodifiableCollection,它屏蔽了集合的修改功能,使之成为一个不可修改的集合。你想想看,这样做的好处是什么?”

“嗯……”思索良久,蔡了想到了答案:“我想,这样做应该有两个好处。一个好处是unmodifiableCollection()方法清晰地向调用者传递了创建不可变集合的语义,另一个好处是将来如果修改了UnmodifiableCollection内部类的定义与实现,也不会影响到该工厂方法的调用者。我说的对吗,马大叔?”

“不错,不错!我看你彻底理解了简单工厂的含义!”

“噢耶!”得到马大叔的肯定,蔡了不由得欢呼起来,不过想到其他工厂模式,又苦下脸来,说道:“可是——我还是不太明白简单工厂模式与工厂方法模式、抽象工厂模式的区别和使用场景呢。”

马丁花想了想,指着茶水间,向蔡了说道:“说了半天,口也渴了,不如我们去那边冲一杯咖啡,边喝边讲,如何?”蔡了点点头,和马丁花一起走到了茶水间的咖啡机旁。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK