软件设计模式(一):简单工厂模式
2024-04-26 14:27:44

写在前面

在诸如github等开源社区里的第三方工具库已经成为程序开发不可分割的一部分的当下,部分从事软件程序开发的技术人员被人们戏称为接口程序员,这类程序员的日常工作内容无非是调用第三方工具库所提供的接口,以此完成公司所需的各类业务需求(这其中的大部分都是机械的CRUD操作),他们不关心程序的可读或可维护性与否(或者说也无法关注,各类繁杂的开发任务使得他们无暇对软件进行具体设计,我认为这也是国内目前软件生态的一种悲哀)认为写出来的代码就算难以维护或升级,也只需将整个项目推到重来罢了。

这种观点是完全错误的,也是软件工程学科所极力避免的,我并不是否认使用第三方工具库的重要性,一个优秀的程序员不会自己造重复的轮子,这一点无可厚非。但是真正优秀的程序员不应该止步于对各类第三方中间件的熟练使用,反而无视了软件设计的重要性。我认为他至少应该具备下面这些品质:

  • 熟练掌握各类数据结构和它们的应用场景
  • 掌握各类算法策略并能够在合适的场景使用它们,不盲目使用算法。
  • 对计算机操作系统的功能有清晰的认识
  • 对计算机的硬件组成、缓存、缓冲、内存、外存、文件系统有良好的认知
  • 清楚地了解计算机网络的架构
  • 有良好的信息检索能力和学习能力,面对新的业务需求,能够找到合适的技术栈学习并解决
  • 有良好的排错能力和沟通能力
  • 有良好的软件设计意识,写出的代码优雅易懂,有良好的可维护性和可拓展性
  • 有不断学习的精神,有一个可以交流技术开发经验的圈子

我一直梦想着成为这样的程序员,这也是我学习设计模式的动机。写这篇博客是为了记录我在学习设计模式路上的一些想法,方便我后续查阅,同时也是为了将这些想法分享出去,或许能对正在读这篇文章的你有一些启发。

本系列文章的绝大数想法与总结都是有关于程杰老师所著的《大话设计模式》在此感谢程杰老师对于我学习软件设计模式的帮助。本系列文章内容仅供学习参考,如有侵权,请联系我删除

为了学习设计模式,你应该具备

  • 学习过面对对象程序语言,例如java C#等
  • 理解继承、封装、多态等面对对象特性
  • 能够理解并绘制UML类图

为什么要学习设计模式?

作为一名软件工程行业的从业者,对于软件设计模式的重要性早就有所耳闻。一段好的程序应该具备什么样的特质?教科书总是在告诉我们好的软件应该是面对对象的、应该具备良好的可维护性,高内聚低耦合,高可拓展性….. blablabla 。有科班学习经历的小伙伴对于这些术语应该耳熟能详,并且也很有可能自以为能够在设计开发的过程中遵循这些开发原则。

但据我本人观察,对于没有学习或者深入了解过软件设计模式的人来说,由于各种各样的原因,他们的思想趋向于对计算机友好的面向过程型。(笔者本人就是如此)即注重算法过程的设计,写出的代码段往往只是为了完成功能,不考虑软件的后续维护、拓展的难易。

通过学习软件设计模式,我们虽然并不可能直接成为软件设计领域的大师,但至少能让我们在潜意识里种下面对对象思维逻辑的种子,或许在将来的某一天,在面对一项业务需求时,你能忽然想到,我使用这种设计模式或许会更好!

简单工厂模式

引例

写一个计算器,能够实现加减乘除。

面向过程实现

我相信面对这个需求,绝大部分初学者的想法是这样的:在main函数里定义三个变量,姑且叫他们a,b,c吧(注意!这是不符合命名规范的)a, b表示数字,c代表运算符,使用if语句根据c的内容选择运算类型,进行对应的计算,并将结果输出到控制台。

搞定!收工!简直手到擒来。

真的是这样吗?

面向过程实现的弊端

我们说过一个好的软件应该具备良好的可维护性,现在请读者考虑如下问题:

  • 如果老板要求你拓展这个计算器的功能,比如求一个数的平方,开方等等,你怎么办
  • 如果老板要求将这个软件到移动手机端(我们假设你之前在windows平台开发),你怎么办?

针对上面的问题,你可能会说:很简单啊,我只需要多写一个判断语句,就能完成新功能的添加啊。

至于软件移植,em,只需要复制粘贴原来的判断逻辑代码,再新写个显示结果的代码就行。

嗯….如果世界上所有的软件业务逻辑都和这个计算器一样简单就好了(笑)。事实是,在实际开发环境中,面对庞大的业务逻辑代码(并且这段代码很有可能不是你开发的),如果按照上述设计思想,读懂并定位具体的代码,最后修改它将会是一件艰难的事情,又或者当你找到、修改完这段代码并重新编译后,其他的业务逻辑却受到了影响,继而导致整个系统发生了崩溃。另一种情况是你在修改代码时,不小心修改了原有的业务逻辑代码,导致原有的业务错误(可怕的是此时你并不知情)这个错误直到系统重新上线才会被发现。

我们假设你排除万难,解决了因为修改一段代码而引起的所有错误。你可能会惊喜的发现,相较于修改这段代码,还不如重新编写一个拥有该新功能的系统,这样反而效率更高。

这还不算完,在通宵加班解决这些问题后,你的老板可能告诉你:

还是用第一版吧!

晕,这还怎么玩?

简单工厂实现

仔细分析面对过程的实现过程,我们会发现问题出在if语句上,如果我们的功能需求发生了变化,我们将不得不修改if语句块,增加新的判断条件。此外客户端界面实现代码与业务逻辑代码耦合在了一起。如果面对平台移植这类需求,我们将无法复用具体的业务实现代码。

1.新增加的功能不应该对原有运行良好的代码产生变化

2.我们一般认为在进行软件设计时,应该敏锐地识别出变化点,并将这个变化点封装成类

为了解决这些问题,我们首先应该将客户端的代码与业务端代码分离,使用类的继承特性,利用简单工厂完成不同实现类的实例化,具体类图如下:

其中运算类中的getResult为抽象方法,其他子类继承并实现这个方法。

简单工厂负责根据业务逻辑(这里即判断运算符的类型)生成具体的运算子类(加法类等)

这样一来我们在业务代码段中编写if语句时,只需根据不同的条件来选择实例化不同的类。若要增加新的功能,则编写一个新的子类,继承并实现getResult方法,再去更改if语句,返回不同的实例化对象到客户端代码端,客户端只需调用对象中的方法即可……

等等?

怎么还要修改if语句?那干嘛还要特意搞个继承?这不是大炮打蚊子嘛?

这个问题的答案目前只能用教科书式的语言来解答:利用类的继承特性,我们在增加新的功能时是可以不管其他原有功能的实现逻辑的,也就是说,你在实际开发过程中,是根本看不到其他的具体业务代码的!你是没有权限访问它们的!这样做的好处就是可以保护运行良好的代码。

1
2
3
4
其实如果读者使用过spring框架,就会明白,spring里也有一个工厂类
当我们将一个类注册为bean后,使用@Autowired注解就可以让spring工厂自动为我们提供一个实例
具体技术原理涉及到了反射等,总之,当我们定义一个接口后,可以根据需求来让spring为我们注入不同的实现类
换句话说,我们真的可以不使用if语句!!!

总结

本篇文章我们主要简单介绍了简单工厂模式,它的核心思想是利用类的继承、多态特性,可以根据不同的逻辑为我们生成具体的实现子类,实现了一定程度的解耦,方便后续功能拓展。