设计模式
设计模式作为软件开发中可能会面临的问题的最佳解决方案,可以作为我们开发过程中的“模板”,学习这些模式可以提升我们开发的效率与稳定性。
根据设计模式的参考书 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 中所提到的,总共有 23 种设计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。
具体的设计模式会尽快在下一篇笔记中总结出来,但在这之前需要了解一下设计模式的六大原则,因为23种设计模式本质上是对这六大原则的实践,学习并深刻理解这些原则有助于我们理解设计模式,同时更有助于我们理解设计模式的本质,达到传说中那种忘记设计模式的境界哈哈哈。
设计模式的六大原则
1、开闭原则
开闭原则是六大原则中的核心原则,其含义是对扩展开放,对修改关闭。意思是当我们对写好的程序进行拓展的时候,不可以修改原有的代码,而应该在原来的代码上进行扩展。
修改原有的代码是很容易出问题的,比如说修改了一个函数,那么使用该函数的地方是否也需要修改,之前的系统中有哪些地方使用了这些函数,都是很容易忽略或出错的地方!
想要做到开闭原则,需要我们的代码有相当的可扩展性,为了实现这样的效果,接口和抽象类是非常关键的。
2、里氏代换原则
里氏代换原则是抽象化过程的具体规范,其含义是任何基类出现的地方,子类一定也可以出现。换句话说,就是将任何一个基类对象换成子类的对象,系统的逻辑行为与正确性不变。该原则规范了基类和派生类之间的关系与原则,同时也告诉了我们什么时候应该使用继承,什么时候不应该使用继承。
我们可以从数学的集合包含角度来考虑该问题,由于基类包括派生类,那么基类符合的特性,派生类也一定符合。再来个反例,动物都可以走路,鱼是动物的子类,而鱼只会游泳却不会走路,这样就不对了,所以鱼就不可以作为动物的子类。
里氏替换原则在继承和多态方面的含义
- 里氏替换原则是针对继承而言的,如果继承是为了实现代码重用,也就是为了共享方法,那么共享的父类方法就应该保持不变,不能被子类重新定义。子类只能通过新添加方法来扩展功能,父类和子类都可以实例化,而子类继承的方法和父类是一样的,父类调用方法的地方,子类也可以调用同一个继承得来的,逻辑和父类一致的方法,这时用子类对象将父类对象替换掉时,当然逻辑一致,相安无事。
- 如果继承的目的是为了多态,而多态的前提就是子类覆盖并重新定义父类的方法,为了符合LSP,我们应该将父类定义为抽象类,并定义抽象方法,让子类重新定义这些方法,当父类是抽象类时,父类就是不能实例化,所以也不存在可实例化的父类对象在程序里。也就不存在子类替换父类实例(根本不存在父类实例了)时逻辑不一致的可能。
不符合LSP的最常见的情况是,父类和子类都是可实例化的非抽象类,且父类的方法被子类重新定义,这一类的实现继承会造成父类和子类间的强耦合,也就是实际上并不相关的属性和方法牵强附会在一起,不利于程序扩展和维护。
里氏替换原则的具体规则
里氏替换原则的含义就是任何基类出现的地方,子类一定也可以出现,以下规则也都是为了含义服务的。
一、子类方法不能违背父类方法对输入输出异常的约定
1. 前置条件不能被加强
前置条件即输入参数是不能被加强的,就像上面Cache的示例,Redis子类对输入参数Key的要求进行了加强,此时在调用处替换父类对象为子类对象就可能引发异常。也就是说,子类对输入的数据的校验比父类更加严格,那子类的设计就违背了里式替换原则。
2. 后置条件不能被削弱
后置条件即输出,假设我们的父类方法约定输出参数要大于0,调用父类方法的程序根据约定对输出参数进行了大于0的验证。而子类在实现的时候却输出了小于等于0的值。此时子类的涉及就违背了里氏替换原则
3. 不能违背对异常的约定
二、子类方法不能违背父类方法定义的功能
三、子类必须完全实现父类的抽象方法
相关的具体内容大家也可以看这篇参考博客,写的很好。设计模式六大原则(二)----里式替换原则
3、依赖倒转原则
原则思想:高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象,抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
描述:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是底层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
优点:可以减少需求变化带来的工作量,做并行开发更加友好。
4、接口隔离原则
原则思想:类和类之间应该建立在最小接口的上。
描述:类A通过接口依赖B,类C通过接口依赖D,如果接口类A和类B不是最小的接口,则依赖的类B和类D必须要实现他们不需要的方法。
优点:提高程序的灵活度,提高内聚,减少对外交互,使得最小的接口做最多的事情。
5、迪米特法则(最少知道原则)
原则思想:一个对象应当对其他对象有尽可能少地了解,简称类间解耦。
描述:一个类尽量减少自己对其他对象的依赖,原则是低耦合,高内聚,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。
优点:低耦合,高内聚
6、单一职责原则
原则思想:一个方法只负责一件事情。
描述:单一职责原则很简单,一个方法 一个类只负责一个职责,各个职责的程序改动,不影响其它程序。 这是常识,几乎所有程序员都会遵循这个原则。
优点:降低类和类的耦合,提高可读性,增加可维护性和可拓展性,降低可变性的风险。