前几天有个新人在进行模块分解时,碰到了问题,他自己已经基本做好了,但总觉得不对劲,却理不清楚怎么做才能更好一些。因此找我帮他分析一下。
由于他并没有绘制任何图形化的东西,也没有任何文字形式的描述,只是口述了自己的困惑。我搞明白他的问题,花了些时间,我觉得这是不值得的。这里我必须再次强调一下,如果你希望与别人交流以尽快获得帮助,那么请务必将问题表达清楚,除了口述外,使用合适的工具、图形和文字将事半功倍。
为了更好的说明问题,我把他的现状用简单的类图画出来,加以说明,以便让大家明白他的问题所在。
其实功能也不复杂,在一个窗口中需要展示一些信息,并能够响应用户的操作。用户输入的信息,需要用到特定的算法进行计算,而计算过程中,需要用到一些参数,这些参数来自于数据库。在系统启动后,这些参数已经由数据库操作类完成了读取,这里为了说明方便,假设为结构A和结构B,且这两个结构由于某些环境的原因,不可以调整。
他现在的实现是,展示窗口类中同时封装有业务相关的内容,当用户在窗口中修改某项内容时,直接在窗口类中进行处理,调用算法函数,完成处理,并重新展示计算后的信息。而在算法类中,某个算法需要分别用到结构A和结构B中的部分信息,因此直接在算法函数中,访问数据库操作类来获得所需的参数。
对于一个新人来说,这样的实现可以说已经比较不错了。已经使用到了类的分工,进行了一些封装。但如果想更进一步的话,似乎有些地方还有改进的空间。下面来分析一下。
在进行类的设计时,很重要的一点,是我们需要明确它的职责和关系。职责单一,内聚度高;关系单一,耦合度低。这是一个基础的目标。
在上面的几个类中,我们可以看到几个问题。
对于展示窗口类,存在职责不明确问题。窗口和视图等信息展示和交互类,一般赋予它们的职责是两个,一是向用户展示信息,二是接受用户的响应。尽量不要涉及具体的业务。
对于算法类,耦合度存在问题。可以看作是工具,它必须和具体的业务无关,经常可以将算法和工具类封装成动态库。这里算法类依赖于数据库操作类,显然大大增加了耦合度,是一个很差的设计。
最后说一说数据库操作类,似乎扩展性存在问题。它只是完成了一些参数从数据库中读取,然后保存在已定义的数据结构中。假设后续需求发生变化,这些参数使用其它形式保存,比如XML,比如INI,那么这个类就需要重新写了。当然,就这个项目短期来说,也许也不是那么重要,但从长远来看,也许优化一下会更好。
基于这些问题,我对他的设计进行了一下改进,希望能够解决目前的一些问题。
在新的设计中,首先将展示窗口类中的业务部分剥离,构建业务类。展示窗口类只与业务类相关,与其它类都没有关系。只要展示的内容和交互方式不变,业务的变化将不再影响窗口展示类。
其次,将算法类中算法所需的参数,定义为一个数据结构,作为算法的输入参数。这样算法类就完全独立了。外部需要使用算法时,必须给出算法所需的参数信息。这里算法类只有业务类会使用它,因此由业务类负责提供参数,而不再由算法类自己去找参数。
对于数据库操作类,其实它真正的核心是对两个结构的封装,数据库只不过是数据的来源而已。考虑到未来数据的来源可能有其它形式,因此将其抽象成一个接口类,两个结构为其成员变量。可以预见的成员函数包括根据数据来源的初始化函数,以及两个结构的获取函数。当未来需要增加新的数据来源时,只要派生相应的子类来重写接口类就可以了。原有的数据库操作类完全不用调整。
对于新的系统参数的处理设计,也许可以找到某个设计模式的影子,可以向着工厂类来演化。
最后,由于算法类需要的参数,与结构A和结构B并不一致,所以这里需要有个适配处理。可以考虑由系统参数接口类提供接口函数来实现,但更建议独立封装一个结构间的适配类。因为系统参数接口类并不需要承担这个职责。
当然,以上的设计肯定还可以进一步优化,留待大家探索。只是希望大家能够从以上的设计优化的思考过程中,吸收到一部分有益的经验,那我就很满足了。