3

讨论实际项目的工厂模式

 3 years ago
source link: http://zhangyi.xyz/discuss-with-factory-pattern-in-real-project/
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.

讨论实际项目的工厂模式

发表于

2021-01-20

|

更新于 2021-02-01

| 分类于 蔡了成长记

| 阅读次数: 107

2021-02-01.jpeg

大思接了满满一杯热咖啡,正欲离去,转身时,偶然瞥见茶水间的白板画满了类图,定睛一看,感慨地说道:“蔡了,你还真是幸福啊!马大叔亲自给你讲解工厂模式。”

蔡了刚刚意识到自己说错了话,还在尴尬中,赶紧说道:“是啊,是啊!”一边说着,一边还使劲地点着头,希望通过过分的礼貌来化解刚才的口不择言。

成大思想到了自己过去学习设计模式的经历,悠悠地说道:“说起来,当初我学设计模式时,可没有你这么好的待遇了。”

这一番话倒是勾起了蔡了的好奇心,赶紧问道:“大思,那你当时是怎么学的呢?”

既然有了谈兴,成大思也在沙发坐下来,慢条斯理地说道:“当初我刚入行没多久,就接到一个开发任务,让我独自完成一个报表组件的设计与开发。这一报表组件是公司应用框架的一部分,需要支持微软的水晶报表和用友的华表。说起来,现在的程序员可能都没听说过这两款报表产品了吧。”谈到过去,同样作为职场老人的马丁花似乎深有同感,举起手里的咖啡,如饮酒一般向大思隔空敬过,二人各自仰头吞了大大一口苦涩的浓咖啡,长吁短叹,对视无言,油然而生英雄迟暮之感。

95后的蔡了哪里能体悟IT历史的往昔与荣光,继续如好奇宝宝地追问:“后来呢?”

大思回过神,继续讲道:“我接到任务后,想了许久,也不知该如何设计才能灵活地支持各种报表,无奈之下,只得去寻求项目经理的帮助。项目经理听了我的问题,丢下一句‘用工厂模式可以解决’,然后就酷酷地抛下我不管了。没有办法,好歹给我指点了明灯,于是我就开始上穷碧落下黄泉地寻找工厂模式的资料。那时候,哪有这么多讲解设计模式的资料?主要的参考资料还是GOF的《设计模式》。说起来,虽然这本书的讲解稍显晦涩,但它才是设计模式的正宗心法啊!蔡了,你可以好好读读这本书!”

design-patterns.jpeg

蔡了谢过大思的推荐,继续追问:“那你最后是如何设计的呢?”

“真要理解了工厂方法模式,说起来也不难。我对这两种报表进行了抽象,分别定义了报表对象(ReportObject)、报表处理器(ReportProcessor)和报表格式器(ReportFormatter),毫无疑问,它们都具有各自的继承体系,可以引入工厂来创建它们。”

说到这里,大思拿起板擦,问道:“白板上的内容可以擦掉吧?”这时的蔡了倒是学会了察言观色,赶紧抢过大思手中的板擦,说道:“我来!我来!”三下五除二,白板擦得干干净净。大思在白板上画下了这样的类图:

report-factory.png

“这不就是抽象工厂模式吗?”蔡了惊喜地嚷道。

“不错!”成大思答道,“说起来,这一设计方案都过去十多年了,我至今对这一方案依旧历历在目,毕竟这是我第一次独立设计一个组件。”

“所以说,还是要做实际的项目才能锻炼人!”马丁花补充道,然后对蔡了道:“听别人讲授设计知识固然能快速帮助你理清思路,掌握知识窍门,但没有经过你自己的思考,不可能真正掌握,更不用说合理地运用到项目中。这样吧,给你布置一个学习任务,利用业余时间学习slf4j日志框架。我们项目也使用了这一框架,你应该知道它的用法,对吧?”

蔡了点头称是。

马丁花继续说道:“slf4j框架灵活地运用了设计模式,也包括今天提到的工厂模式。针对slf4j如何创建一个Logger,你可以查阅相关资料,并阅读slf4j的源代码,深入了解其设计手段,写一份学习笔记。”

三人结束了这番在茶水间的谈话,留给蔡了的又是一份没法拒绝的学习礼物。


三天后,蔡了向马丁花交付了一份学习笔记。内容如下:

slf4j本身是一个日志框架,为了让Java开发人员可以采用同一种方式使用日志,它又很好地集成了logback、log4j等其他日志框架。为此,它定义了标准的日志接口,slf4j的slf4j-simple日志框架与logback都实现了日志接口。由于log4j的诞生要早于slf4j,则专门提供了slf4j-log4j12将log4j整合到slf4j中。

不管是什么样的日志框架,只要使用slf4j,都可以通过如下代码来创建Logger

private final Logger logger = LoggerFactory.getLogger(Demo.class);

LoggerFactorygetLogger()方法就是简单工厂模式的体现。在getLogger()方法内部,具体创建什么样的Logger,又由ILoggerFactory工厂来决定的。ILoggerFactory工厂和它创建的Logger就是slf4j定义的通用日志对象接口。以logback为例,它定义了自己的日志工厂和日志对象,实现了slf4j的接口,如果其他日志框架,如log4j需要slf4j作为统一的日志入口,也需要实现这些接口。

logger.png

这实际上是工厂方法模式的体现。

slf4j具体使用了哪一个日志框架,取决于项目依赖的日志包。只要将对应的日志系统jar包加入到项目中,slf4j就会自动选择使用它。例如,倘若添加这样的Maven依赖:

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<optional>true</optional>
<scope>runtime</scope>
</dependency>

意味着slf4j会使用logback日志框架。

ILoggerFactory对象的创建由slf4j来决定,不需要进行配置,更不需要人为的修改创建代码。怎么才能做到这一点呢?

我仔细阅读了slf4j的源代码,又查阅了相关的资料,发现slf4j具体选择哪一个ILoggerFactory,是由LoggerFactoryBinder决定的,可以将它理解为是日志工厂的工厂,在slf4j框架中,将其形象地称之为一种绑定(bind)操作。

根据这一设计,我最初以为slf4j要通过反射创建不同日志框架的LoggerFactoryBinder对象,后来发现LoggerFactoryBinder接口只有一个实现类,即StaticLoggerBinderLoggerFactory调用了它的getSingleton()方法获得StaticLoggerBinder对象,再由此获得ILoggerFactory工厂:

StaticLoggerBinder.getSingleton().getLoggerFactory()

我翻阅了成大思推荐的《设计模式》,了解到创建StaticLoggerBinder对象的这种方式称之为单例模式。我站在调用者的角度去思考,发现这一设计可以高效地获得StaticLoggerBinder对象,因为它可以避免对象的频繁创建。

由于getSingleton()StaticLoggerBinder的静态方法,因此LoggerFactory对它是类型依赖的,做不到实例方法的多态扩展。实际上,这个类的名称也说明了它采用了静态方式绑定了日志工厂。这就要求所有实现了slf4j接口的日志框架,都需要定义这个类,且这个类的classpath应该保持一致。在slf4j的LoggerFactory中定义了这个类的路径:

private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";

查看logback的JAR包,确实在指定位置发现了该类:

binder.png

马丁花收到这份作业后,不禁抓了抓已经日渐稀薄的头发,心里哀叹,要培养一个新人,还有很长的一条路要走。是否让蔡了继续深入研究slf4j这一框架呢?马丁花陷入了沉思中。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK