从抽象到模式——面向对象之旅(二)、代码的坏味道
2009-06-21 21:33
225 查看
在上一节中,我们设计了一个过滤器的抽象类,并且在客户代码中通过抽象,很好地利用了类的接口。瞧,抽象是多么地强大,多态是多么强大!天是蓝的,水是清的,生活真是美好啊(Copy某位大师的口吻^_^)。
但是,不要高兴太早,问题来了。现在,我们有一个新的需求:
为了提供更好的扩展性,要求每个过滤器除了提供一套默认规则之外,还要允许客户(即系统的使用者)可以根据需要对规则进行扩充,当然,用户自定义规则是和默认规则分开保存的。
呃,这是哪个家伙出的馊主意?!!
不过还好,变化不是很大。不就是把规则分为默认规则和自定义规则吗?这有何难!
看,我只需要增加两个函数就可以了,GetDefaultRules()读取默认规则,GetConfigRules()读取自定义规则,然后把InitRules()当中读取规则的地方用这两个函数替代。
不过子类还是要辛苦一下了,把原来的InitRules()函数名替换成GetDefaultRules(),然后再加入新的GetConfigRules()。
OK,一切搞定!代码看起来仍然不错。真的是这样吗?
现在到了搬“教条”的时候了,翻开Bob大叔的《敏捷软件开发》看一下——单一职责原则。我们的Filter类是一个数据检查类,还是规则读取类,还是两个都有?如果是数据检查类,那么看,怎么读取规则的接口比数据检查的接口还要多?如果我是代码的阅读者,我更愿意相信他是一个规则读取类。事实上,恭喜您中奖了,这个类集数据检查和规则获取于一身,已经违反了单一职责原则。
很显然,我们要把两个刚刚添加的,可恶的接口提取出来,形成一个新的类才行。
慢着,我们不是说不同的过滤器规则不同,保存的形式也不同吗?好吧,看来还要用上抽象,让每个过滤器子类同时再继承RuleReader类。完成后的代码就是下面这个样子,我们的DataCheckFilter类做AuthFilter类同样的处理就可以了:
编译一下吧,惨了,没有通过。看看哪里出了问题,原来是Filter中的InitRules接口不见了,在PlugIn类中调用Filter的InitRules时就出现了问题。
等等,这个主意怎么样?我们的过滤器子类不都继承了RuleReader吗,我们把Filter *强制转成RuleReader *不就可以了嘛。
呃,我可不认为这是个好主意。另外,除了上面这一个问题,这段代码仍然存在着其他问题:
1)我们刚刚把规则读取接口提取出来形成了一个新的类,目的是为了实现单一职责原则。但是刚刚过滤器子类对RuleReader的继承,不是让我们的功夫白费了。这些丑陋的子类还是职责不明啊。
2)抛开单一职责不说,从另一个角度分析,这段代码的臭味也不小。继承只有在子类“IS A”父类的情况下使用,在这里我们更应该使用“HAS A”的关系。想一下,过滤器有一个规则读取器,用来读取相应的规则,这样的话看起来还不错。
3)当我们某一天需要的仅仅是读取AuthFilter的规则,而不需要对数据做检查时,仍然要让AuthFilter调用RuleReader的接口去做这件事吗?当然是不合适的。没关系,到那个时候再派生出一个专门读取规则的子类来不就完了吗?好,让你说中了。与其火烧眉毛了才做,不如现在就派生出来,而且也可以解决我们现在的这一系列问题。
那么下一步我们要怎么重构呢?先休息一下,下一节我们继续。。。
但是,不要高兴太早,问题来了。现在,我们有一个新的需求:
为了提供更好的扩展性,要求每个过滤器除了提供一套默认规则之外,还要允许客户(即系统的使用者)可以根据需要对规则进行扩充,当然,用户自定义规则是和默认规则分开保存的。
呃,这是哪个家伙出的馊主意?!!
不过还好,变化不是很大。不就是把规则分为默认规则和自定义规则吗?这有何难!
Class Filter { public: Filter(); virtual ~Filter(); void InitRules(); virtual void GetDefaultRules(); virtual void GetConfigRules(); virtual void CheckRequest(string &request) = 0; }; void Filter::InitRules() { ...... GetDefaultRules(); GetConfigRules(); ...... }
看,我只需要增加两个函数就可以了,GetDefaultRules()读取默认规则,GetConfigRules()读取自定义规则,然后把InitRules()当中读取规则的地方用这两个函数替代。
不过子类还是要辛苦一下了,把原来的InitRules()函数名替换成GetDefaultRules(),然后再加入新的GetConfigRules()。
OK,一切搞定!代码看起来仍然不错。真的是这样吗?
现在到了搬“教条”的时候了,翻开Bob大叔的《敏捷软件开发》看一下——单一职责原则。我们的Filter类是一个数据检查类,还是规则读取类,还是两个都有?如果是数据检查类,那么看,怎么读取规则的接口比数据检查的接口还要多?如果我是代码的阅读者,我更愿意相信他是一个规则读取类。事实上,恭喜您中奖了,这个类集数据检查和规则获取于一身,已经违反了单一职责原则。
很显然,我们要把两个刚刚添加的,可恶的接口提取出来,形成一个新的类才行。
Class RuleReader { public: RuleReader(); ~RuleReader(); void InitRules(); void GetDefaultRules(); void GetConfigRules(); };
慢着,我们不是说不同的过滤器规则不同,保存的形式也不同吗?好吧,看来还要用上抽象,让每个过滤器子类同时再继承RuleReader类。完成后的代码就是下面这个样子,我们的DataCheckFilter类做AuthFilter类同样的处理就可以了:
Class Filter { public: Filter(); virtual ~Filter(); virtual void CheckRequest(string &request) = 0; }; Class RuleReader { public: RuleReader(); virtual ~RuleReader(); void InitRules(); virtual void GetDefaultRules() = 0; virtual void GetConfigRules(); }; void RuleReader::InitRules() { ...... GetDefaultRules(); GetConfigRules(); ...... } Class AuthFilter : public Filter , public RuleReader { public: AuthFilter(); ~AuthFilter(); virtual void GetDefaultRules(); virtual void GetConfigRules(); virtual void CheckRequest(string &request); };
编译一下吧,惨了,没有通过。看看哪里出了问题,原来是Filter中的InitRules接口不见了,在PlugIn类中调用Filter的InitRules时就出现了问题。
等等,这个主意怎么样?我们的过滤器子类不都继承了RuleReader吗,我们把Filter *强制转成RuleReader *不就可以了嘛。
呃,我可不认为这是个好主意。另外,除了上面这一个问题,这段代码仍然存在着其他问题:
1)我们刚刚把规则读取接口提取出来形成了一个新的类,目的是为了实现单一职责原则。但是刚刚过滤器子类对RuleReader的继承,不是让我们的功夫白费了。这些丑陋的子类还是职责不明啊。
2)抛开单一职责不说,从另一个角度分析,这段代码的臭味也不小。继承只有在子类“IS A”父类的情况下使用,在这里我们更应该使用“HAS A”的关系。想一下,过滤器有一个规则读取器,用来读取相应的规则,这样的话看起来还不错。
3)当我们某一天需要的仅仅是读取AuthFilter的规则,而不需要对数据做检查时,仍然要让AuthFilter调用RuleReader的接口去做这件事吗?当然是不合适的。没关系,到那个时候再派生出一个专门读取规则的子类来不就完了吗?好,让你说中了。与其火烧眉毛了才做,不如现在就派生出来,而且也可以解决我们现在的这一系列问题。
那么下一步我们要怎么重构呢?先休息一下,下一节我们继续。。。
相关文章推荐
- 面向对象的企业开发(2)事务脚本设计模式代码讲解
- 代码的坏味道,重构,模式
- 请用代码简单描述一下Singleton、抽象工厂、Bridge、Composite(任选三个)的设计模式的概念
- 简单抽象工厂设计模式代码
- 从抽象到模式——面向对象之旅(三)、设计模式的力量
- 代码的坏味道,重构,模式
- 代码的坏味道,重构,模式
- 代码坏味道,重构与模式
- 代码设计(6.1)《深入PHP:面向对象、模式与实践》
- Java设计模式笔记之抽象工厂代码示例
- java_面向对象_02_静态(代码块)_main_单例设计模式
- 面向对象的抽象思想和模式设计如何连接
- Delphi 设计模式:《HeadFirst设计模式》Delphi代码---工厂模式之抽象工厂
- 从抽象到模式——面向对象之旅(一)、抽象的魅力
- 设计模式之抽象工厂 代码示例
- PHP面向对象设计模式-9.2-【抽象工厂】模式代码演化实例
- Android中MVP模式的抽象MVP类,减少代码量,增加中断机制(三)
- 判断程序是否运行的代码 (有点单例模式的味道)
- 用人类的思维方式去写更容易维护的代码——用<抽象-基础-逻辑>模式开发Java(Android)程序
- 简单抽象工厂设计模式代码