OO第二单元架构随笔

关于Unit2的整体总结本人已发表于这篇博客https://www.cnblogs.com/Sept-Bug-Maker/p/12717592.html,但除此以外,我还想就HW3的架构深入的谈一下本人的一些心得体会。

这是本人HW3的UML类图,以下分析将基本围绕其展开。

一个思路

其实第二单元上课的过程中许多模型的建立都是源于生活实际,包括这次电梯在内的问题也都是为了解决实际生活中的问题,那么我们能否根据生活中的经验来帮助我们建立相应的代码架构呢?我想这一答案是肯定的,这也正是本人Unit2迭代开发的核心思路——面向生活设计与构造。这么说来可能很抽象,下面我将具体阐述我的思路。

首先,我想将Unit2的整个任务比作现实生活中的盲人乘电梯问题。为什么是盲人?因为这些Request自己本身并不是一个线程,它们只是知道自己叫什么,从哪出发,想去哪,但不会主动行动,只能被动接受调度。提炼出了这个问题后,我们如何解决呢?这又涉及到这些盲人所要进入大楼的电梯配置。

HW1中的大楼十分朴素,只有一部电梯,故所有盲人都只能在这部电梯门口排队等待接送。他们要做事的很简单,只是告诉电梯,我在这个楼层发出请求了以及我要去哪。接下来的所谓调度部分也只是单部电梯内部的优先级处理而已,而这是对外透明的,故架构设计时无需对其考虑,盲人们也无需关心。

而到了HW3,这栋楼转眼间升级了,配备了多部电梯并且限制了可达楼层。与HW1相区分,此时盲人们不能再进行无脑地排队,故很自然地能想到建立一个通知者站在这栋楼的门口。对于每一个来的盲人,这个人先看一下当前各个电梯的情况,再询问其出发楼层和目的楼层,最后结合两者告诉他:“你去xx电梯排队吧”。除了第一次进入大楼以外,盲人从换乘楼层出来的时候也需要知道他第二次要去的电梯,此时,它会再次询问这个通知者。这样一来,我们就可以通过总结这个通知人的功能抽象出一个主调度器Channel类,他只负责根据当前各个电梯的状态,制定策略将盲人们引导至正确的电梯队列Table中。

这样,就基本完成了从HW1到HW3的架构过渡配置。不过,我们还要对架构进行优化,根据设计原则增加每个类的内聚度。其实这就相当于生活中给那个工作单元减负,让其变的更加专一,更加“傻瓜”。以调度盲人的通知者Channel为例,他又要收集两方的信息,又要制定相应策略,太累了。因此,考虑让他变“傻”。将策略类Strategy分离出来,相当于给这个通知者配了一名“军师”,工作时,通知者把所知道的信息告诉给军师,然后他就不管了,等着军师帮他制定策略,告诉他这个人分配到哪个电梯最好。

以这个思路进行思考,HW3架构的设计与实现也就水到渠成了。我们需要做的,只是提炼出各个现实生活中工作单元的功能,写出相应的类。而它们之间的协作关系,通过现实生活的映射,是十分清晰的。

两种模式

无论是理论课还是研讨课上,官方和同学都给我们安利了各种模式。(在这里我就不举例子了,因为我也记不住名字)这些模式都相当于前人的一些经验总结,熟练掌握会起到事半功倍的作用。但客观来说,真的是很难记住全部。因此,我在设计时用几个容易记忆的简单模式进行组合,来代替一些更复杂的模式。以下是我在HW3中应用的两个模式:

生产者-消费者模式

这是个官方安利的模式,相信大家基本也都采用了,在此我就不多赘述。

观察者模式

这个模式则是我们实验课练习的模式,根据我的个人经验,实验课填写的代码往往对课下作业有很大的指导价值。上文中提到过,主调度器Channel类需要知道当前电梯的运行状态,而电梯状态是实时更新的,这样一思考,就能自然而然地想到应用这一模式——向Channel类新增update方法来更新所记录的状态。

综上,其实模式的应用往往是水到渠成的,我们要做的只是平时尽量多积累,结合一些实例去理解与记忆。

三个代表

下面我想进一步深入,举几个HW3架构中典型的类来阐述我架构的设计及优化。

Channel&Table——无尽的套娃迭代

从HW1到HW3,本人架构最为核心的改变就是原来的单层调度变成了一个主调度器加一些分调度器的双层调度。其实,这就相当于一个问题转化的过程——当你已经有了一个能解决单个电梯运输的架构,面对多部电梯的问题时,你只需要解决将乘客调度往单个电梯门口这一个小问题。解决完以后,你会发现剩余的问题你之前早已解决。试想,如果将来面对的需求时多栋楼的多部电梯运输,其实现也不过就是再套一层娃——构建三层调度。因为我们的OO作业都是迭代开发,故采用这种套娃的方式无疑是很高效的,也是很有正确性保障的。我想这也是课程组设计这些作业时希望我们采用的方式。

Strategy——军师的锦囊妙计

前文说过,这个类相当于主调度器的调度算法类,抽象出这个类,是本人根据SRP原则增加类内聚度的结果。我认为,如果可以做到的话,在设计时尽量让代码中像调度算法这样的与性能直接挂钩的部分单独成为一个类,降低功能和性能的耦合度。这样在优化性能时,就减少了改动代码而严重影响正确性的风险。(本人Unit1的惨痛教训)当然,这种调度算法更好的实现方式是采用接口,这一点我在Unit2单元小结中有详细说明,在这就不再展开。

Person——你总想知道更多

一开始做HW3,我在前面作业的基础之上将请求进行调度时,有个很明显的感受:为啥PersonRequest类只能告诉我这么少的东西啊,好麻烦。比如,请求在进入电梯时,电梯甚至不知道其是不是属于换乘的乘客,还必须去访问主调度器,这样不仅显得很复杂,而且增加了类之间的耦合度。于是我想,就像盲人乘电梯问题,电梯从盲人那得到的信息越多,我的调度不就越方便吗?所以,我在Strategy类进行制定策略时将PersonRequest类封装成了Person类。这就相当于盲人在询问要去的电梯时,记住了自己要去哪部电梯,自己是否需要换乘,换乘在哪一层下等信息。更多信息的传递带来的,是单元间耦合度的降低,以及整个体系工作效率的提升。

关于其他

Unit2中,线程安全问题成为了大家关注最多的问题之一。其实,在我看来,线程安全问题很大程度上可以从架构层面就得到解决。以坑了许多同学的“电梯如何结束运行”这一问题为例,基于本人的HW3架构,这个问题不算是个难题——关闭时,通知者(主调度器)先知道这栋楼要关闭了,再等待至成功到达的人数等于进入的总人数,这时通知所有等待运作的电梯,你们不用等了,直接下班吧。提炼出这个流程以后,翻译成代码来具体实现并不困难。既然设计阶段就将这些产生bug的隐患排除,那么我们何不再多花一些时间在设计上,做到防患于未然呢?

以上就是本人关于Unit2想说的一些东西,如你所见,这篇博客真的是通篇都在围绕着架构展开,而没有一点性能优化之类的内容。(因为本人的电梯性能是真的不行x)可能对于有些大佬,这篇博客通篇都是废话,但我还是真心希望它能多多少少帮助到一些人们。最后欢迎大家与我交流或对我的错误进行指正。

posted @ 2020-04-19 16:40  林间九月  阅读(123)  评论(0编辑  收藏  举报