这就是著名的
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这样的语句,扩展性良好,也有了运行期内绑定的优点。
在这里,只拿出命令模式一个模式来作为例子来看看模式是怎么遵从里氏替换原则的。其他的模式的例子就不再拿出来说。我们可以看到,几乎所有的模式都遵从了该原则,不信可以自行找来看看。
分享到:
相关推荐
3. 里氏替换原则(Liskov Substitution Principle, LSP):子类型必须能够替换掉它们的基类型。这保证了继承不会破坏原有的程序行为。 4. 接口隔离原则(Interface Segregation Principle, ISP):不应该强迫客户端...
里氏替换原则是由Barbara Liskov提出的,该原则强调“任何基类出现的地方,子类都可以替换”。这一原则确保了继承关系中的子类型可以完全替代其父类型的行为,从而保证了系统的稳定性。 #### 实际应用场景 例如,...
面向对象编程是Java的核心,它遵循一系列基本原则和设计模式,以确保代码的可维护性、扩展性和高效性。本教程“Java面向对象程序设计杨晓燕面向对象基本原则和模式”着重讲解了这些关键概念。 首先,UML(Unified ...
4. **里氏替换原则(Liskov Substitution Principle, LSP)** - **原则阐述**:子类必须能够替换它们的基类。 - **实践意义**:确保继承关系中的子类可以安全地替代基类,避免因不当使用继承而导致的行为异常。 5...
首先,我们来了解四个基础的面向对象设计原则:单一职责原则(SRP)、开放封闭原则(OCP)、里氏替换原则(LSP)和接口隔离原则(ISP)。 1. 单一职责原则(Single Responsibility Principle, SRP):一个类或模块...
3. **里氏替换原则 (Liskov Substitution Principle, LSP)** - 子类型必须能够替换掉它们的基类型。这意味着子类可以完全替代父类在程序中的角色,而不会导致任何错误或需求的改变。这有助于保持代码的稳定性和可...
3. 里氏替换原则(Liskov Substitution Principle, LSP): 子类型必须能够替换掉它们的基类型,不会破坏程序的正确性。保证了多态性。 4. 接口隔离原则(Interface Segregation Principle, ISP): 避免客户端依赖...
面向对象设计原则是Java开发中不可或缺的基础,它们指导着开发者创建可维护、可扩展和可复用的软件系统。本文将深入探讨其中的两个核心原则:“开-闭”原则(Open-Closed Principle, OCP)和里氏代换原则(Liskov ...
3. **里氏替换原则(Liskov Substitution Principle, LSP)**:子类型必须能够替换掉它们的基类型。这意味着在所有使用基类型的地方,都可以安全地使用子类型,而不会影响程序的正确性。遵守此原则能确保程序的稳定...
面向对象编程(Object-Oriented Programming,简称OOP)是一种重要的编程范式,它基于“对象”的概念,通过封装、继承和多态等机制来组织代码。在OOP中,有四个基本原则,它们是设计高质量、可维护软件的基石。这四...
**里氏替换原则 (LSP, Liskov Substitution Principle)** - **定义**: 子类的对象必须能够完全替代其父类的对象。 - **目的**: 确保子类可以作为父类的完美替代,不会因为类型的变化而导致程序出现错误。 - **实践...
首先,我们要理解面向对象设计的四大原则:单一职责原则(Single Responsibility Principle, SRP)、开放封闭原则(Open-Closed Principle, OCP)、里氏替换原则(Liskov Substitution Principle, LSP)和接口隔离...
3. 里氏替换原则(Liskov Substitution Principle, LSP):子类应该能够替换它们的基类。即派生类应该能够完全替代基类出现在程序中,同时保持程序的正确性。 4. 依赖倒置原则(Dependency Inversion Principle, ...
以下将详细介绍面向对象设计的六大原则:单一职责原则(Single Responsibility Principle, SRP)、开放封闭原则(Open-Closed Principle, OCP)、里氏替换原则(Liskov Substitution Principle, LSP)、依赖倒置原则...
面向对象六大原则是软件开发中面向对象编程的重要理论基础,它们分别是单一职责原则(Single Responsibility Principle, SRP)、开闭原则(Open/Closed Principle, OCP)、里氏替换原则(Liskov Substitution ...
1. **LSP (Liskov Substitution Principle, 替换原则)** - 描述:父类出现的地方,子类都可以替换。 - 解释:子类应该能够替换其基类,并且程序的行为不会改变。这意味着子类不能添加新的约束或改变父类的方法签名...
里氏替换原则(Liskov Substitution Principle,简称LSP)是面向对象设计的基本原则之一,它是由美国计算机科学家芭芭拉·里科夫(Barbara Liskov)提出的。这个原则强调的是在软件工程中,子类型必须能够替换它们的...
本系列课程"面向对象设计模式纵横谈"将深入探讨这些模式及其在C#中的应用。 首先,我们需要理解面向对象的基本概念,包括封装、继承和多态。封装是隐藏对象的属性和实现细节,只对外提供公共访问方式,以此提高安全...
此外,可能还包括里氏替换原则(Liskov Substitution Principle)、依赖倒置原则(Dependency Inversion Principle)和接口隔离原则(Interface Segregation Principle)。这些原则旨在提高代码的可读性、可维护性...
#### 原则4:Liskov替换原则(LSP) **Liskov替换原则** - LSP指出,如果S是T的子类型,则任何T都可以被替换为S而不影响程序的正确性。这确保了子类能够无缝替换基类。 **LSP示例** - 展示了如何通过遵循LSP来...