设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
设计原则都是要实现高内聚低耦合,设计模式实现这些原则,从而达到了代码复用、可维护性增加的目的。
一、单一职责原则( The single responsibility principle :SRP)
对于一个类而言,应该只有一个引起它变化的原因,即一个类只负责一项职责。
- 问题:类T负责两个不同的职责:职责A和职责B。当由于职责A需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责B功能发生故障。
- 解决方案:分别建立两个类T1、T2,使T1完成职责A功能,T2完成职责B功能。这样,当修改类T1时,不会使职责B发生故障风险;同理,当修改T2时,也不会使职责A发生故障风险。
二、开放-封闭原则(The open-close principle : OCP)
软件实体(类、模块、函数等等)应该是对扩展开放,对修改封闭。
“对扩展开放”,意味着模块的行为是可扩展的。当软件的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。换句话说,我们可以改变模块的功能。“
“对修改封闭”,意味着对模块行为进行扩展时,不必改动模块的源代码或者二进制码。模块的二进制可执行版本,无论是可链接的库、DLL、或者Java的.jar文件,都无需改动
- 问题:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。
- 解决方案:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
三、里氏替换原则(Liskov Substitution Principle:LSP)
子类型必须能够替换掉它们的基类型,子类可以扩展父类的功能,但不能改变父类原有的功能。 正是子类型的可替换性才让使用基类类型的模块在无需修改的情况下就可以扩展。
- 子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。为了保证系统的扩展性,在程序中通常使用父类来进行定义,如果一个方法只存在子类中,在父类中不提供相应的声明,则无法在以父类定义的对象中使用该方法。
- 尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法。运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能,同时无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。
四、依赖倒置原则(Dependence Inversion Principle:DIP)
高层模块不应该依赖于低层模块,两者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
实现依赖倒置原则的三个手段:
- 没有具体超类原则――任何类不应该从具体类派生;
- 任何类都不应该持有一个指向具体类的指针或者引用;
- 任何方法都不应该覆写它的任何基类中已经实现了的方法(非虚函数的方法)。
- 问题:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
- 解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。
一个客户类不应该依赖于他们不用的方法,一个客户类对另外一个类的依赖性应当是建立在最小的接口上。 也就是说,使用多个专门的接口比使用单一的总接口要好。
- 问题:类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法。
- 解决方案:将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。
六、迪米特法则(Law Of Demeter:LOD)
一个对象应当对其它对象有尽可能少的了解。
- 问题:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
- 解决方案:尽量降低类与类之间的耦合。
六大原则其实就是告诉我们用抽象构建框架,用实现扩展细节的注意事项而已:单一职责原则告诉我们
实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向
接口编程;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低
耦合。而开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。