说点题外话02 模式并不是解决方案
你好,我是徐昊。今天我们再来专门说点题外话。
说点题外话系列,是我根据评论区的留言,以及不少读者直接给到编辑的反馈中,挑选出来一些值得回答,但又不好直接回答的问题,然后呢,我会讲讲这些问题背后对应的原则。希望你可以感受到我强烈的暗示,在学完之后,不仅要思考,还要主动去寻找一下答案。
除此之外,我也希望给平淡的连载生活带来一定的现场感。比如今天这篇文章是周四零点推送,那么编辑小姐姐会在周三中午12点截止反馈收集。然后呢,我会从中挑选要写的话题,从下午两点开始写,然后录音。这样或多或少可以为专栏课程带来一些不可预知的可能性(比如编辑小姐姐一整个下午都在担心专栏是不是会断更,以及一整个下午,都保持着随时待命的状态)。
言归正传,今天我要讲一讲模式(Pattern)。自从GoF(Gang of Four)在1994年发布设计模式(Design Pattern)以来,模式就成了获得可重用的对象模型的重要手段,而模式语言(Design Language),也成了我们描述架构和解决方案的重要手段。
我知道我们这个专栏的读者都有比较长的工作年限,也有比较丰富的工作经验,想来对于模式,你肯定是不陌生的。那么我先问一下,如下图所示是什么模式:
如图所示,某个类A将自己的行为委托给某个接口B,然后某个具体实现类C,实现了B的接口。你能想到什么模式呢?
如果仅从这个结构来看,那么对应的模式有很多很多可能性,我随便可以列出五种:
- 策略模式(Stragegy Pattern):B是策略的接口,C是具体的策略实现。
- 适配器模式(Adapter Pattern):B是某个接口,C是将另一个实现转换为B的实现。
- 状态模式(State Pattern):B表示状态的接口,C是某个具体状态。
- 备忘录模式(Memento Pattern):B表示备忘录的接口,C是备忘录某个特定的数据备份。
- 装饰器模式(Decorator Pattern):B是某个接口,C是装饰了这个接口的额外功能。
我们会发现,如果仅仅给定最终的实现结构,那对应的模式可能有很多很多种。那么,怎么才能判断我们到底使用的是什么模式呢?
这个时候,我们就要回到模式的定义了。模式至少包含两个部分:问题和解决方案。同样的解决方案可能会对应着不同的问题。那么,同样的解决方案与不同的问题配对,也就产生了不同的模式。
比如说,对于策略模式,问题是对于某个类,需要施加不同的算法以完成不同的功能。那么怎么做才能使得算法可以替换,还不会影响到使用这些算法的类呢?
那么对应的解决方案就是,通过接口表示算法,然后将不同的算法封装到不同的对象中,并让这些对象实现这个表示算法的接口,这样就可以实现算法的替换了。
而对于状态模式而言,问题是某个类的行为随着它内部状态的改变而改变,我们如何将不同内部状态下的行为进行有效地封装,并在状态改变时做出与之对应的变化呢?
对应的解决方案就是,通过接口表示状态,然后将不同状态下的行为,封装到不同的对象中,并让这些对象实现这个表示状态的接口,这样就可以在内部状态改变的时候,实现行为的改变了。
虽然从最后结果上看,我们得到了类似的代码结构,但实际上,两者却是完全不同的两个模式,因为它们要解决的问题完全不同。那么我们不禁要问了,是否能够从解决方案反推它要解决的问题,然后判断我们使用了什么模式呢?
答案是,不行,最好也别这么做!
其实不光是在写代码,与人交往的时候,也最怕从行为贸然推断意图。比如说,哪个小姐姐多看了你一眼,你就觉得人家看上你了。于是批《红楼梦》的脂砚斋就跳出来说,古来穷酸最会替别人取中自己。你看,你和脂砚斋都已经在意图批判了,这样不好。
再比如这道经典送命题:为什么你去喝酒都不陪我,你一定是不爱我了。这也是标准的意图推断,多少不和谐都是从这里来的。
所以如果你深受其苦,那么请反思一下自己,为什么你看到某个具体的实现方案,就会猜测它是不是某个模式呢?我们还是要理解具体在解决什么问题,然后结合解决方案,才能明白具体是什么模式。
因为这样做其实也带来了另一个问题,就是不对问题加以判断,盲目地去套用某个解决方案的结构。这造就了大量看起来很高级,实则一无是处的烂代码,而且这种代码还充满了酸臭的匠气。
所以说,模式与建模是一样的,都是问题先行。模式最难的地方就在于判断当前的问题与模式要解决的是否是同一个问题,这也是为什么我们提倡通过重构获得模式:当问题明显出现的时候(以坏味道的形式),那么最难的一步其实你已经解决了。这时再通过重构去得到具体的解决方案,反而是简单的。
思考题
最后是非常值得我们思考的思考题环节。请问:我们在第4-6节所讲的模式,它们各自对应的问题和解决方案分别是什么?
一定记得把你的思考和想法分享在留言区,我会和你交流。同时,也非常期待你能把自己的疑问和想听的话题,写在留言区,或者反馈给专栏编辑(微信号:seekforli)。我们下个题外话再见!
- 码农戏码 👍(23) 💬(1)
貌似开了点窍,总结一下,有把锤子哪里都是钉子,有了一套解决方案什么问题都能套得上,但不是每个问题都是同一个问题 由具体问题抽象出解决方案,再在解决方案指导下给出处理具体问题具体路径 问题N:N模式 转化为 问题N:1 解决方案 1:N具体模式 这是N:N的关系,必然从一边是推导不出另一边的 文中的每个问题抽象出是要可扩展,解决方案是面向接口编程,但每个问题都不同,所以具体行为也不同,策略模式是应对行为,状态模式是应对状态 解决方案是一个,可问题却各自具象,魔鬼在细节 能力供应商模式也是同样的,从表面看是接口定义在domain,实现在其它地方,保证domain的稳定,形式上跟很多相似,ACL、整洁架构,也就是解决方案相似,但其实解决的具体问题不同,所以不能说能力供应商就是ACL 很多时候程序员不是在编写代码,而是在摸索业务领域知识;也就是从当前的代码去推测当时在解决什么问题,再重新定义问题,进行重构,这是因为知识没有有效传承,或者没有达到代码即模型
2021-07-22 - Oops! 👍(45) 💬(1)
夜不能寐,尝试答一下… 1 关联对象,通过将集合逻辑封装到关联对象中的方式解决聚合时性能和逻辑封装之间的矛盾。 2 角色对象,通过将实体在不同上下文中的逻辑封装在不同的角色对象中,解决上下文过载问题。 3 上下文对象,通过显式的对上下文进行建模,将跨域业务逻辑和上下文依赖封装到领域对象中,进一步解决上下文过载问题。 4 能力供应商,通过从基础设施层提取具备业务含义的能力接口纳入到领域层,消除基础设施层,将其转变为能力供应商,参与各层逻辑,解决传统分层下,领域层和基础设施层之间“不正当”的依赖关系破坏了领域层的稳定性的问题。
2021-07-22 - 梦倚栏杆 👍(3) 💬(1)
说的非常对,but现在很多的实际场景就是根据代码来推测当时要解决的问题,也确实容易走偏(在熟悉业务之后,发现当时可能偏了)
2021-07-22 - 6点无痛早起学习的和尚 👍(1) 💬(0)
2024年01月17日09:04:28 所以我理解应该是定义清楚问题,再去找解决方案。同时我们学习设计模式的时候,也应该先清楚这个模式解决了什么问题,而不是直接上去学习模式是什么。
2024-01-17 - 林铭铭 👍(1) 💬(1)
最难的还是如何把问题定义好。
2021-07-23 - 冯 👍(1) 💬(0)
设计模式不是设计出来的
2021-07-22 - 端碗吹水 👍(0) 💬(0)
想到了经典的 xy问题
2024-03-28 - 范飞扬 👍(0) 💬(0)
勘误: “而模式语言(Design Language)” 英文翻译错了吧
2024-03-25 - 云川 👍(0) 💬(0)
换句话说,如果看代码知道了模式,那就可以确定其需要解决的问题和具体的解决方案。
2023-04-03 - 201201624 👍(0) 💬(0)
策略模式是应对行为,状态模式是应对状态
2022-08-10 - 云师兄 👍(0) 💬(0)
深呼吸啊😮💨
2021-07-23