单一职责原则(Single Responsibility)
一个类只负责一个职责,或者定义为:一个类应该只有一个发生变化的原因。
单一职责原则要求一个类不能太“累”!在软件系统中,一个类承担的职责越多,它被复用的可能性就越小,因为耦合性越高,当其中一个职责变化时,可能会影响其他职责的运作,因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封装在同一类中。
单一职责原则是实现高内聚、低耦合的指导方针,它是最简单但又最难运用的原则,需要开发人员发现类的不同职责并将其分离,对开发人员的要求比较高。
开闭原则(Open-Closed)
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。应尽量在不修改原有代码的情况下进行扩展。
开闭原则要求我们在设计软件实体时,要尽量做到足够好,写好之后只能对其扩展,而不能修改。
随时间的推移软件的需求也会发生诸多变化。当软件系统面对新的需求时,我们应该尽量保证系统的设计框架是稳定的。如果一个软件设计符合开闭原则,那么可以非常方便地对系统进行扩展,而且在扩展时无须修改现有代码,使得软件系统在拥有适应性和灵活性的同时具备较好的稳定性和延续性。随着软件规模越来越大,软件寿命越来越长,软件维护成本越来越高,设计满足开闭原则的软件系统也变得越来越重要。为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。
里氏替换原则(Liskov Substitution)
所有引用父类的地方必须能使用其子类的对象。
通俗一点地讲,里氏替换原则就是“老爸能干的事,换成儿子去干也可以”,因为儿子继承了老爸的一切,但是反过来则不行。里氏替换原则是实现开闭原则的重要方式之一,它克服了继承中重写父类造成的可复用性变差的缺点。
在使用里氏代换原则时需要注意以下几个问题:
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
- 子类中可以增加自己特有的方法。
- 当子类的方法重载父类的方法时,输入参数要比父类的方法更宽松或相等。
- 当子类的方法实现父类的方法时,输出结果要比父类的方法更严格或相等。
在运用里氏代换原则时,尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法,运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能,同时无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。
依赖倒置原则(Dependency Inversion)
抽象不应该依赖于细节,细节应当依赖于抽象。即要针对接口编程,而不是针对实现编程。
依赖倒置原则基于一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。
依赖倒置原则的核心是面向接口编程,在实际编程中,我们要做到以下几点:
- 每个类尽量都要有抽象类或接口,或者两者都有。
- 变量的声明类型尽量是抽象类或接口。
- 使用继承时遵循里氏替换原则。
接口分离原则(Interface Segregation)
要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
接口分离原则指在设计时采用多个与特定客户类有关的接口比采用一个通用的接口要好。即,一个类要给多个客户使用,那么可以为每个客户创建一个接口,然后这个类实现所有的接口;而不要只创建一个接口,其中包含所有客户类需要的方法,然后这个类实现这个接口。
它与“单一职责原则”的区别:
- 单一职责原则原注重的是职责;而接口隔离原则注重对接口依赖的隔离。
- 单一职责原则主要是约束类,它针对的是程序中的实现和细节;而接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。
运用接口隔离原则,需要做到以下几点:
- 接口尽量小,但是要有限度。
- 为依赖接口的类定制服务,只暴露给调用的类需要的方法,将不需要的方法隐藏起来。
- 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。