设计模式灵魂之设计原则

来源:互联网 发布:数据异常值处理 编辑:程序博客网 时间:2024/06/08 03:30

 软件开发中,一个软件的好与坏体现在这个软件面对需求的抵抗能力上,也就是具有良好的扩展性和可维护性上。而要达到这样的要求取决于软件的架构的设计,而软件架构设计的好坏程度取决于设计模式内功的修炼程度,而设计模式内功的修炼程度又取决于对设计模式灵魂-设计原则的理解程度。要使得开发的软件具备接受变化的挑战的能力,那么从一开始就得把设计模式思想,设计原则牢记于心,然后在设计或者已有的应用中,寻找一些可以用到它们的地方。

  今天花了一天的时间去仔细认真的看了《Head First Design Partten》的第一章,这一章主要是通过一个简单的模拟鸭子应用程序讲述了设计原则:“把会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分“。这是每个设计模式背后的精神所在,所有的模式都提供了 一套方法让”系统中的某部分改变不会影响到其他部分“。

  其实刚开始我有个疑问是弄不明白的:书中说到模拟鸭子程序需要扩展,在原有的基础上新增加一种会飞的鸭子,该程序原来是这样的:有个鸭子超类(父类),包含了所有鸭子都会有的叫声行为quack()方法,游泳行为swim(),并有一个抽象方法display(),这方法用于表示每一种鸭子的外观不同,所以用抽象方法,在每个鸭子子类中实现各自的display()行为显示自己的外观。现在要增加一种会飞的鸭子(风筝鸭),我的思路是这样的,在原来的基础上新增加一个类继承鸭子超类,在子类中新增加会飞的行为fly(),这样这个类就表示会飞的鸭子了,因为根据继承的概念,子类可以有自己的独特的行为方法。这样不就解决了。

    呵呵,这个疑问我想了很久,后来想想自己不也正是应用到了那设计原则么?把会变化的部分取出并封装起来,把会变化的部分和不会变化的部分独立起来。这超类不就是不会变化的部分的封装么?而子类中独特的行为不就是会变化的部分的封装么?唉呀,这么简单的都还想了这么久,看来得多钻研钻研设计模式了。

   慢慢看着我就发现了自己想出的那个方法的缺陷:比如后续又要增加会飞的气球鸭的时候,此时按照刚才的思路,继承鸭子父类,在子类中添加特有的会飞的方法fly(),并实现display()方法,展现自己的外表。此时问题就显现出来了,这表现在气球鸭的fly()方法和风筝鸭的fly()方法都是一样的,重复了代码的编写,如果以后再增加会飞的鸭子的话,那么这个fly()方法得写多少遍啊,一般来说重复代码多的时候必然是不好的设计,为什么呢?因为在以后的需求中如果要求改变飞的行为时,那么你就得一个一个类去改变会飞的鸭子类中的fly()方法,而且改动都是一样的,这就是牵一发而动全身啊,多么痛苦的事。在这里这飞的行为对于会飞的鸭子来说是不变的,但从另外一个角度来看这飞的行为又是变化的部分。

    其实接下来很自然的大家都会想到用接口来实现的,因为接口的作用是提供一个规范,这里就把fly()抽取出来,成为一个接口,让会飞的鸭子去继承此接口,但其实这样的做法和上述做法是一样的,因为当要修改飞行的行为时,就必须找到所有继承了这些接口并修改fly方法,也是牵一发而动全身啊。这些在软件开发中很明显的缺点:代码无法复用。因为接口中没有实现方法,所以继承接口无法达到代码复用。

     此时想,如果有多继承就比较好办了,因为把会飞的鸭子类归为一个父类,其中有通用的fly()方法,另外一个所有鸭子的父类,有所有鸭子都具有的行为swim()方法,这样会飞的鸭子就可以同时继承会飞的鸭子父类和通用鸭子父类,这样做就可以克服上述的缺陷,但这样做又会出现新的问题:就是类过度。而且很多语言中不支持多继承。

    那么如何做才能做到此鸭子模拟程序中既有会飞的鸭子,又有不会飞的鸭子,而且以后扩展新的鸭子类型的时候能做到改变最小呢?

    此时,就改考虑到设计原则之一了:“把会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分“。这样子做的好处是代码变化引起的改动变小,使得系统维护性强。这个设计原则的另外一种思考方法是:把会变化的部分取出并封装起来,以便以后可以轻易的改动或扩充这部分,而不影响不需要变化的其他部分。