软件设计的7大原则-自己的理解

选自javaweb软件工程设计七大原则。分别如下:

一、 开闭原则(OCP)

        开闭原则(Open-Closed Principle):一个软件实体应当对扩展开放,对修改关闭。

        客户的需求是不稳定的,通过扩展已有的软件系统而不是通过修改软件系统来满足客户的需求,这样的软件系统就满足开-闭原则,即软件系统要有一定的灵活性和适应性。

        已有的模块,特别是抽象层的模块不能修改,保证软件系统的稳定性和延续性。解决问题的关键是抽象化,把它与具体实现分离开来。接口(interface),抽象类的应用对可变性封装:将可变性封装到一个对象里。优点是通过扩展已有软件系统,可以提供新的行为,以满足对软件的新的需求,使变化中的软件有一定的适应性和灵活性。已有软件模块,特别是最重要的抽象层模块不能再修改,这使变化中的软件系统有一定的稳定性和延续性。

实现开闭原则的关键:

       抽象化是解决问题的关键,在面向对象的编程语言里,可以给系统定义出一套相对较为固定的抽象设计,此设计允许无穷无尽的行为在实现层被实现。在语言里,可以给出一个或多个抽象类或者接口,规定出所有的具体类必须提供的方法的特征作为系统设计的抽象层。这个抽象层预见了所有的可扩展性,因此,在任何扩展情况下都不会改变。这就使得系统的抽象不需要修改,从而满足了开闭原则的第二条,对修改关闭。同时,由于从抽象层导出一个或多个新的具体类可以改变系统的行为,因此系统的设计对扩展是开放的,这就满足了开闭原则的第一条。


对可变性的封装原则:

      这是对开闭原则的另外一种描述,它讲的是找到一个系统的可变因素,将之封装起来。该原则意味着两点:
     ①一种可变性不应当散落在代码的很多角落,而应当封装到一个对象里面。继承应当被看做是封装变化的方法,而不应该被认为是一种从一般对象生成特殊对象的方法。
     ②一种可变性不应当与另外一种可变性混合在一起。这意味着一般的继承层次不会超过两层。

有一个图表打印的模型,可以打印出饼图,条形图……

一个不合格开闭原则的设计模型图如下:

显然如果在要有一个打印其他图像的类进行扩展,则需要修改ChartDisplay类中的内容,显然不合适,这么设计是有问题的。

正确的是code如下:

引入了一个抽象类,这种写法有点类似与抽象工厂模式。

二、 里氏代换原则(LSP)

        里氏代换原则(Liskov Substitution Principle):子类型必须能够替换它们的基类型。反过来的代换不成立。

        当两个具体类关系违反里氏代换原则时,一种办法是抽象出一个基类,作为这两个类的父类,一种是应用组合聚合关系建立关系。不要为了使用某些类的方法(功能)而滥用继承。

 

关键知识点:
       里氏替换原则的概念,基类可以出现的地方,派生类同样可以出现;
       里氏替换原则的反命题不成立;
       对于AB两个类,BA派生,如果这种继承违反里氏替换原则,可以采用如下方法进行重构:将AB的共同行为抽象出来,建立一个抽象类CAB都是C的派生类

子类能做父类所有的事情,例如,person能使用普通枪,police能使用警枪,当然也能使用简单的枪。

尽量不要重写父类的方法,以免破坏原有方法的特性,如果子类不具备父类的行为方法

三、 依赖倒置原则(DIP)

      依赖倒置原则(Dependence Inversion Principle):具体要依赖于抽象,抽象不要依赖于具体。

      简单的说,依赖倒置原则要求客户端依赖于抽象耦合。原则表述:抽象不应当依赖于细节;细节应当依赖于抽象;要针对接口编程,不针对实现编程。传递参数,或者在组合聚合关系中,尽量引用层次高的类。主要是在构造对象时可以动态的创建各种具体对象,当然如果一些具体类比较稳定,就不必再弄一个抽象类做它的父类,这样有画蛇添足的感觉。

 

      三种耦合关系

     ①零耦合关系,如果两个类没有耦合关系,就称之为零耦合;

     ②具体耦合,具体耦合发生在两个具体的类之间,经由一个类对另外一个具体类的直接引用造成的。

     ③抽象耦合关系,抽象耦合关系发生在一个具体类和一个抽象类之间,使用两个必须发生关系的类之间存在有最大的灵活性。

     依赖倒转原则的另外一种表述是:

     要针对接口编程,不要针对实现编程(Program to an interface, not an implementation)[GOF95]。同样,在处理类之间的耦合关系时,尽量使用抽象耦合的形式。

     氏替换原则是依赖倒转原则的基础。

     工厂模式、模板模式、迭代子模式都是对依赖倒转原则的体现。

四、 接口隔离原则(ISP)

     接口隔离原则(Interface Segregation Principle):使用多个专门的接口比使用单一的总接口总要好。

     换而言之,从一个客户类的角度来讲:一个类对另外一个类的依赖性应当是建立在最小接口上的。过于臃肿的接口是对接口的污染。不应该强迫客户依赖于它们不用的方法。定制服务的例子,每一个接口应该是一种角色,不多不少,不干不该干的事,该干的事都要干。

 

     接口隔离原则讲的是为同一个角色提供宽、窄不同的接口,以应对不同客户端的需求,下例以set为例讲解:

     TreeSet是一种使用树状数据结构的可排序的Set容器,它既实现了Set接口(通过继承AbstractSet),又实现了SortedSet接口。这里并没有提供一个总的既有排序功能又有Set功能的总接口,而是针对不同的需求,将两种角色分别定义成两种接口,这样的设计,是符合接口隔离原则。

 

     接口污染

    将不同角色的接口合并为一个臃肿的接口就是对接口的污染。这种做法同时违反了可变性封装原则,它将不同的可变性封装到了同一个软件实体中。

    对接口隔离原则的具体应用可以参考备忘录模式和迭代子模式。

五、 合成/聚合复用原则(CARP)

     合成/聚合复用原则(Composite/Aggregate Reuse Principle或CARP):就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新对象通过向这些对象的委派达到复用已有功能的目的。

     简而言之,要尽量使用合成/聚合,尽量不要使用继承。区分Has a和Is a的问题。

     合成:一荣俱荣,一损俱损,整体和部分的生命周期是一样的。是比聚合关系强的关系。它要求普通的聚合关系中代表整体的对象负责代表部分对象的生命周期,合成关系是不能共享的。代表整体的对象需要负责保持部分对象和存活,在一些情况下将负责代表部分的对象湮灭掉。代表整体的对象可以将代表部分的对象传递给另一个对象,由后者负责此对象的生命周期。换言之,代表部分的对象在每一个时刻只能与一个对象发生合成关系,由后者排他地负责生命周期。

     聚合:部分可以是整体的一部分,也可以脱离整体而存在。例如,汽车类与引擎类、轮胎类,以及其它的零件类之间的关系便整体和个体的关系。聚合关系也是通过实例变量实现的。但是关联关系所涉及的两个类是处在同一层次上的,而在聚合关系中,两个类是处在不平等层次上的,一个代表整体,另一个代表部分。

六、 迪米特法则(LoD)

     迪米特法则(Law of Demeter或简写LoD)又叫最少知识原则(Least Knowledge Principle或简写为LKP):一个对象应当对其它对象有尽可能少的了解。不要和陌生人说话。

 

    ξ 1 迪米特法则的各种表述

    ①只与你直接的朋友们通信;

    ②不要跟“陌生人”说话;

来源:fengchao2016

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2017年1月20日
下一篇 2017年1月20日

相关推荐