概念
复习到了LSP原则,也叫里氏替换原则,是软件构造当中,java的23中设计模式的6个设计原则之一,一句话来说就是在所有基类可以出现的地方,都必须可以使用子类进行替换。
这个原则的提出者是Barbara Liskov,是面向对象最重要的几大原则之一;
LSP原则与子类型多态(Behavioral subtyping)
子类型多态和LSP一个意思,Java当中,在编译的时候会进行以下规则的静态类型检查:
这里提到了协变和反协变:
协变co-variance:返回值或者参数、异常的类型只能一样或者是原来类型的子类,可以简单理解为更加具体;
反协变contra-variance:和协变相反,可以简单理解为更加宽泛;
LSP具体一些就是:
即子类必须比父类有更强的不变量,更弱的前置条件,更强的后置条件;
更强的不变量意味着,表示空间的合法子集范围更小;
更弱的前置条件意味着能够处理更多种的参数,使用者对参数的使用更加随意,开发者需要思考更多来处理不同的参数;
例子:
在编程语言中,LSP原则依赖于以下限制:
和前面所述一个性质。
集合类的子类问题
先来看一个问题:
我们知道ArrayList数组是List数组的子类型,前提是二者的元素是同一个类型的对象实例,但是如果同样是List数组,其中包含的元素是父子类关系的话,这两个list还是父子关系吗?
答案:不是
实际上,集合类型的父子关系,和集合内的元素类型的关系是完全没有关系的,那么如果我们想要实现集合类型的LSP替换应该如何做呢?
这时候就需要用到通配符 ? 了,符号 ?就是通配符;
List<?>表示一个包含元素为未知类型对象实例的List;
因此List<?>也就是所有List<Object>的一个父类(Obeject是具体的对象),可以使用List<?>来实现LSP替换:
如果想要更加具体一点,防止List<?>被不想出现的对象所替换,通配符还提供了上确界和下确界的方法:
<? super A>表示集合的元素是未知的,但是必须是A的父类或者A本身;
<? extends A>表示集合的元素是未知的,但是必须是A的子类或者A本身;
三种写法的范围关系如上;
6大设计原则
最后提一下Java当中的设计模式的6大设计原则,他们是23种设计模式的基础:
1、开闭原则(Open Close Principle)
开闭原则说的是对扩展开放,对修改关闭,在程序需要修改的时候不能修改原有的代码
2、里氏替换原则LSP
上面已经介绍过了。
3、依赖倒转原则(Dependence Inversion Principle)
抽象的模块不应该依赖于具体的模块,具体的模块应该依赖于抽象的模块;
例子:
4、接口隔离原则(Interface Segregation Principle)
客户端不应该依赖于他们不需要的方法,说人话就是你提供给客户端一个接口,这个接口可以实现若干种操作,但是每个客户端只需要用到其中的一个操作,那么这个接口的实现就是不好的,应该将其分解成多个接口,每个接口提供一种特定功能,不同的接口向不同的客户端提供服务,客户端只需要访问自己所需要的端口。
下面的接口实现是符合接口隔离原则的。
5、迪米特法原则(Demeter Principle)
每个模块在系统当中应该是相对独立的,尽量做到一个类一个责任,一个功能,出了问题容易修正
6、合成复用原则(Composite Reuse Principle)
尽量使用组合/聚合的方式,少用继承。
关于设计原则和设计模式的相关内容,参考了这篇文章:
Java中常用的设计模式_Superme-CSDN博客_java 设计模式
其中比较全面的介绍了23种设计模式。