`
starbhhc
  • 浏览: 649551 次
  • 性别: Icon_minigender_2
  • 来自: 深圳
社区版块
存档分类
最新评论

子类行为不能超过父类行为——由模式谈面向对象的基本原则里氏替换原则

    博客分类:
  • java
阅读更多
原则:子类型必须能够替换它的基类型。
     这就是著名的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这样的语句,扩展性良好,也有了运行期内绑定的优点。
     在这里,只拿出命令模式一个模式来作为例子来看看模式是怎么遵从里氏替换原则的!
分享到:
评论

相关推荐

    OO设计原则-里氏替换原则

    里氏替换原则由芭芭拉·利斯科夫教授于1987年提出,该原则的主要思想可以总结为:“所有引用基类的地方必须能透明地使用其子类的对象。”换句话说: 1. **替换性**:如果一个程序中的某个位置使用了基类对象,那么...

    面向对象设计原则

    以上介绍的五种面向对象设计原则——单一职责原则、里氏替换原则、依赖倒置原则、接口隔离原则和开闭原则,都是面向对象编程领域中非常重要的指导思想。遵循这些原则可以帮助开发者构建出更加健壮、可维护和易于扩展...

    面向对象程序设计——入门

    这份名为“面向对象程序设计——入门”的资料,以PPT的形式,为初学者提供了一个系统的学习路径,涵盖了面向对象的基本概念、核心原则以及在Java语言中的实现方法。 1. **面向对象的基本概念** - **对象**:对象是...

    面向对象设计课件(JAVA)

    除了这些基本概念,面向对象设计还涉及类的设计原则,如单一职责原则(Single Responsibility Principle, SRP)、开闭原则(Open-Closed Principle, OCP)、里氏替换原则(Liskov Substitution Principle, LSP)、...

    设计模式可复用面向对象软件的基础

    面向对象设计原则也是设计模式的基础,包括单一职责原则(Single Responsibility Principle,SRP)、开闭原则(Open-Closed Principle,OCP)、里氏替换原则(Liskov Substitution Principle,LSP)、接口隔离原则...

    面向对象培训资料 面向对象培训资料

    3. 里氏替换原则:子类必须能够替换它们的父类并出现在父类能够出现的任何地方,而不影响程序的正确性。 4. 接口隔离原则:不应该强迫客户端依赖它们不需要的接口,应将一组相关的接口封装在一个单独的接口中。 5....

    面向对象程序设计与原则

    3. 里氏替换原则(Liskov Substitution Principle, LSP):子类型必须能够替换它们的基类型,并且在任何使用基类型的地方都能正常工作。 4. 接口隔离原则(Interface Segregation Principle, ISP):不应该强迫客户...

    面向对象5大原则.pdf

    面向对象设计的五个基本原则,通常称为SOLID,是五个英文单词的首字母缩写:单一职责原则(Single Responsibility Principle, SRP)、开闭原则(Open/Closed Principle, OCP)、里氏替换原则(Liskov Substitution ...

    java设计模式之面向对象的思想(think in OO(Object Oriented))

    在实际项目中,结合面向对象的设计原则(如单一职责原则、开闭原则、里氏替换原则、依赖倒置原则等),我们可以构建出高效、高质量的软件系统。学习并熟练掌握这些设计模式,对于提升Java程序员的专业素养至关重要。

    C#面向对象练习进阶

    此外,面向对象设计原则,如SOLID原则(单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则),也是学习进阶的重要部分。遵循这些原则可以使代码更易于维护、扩展和测试。 总之,“C#面向对象练习...

    面向对象思想学习(精华版)

    设计原则如单一职责原则(SRP)、开闭原则(OCP)、里氏替换原则(LSP)、依赖倒置原则(DIP)和接口隔离原则(ISP)等,可以帮助我们编写出更高质量的代码。同时,设计模式如工厂模式、单例模式、观察者模式等,是...

    信息系统面向对象方法2

    - **里氏替换原则**:子类对象应当能够替换掉它们的父类对象,而不影响程序的正确性。 - **依赖倒置原则**:高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象。 - **接口隔离原则**:客户端不应该被迫...

    面向对象思想的精辟论述.txt

    封装是面向对象编程的基础,它将数据和操作数据的方法绑定在一起,形成一个不可分割的整体——类(Class)。外部只能通过类提供的接口访问内部状态,这保护了数据的安全性和完整性。例如,在Java中,我们可以通过...

    面向对象的一些知识(一)

    3. 里氏替换原则(Liskov Substitution Principle, LSP):子类型必须能够替换它们的基类型,而不影响程序的正确性。 4. 接口隔离原则(Interface Segregation Principle, ISP):客户端不应该被迫依赖它不需要的...

    面向对象_在线培训学习资料

    SOLID 是五个基本原则的首字母缩写,包括单一职责原则(SRP)、开闭原则(OCP)、里氏替换原则(LSP)、接口隔离原则(ISP)和依赖倒置原则(DIP)。遵循这些原则,可以使代码更易于理解和维护。 本在线培训学习...

    Java面向对象程序设计教程.pdf

    学习Java面向对象编程不仅仅是掌握语法,更重要的是理解其设计理念和原则,如SOLID原则(单一职责、开闭、里氏替换、接口隔离、依赖倒置),以及设计模式的应用,如工厂模式、单例模式、观察者模式等。这些都将在...

    面向对象软件构造object-oriented software construction

    11. SOLID原则:SOLID是面向对象设计的五个基本原则,包括单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则,它们指导开发者编写更灵活、可维护的代码。 12. 面向服务架构(SOA):虽然不完全是...

    道法自然 面向对象实践指南(包括源代码)

    7. ** SOLID原则**:SOLID是面向对象设计的五个基本原则的首字母缩写,包括单一职责原则、开放封闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。书中可能会深入解析这些原则,并展示如何在实践中应用它们。 8....

Global site tag (gtag.js) - Google Analytics