设计模式入门

设计模式入门

学习编程已经有一年多了,一直都没能力开发一个大型的App,由于对计算机课程体系知识的欠缺,一直不知道自己到底是差在什么地方,还以为自己只是单纯写的代码太少。直到大一期末Java项目实践的时候通过泡泡糖项目,当时看到那个项目里的ScoreManager、ScoreService这样的更抽象的类就觉得这些类与课本里所说的用于描述事物特征的类不太一样,也跟课本里描述一类东西特征的抽象类不一样。这个项目最终是管中窥豹,也没有人讲解指点迷津,最终没能突破那一层发现代码中的设计模式从而发现自己欠缺的知识。

到了大二,逐渐开始接触一些别人开发的小型项目,甚至自己开始着手开发一些小型项目,问题立马就暴露的立竿见影了,对于一个完整的项目,如何设计它的模块与类豪无头绪,更何谈组织程序架构。往往其实要解决的问题都很清晰,但是就是很难真正的运用面向对象的思想去解决问题,最终代码还是堆积在一个文件里,难以修改和维护,随着功能的增加更是寸步难行,开源课开发开源项目的时候甚至没法读懂别人开源项目写的代码。

有一个笑话是这样的:
(当团队都懂得“挡拆”模式的篮球场景)
A:来,我们下一次突破用挡拆战术。
B:好的,走!

(当某人不懂“挡拆”怎么说的篮球场景)
A:来,下一次突破,谁要是拿住了球,另一个人就在前面站住,拿球的人运球朝他运动,站住的人不动,准备用胸抵挡住防守球员肩部的冲撞……
B:别废话了,要上场了!

如果不懂得设计模式,简单的堆砌代码,就像盖房子没有设计图,最终是没法设计出灵活稳健的项目的,也正是因为这个原因,我开始使用Head First设计模式一书开始自学设计模式。

前言:面向对象的“不足”

往往在面向对象思想的指导下,我们会使用类的继承来实现对于不同对象的代码复用,当对象确实存在大量的相似之处时,这么做确实行之有效。书中以Duck类举了一个例子,它在创建不同的Duck子类的时候,由于不同的Duck行为不同,很可能导致行为方法无法复用。

换做以前的我,我不会像书中那样在子类中覆盖有区别的方法,我会将Duck类中可以复用的部分保持不变,不可以复用的部分在子类中分别定义,实现不同的部分。那么问题又来了,为了少数的异类将大多数子类可复用的部分剥离出父类,这不是违背了面向对象的初衷了吗?

好在面向对象还提供了一个工具,叫做接口,对于满足某些条件的Duck,我们可以令它们实现接口,不满足的则不实现,这样听起来也十分完美了,但是需要注意,接口并没有方法的实现,方法的实现还是需要到每个实现这个接口的方法中自己定义,所以我们还是没有实现代码的复用。对我来说,我可能会将Duck父类先继承出两个子类,再由需求去决定具体的类到底继承哪一个子类。考虑一个问题,当行为不同的Duck子类变多时,Duck父类只有两个子类很可能不够,需要继续增加,最后变成一个树形结构,这也违背了面向对象的初衷。到这里为止,我已经再无头绪了,只能硬着头皮继续添加描述不同状态的子类去实现不同行为。于是书中给出了第一个设计原则:

设计原则:将可能需要变化的代码与不需要变化的代码区分开来。

只有将变化的部分提取出来,才能实现代码的弹性。这句话说起来容易,但实际上以我的经验做起来很难,这句话也恰恰是设计模式的本质,所谓的把书读厚再读薄大概不过如此。

如何将变化的部分剥离出来

我们观察Duck类的行为,当我们对同一个行为采用不同的方式去实行的时候,结果也会不一样。想象一个场景,我们现在需要写一个Car类,实现对车的描述。同样是补充能量,燃油车可以加油,电动车可以充电,甚至还有氢燃料,这就是程序设计中变化的部分。我们要把补充能量的行为(ChargingBehavior)从Car类中剥离出来,作为新的类表示一种ChargingBehavior。

我们甚至还希望动态的改变Car类的ChargingBehavior,毕竟这个世界上有种东西叫混动车。那么如何才能动态的改变类的行为呢?要实现这些目标,我们引出下一条设计原则:

设计原则:针对接口编程,而不是针对实现编程。

我们现在将行为与类剥离,放在单独的行为类中,而我们的Car类通过接口调用ChargingBehavior,每一个ChargingBehaviorClass都实现ChargingBehaviorInterface,当我们的Car类调用接口时可以灵活的对其指派对应的具体行为,这样,我们就将变化的部分从Car类中剥离出来,而这些ChargingBehaviorClass甚至还可能供Airplane、Engine类之类的实例类调用,达到真正的复用,而这一切都只需要调用ChargingBehaviorInterface,委托(Delegate)接口来实现我们需要实现的目的即可。

Car类UML图

我们通过给Car实例类指派特定的ChargingBehavior实例类作为成员变量即可实现我们的目标。在重新设计后的类结构中,我们不再使用继承实现代码的复用,我们使用的是类与类之间的组合,使用组合建立系统可以获得弹性,在动态运行的过程中改变行为,由此引出第三条设计原则:

设计原则:多用组合,少用继承。

这是我们学习到的第一个设计模式,叫做策略模式(Strategy Pattern),而我们所使用的接口类也叫做策略类。策略模式的目的是将一系列的算法封装起来,实现动态调用,解决了当多种算法类似的情况下使用if-else带来的复杂和难以维护。但是这么做不意味着完美无瑕,同样会带来问题,当我们的ChargingBehaviorClass增多时,会产生策略类膨胀的现象,关于这个缺点,还需要继续学习其他设计模式进行弥补。


设计模式入门
https://wenchanyuan.com/design_pattern_introduce/
作者
蟾圆
发布于
2019年11月22日
许可协议