这就是著名的
Liskov替换原则(LSP),又称
里氏替换原则。
对于这个原则,通俗一些的理解就是,父类的方法都要在子类中实现或者重写,不允许子类出现父类所没有定义的方法。
我们前面说过依赖颠倒原则,说的是父类不能依赖子类,它们都要依赖抽象类。我们说这种依赖是我们实现代码扩展和运行期内绑定(多态)的基础。因为一旦类的使用者依赖某个具体的类,那么对该依赖的扩展就无从谈起;而依赖某个抽象类,则只要实现了该抽象类的子类,都可以被类的使用者使用,从而实现了系统的扩展。
但是,光有依赖颠倒原则,并不一定就使我们的代码真正具有良好的扩展性和运行期内绑定。请看下面的代码:
public class Animal
{
private String name;
public Animal(String name)
{
this.name = name;
}
public void descriptiion()
{
System.out.println(“This is a(an) ”+name);
}
}
下面是它的子类:
public class Cat extends Animal
{
public Cat(String name)
{
super(name);
}
public void mew()
{
System.out.println(“The cat is saying like ‘mew’”);
}
}
然后我们来看看Dog的实现:
public class Dog extends Animal
{
public Dog(String name)
{
super(name);
}
public void bark()
{
System.out.println(“The dog is saying like ‘bark’”);
}
}
最后,我们来看客户端的调用:
public decriptionTheAnimal(Animal animal)
{
if(animal instanceof Cat)
{
Cat cat = (Cat)animal;
Cat.decription();
Cat.mew();
}
else if(animal instanceof Dog)
{
Dog dog = (Dog)animal;
Dog.decription();
Dog.bark();
}
}
通过上面的代码,我们可以看到虽然客户端的依赖是对抽象的依赖,但依然这个设计的扩展性不好,运行期绑定没有实现。
是什么原因呢?其实就是因为不满足里氏替换原则,子类如Cat有mew()方法父类根本没有,Dog类有bark()方法父类也没有,两个子类都不能替换父类。这样导致了系统的扩展性不好和没有实现运行期内绑定。
现在看来,一个系统或子系统要拥有良好的扩展性和实现运行期内绑定,有两个必要条件:第一是依赖颠倒原则;第二是里氏替换原则。这两个原则缺一不可。
里氏替换原则的好处:
第一、保证系统或子系统有良好的扩展性。只有子类能够完全替换父类,才能保证系统或子系统在运行期内识别子类就可以了,因而使得系统或子系统有了良好的扩展性。
第二、实现运行期内绑定,即保证了面向对象多态性的顺利进行。这节省了大量的代码重复或冗余。避免了类似instanceof这样的语句,或者getClass()这样的语句,这些语句是面向对象所忌讳的。
第三、有利于实现契约式编程。契约式编程有利于系统的分析和设计,指我们在分析和设计的时候,定义好系统的接口,然后再编码的时候实现这些接口即可。在父类里定义好子类需要实现的功能,而子类只要实现这些功能即可。
我们知道,在我们的大多数的模式中,我们都有一个共同的接口,然后子类和扩展类都去实现该接口。这里,我们以命令模式为例。
下面是一段原始代码:
if(action.equals(“add”))
{
//do add action
……
}
else if(action.equals(“view”))
{
//do view action
……
}
else if(action.equals(“delete”))
{
//do delete action
……
}
else if(action.equals(“modify”))
{
//do modify action
……
}
我们首先想到的是把这些动作分离出来,就可能写出如下的代码:
public class AddAction
{
public void add()
{
//do add action
……
}
}
public class ViewAction
{
public void view()
{
//do view action
……
}
}
public class deleteAction
{
public void delete()
{
//do delete action
……
}
}
public class ModifyAction
{
public void modify()
{
//do modify action
……
}
}
我们可以看到,这样代码将各个行为独立出来,满足了单一职责原则,但这远远不够,因为它不满足依赖颠倒原则和里氏替换原则。
下面我们来看看命令模式对该问题的解决方法:
首先是定义一个接口:
public interface Action
{
public void doAction();
}
然后是各个实现:
public class AddAction implements Action
{
public void doAction()
{
//do add action
……
}
}
public class ViewAction implements Action
{
public void doAction()
{
//do view action
……
}
}
public class deleteAction implements Action
{
public void doAction()
{
//do delete action
……
}
}
public class ModifyAction implements Action
{
public void doAction()
{
//do modify action
……
}
}
这样,客户端的调用大概如下:
public void execute(Action action)
{
action.doAction();
}
看,上面的客户端代码再也没有出现过instanceof这样的语句,扩展性良好,也有了运行期内绑定的优点。
在这里,只拿出命令模式一个模式来作为例子来看看模式是怎么遵从里氏替换原则的。其他的模式的例子就不再拿出来说。我们可以看到,几乎所有的模式都遵从了该原则,不信可以自行找来看看。
分享到:
相关推荐
第1页/共30页 Java面向对象程序设计杨晓燕面向对象基本原则和模式全文共30页,当前为第1页。 类的UML图 UML类图显示类的三个组成部分,第一是Java中定义的类名,第二个是该类的属性,第三个是该类提供的方法。 第一...
LISKOV替换原则 第11章 DIP:依赖倒置原则 第12章 ISP:接口隔离原则 第13章 写给C#程序员的UML概述 第14章 使用UML 第15章 状态图 第16章 对象图 第17章 用例 第18章 顺序图 第19章 类图 第20章 咖啡的启示 第三...
本书中,享誉全球的软件开发专家和软件工程大师RobertC.Martin深入而生动地使用真实案例讲解了面向对象基本原则、重要的设计模式、UML和敏捷实践等程序员必备的知识。 本书于2003年荣获第13届Jolt大奖,是C++和Java...
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能...
第10章 Liskov替换原则(LSP) 第11章 依赖倒置原则(DIP) 第12章 接口隔离原则(ISP) 第三部分 薪水支付案例研究 第13章 COMMAND模式和ACTIVE OBJECT模式 第14章 TEMPLATE METHOD模式和STRATEGY模式:继承与委托 ...
第十章 Liskov替换原则(LSP) 10.1 Liskov替换原则(LSP) 10.2 一个违反LSP的简单例子 10.3 正方形和矩形,更微妙的违规 10.4 一个实际的例子 10.5 用提取公共部分的方法代替继承 10.6 启发式规则和习惯用法 10.7 ...
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能...
C++设计模式 什么是设计模式 “每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的...Liskov 替换原则(LSP) 子类必须能够替换它们的基类(IS-A)。 继承表达类型抽象。 接口隔离原则(ISP)
里氏替换原则(Liskov Substitution Principle,LSP) 只要父类出现的地方都可以用子类替换。 依赖倒置原则(Dependece Inversion Principle,DIP) 面向接口编程。细节应该依赖抽象。 依赖可以传递。 依赖有三...
http 里氏替换原则在面向对象的程序设计中,里氏替换原则(Liskov替代原则)是对子类型的特别定义。它由芭芭拉·利斯科夫(Barbara Liskov)在1987年在一次会议上称为“数据”的抽象与层次”的演说中首先提出。里氏...
替换原则时将看到的, class Square实际上并不是class Rectangle特例,因为简而言之,在不破坏应用程序的情况下, Square的实例不能被Rectangle的实例替换. 人们普遍认为面向对象编程是关于模仿现实世界的对象,但...
design_patterns_cpp 设计模式范例整理 ...Liskov替换原则(LSP) 子类必须能够替换其的基基(IS-A) 继承表达类型抽象 接口隔离原则(ISP) 不应该强迫客户程序依赖它们不用的方法 连接应该小而完备 优先使
中实用的面向对象设计 - 笔记 笔记: / 预订: 这是我在看书时做的一些笔记。 如果您想进行改进,请随时向我发送拉取请求。 第 1 章 - 面向对象设计 为什么要设计? 应用程序的变化是不可避免的。 由于对象之间的...
2、里氏代换原则(Liskov Substitution Principle)里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类...
LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类(父类)可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类(父类),软件单位的功能不受到影响时,基类(父类)才能...
面向对象设计模式有五项原则和一条规则。SOLID原则和DP规则。 (在对象设计模式中存在的是大原则和一个法则。SOLID原则和DP法则。) SRP(单一职责原则) (单一职责) OCP(Open Close Principle):启用热插拔,提高...
13.6.4 Liskov替换法则(LSP) 485 13.6.5 单一职责原则(SRP) 486 13.6.6 依赖倒置原则(DIP) 486 13.6.7 接口分离原则(ISP) 489 13.6.8 面向对象设计时需要注意的 问题 490 本章常见技术面试题 491 本章小结 ...
13.6.4 Liskov替换法则(LSP) 485 13.6.5 单一职责原则(SRP) 486 13.6.6 依赖倒置原则(DIP) 486 13.6.7 接口分离原则(ISP) 489 13.6.8 面向对象设计时需要注意的 问题 490 本章常见技术面试题 491 本章小结 ...