精华内容
下载资源
问答
  • 里氏替换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。里氏替换原则是实现...
  • 里氏替换原则

    2021-04-30 14:39:22
    里氏替换原则的定义: 继承必须确保超类所拥有的性质在子类中仍然成立。它反映了积累与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。 里氏替换原则的作用: (1)里氏替换原则是实现开...

    里氏替换原则的定义:

    	继承必须确保超类所拥有的性质在子类中仍然成立。它反映了积累与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。
    

    里氏替换原则的作用:

    	(1)里氏替换原则是实现开闭原则的重要方式之一。
    	(2)它克服了继承中重写父类造成的可复用性变差的缺点。
    	(3)他是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。
    

    里氏替换原则的实现方法:

    	里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新
    增功能外,尽量不要重写父类的方法。如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会
    比较差,特别是运用多态比较频繁时,程序运行出错的概率会非常大。如果程序违背了里氏替换原则,则继承类的对象在基类出现的地方
    会出现运行错误。这时其修正方法是:取消原来的继承关系,重新设计它们之间的关系。
    

    下面以“几维鸟不是鸟”为例说明里氏替换原则:

    UML类图:
    UML类图

    package lsp;
    
    //动物
    public class Animal {
        protected double runSpeed;
    //    奔跑速度
        public void setRunSpeed(double runSpeed) {
            this.runSpeed = runSpeed;
        }
    //    奔跑时间
        public double getRunTime(double distance){
            return (distance / runSpeed);
        }
    }
    
    
    package lsp;
    
    //鸟
    public class Bird extends Animal{
        protected double flySpeed;
    
    //    飞行速度
        public void setSpeed(double speed) {
            flySpeed = speed;
        }
    
    //    飞行时间
        public double getFlyTime(double distance){
            return (distance / flySpeed);
        }
    }
    
    
    package lsp;
    
    //燕子
    public class Swallow extends Bird {
        protected double flySpeed;
    
    //    飞行速度
        @Override
        public void setSpeed(double flySpeed) {
            this.flySpeed = flySpeed;
        }
    
    //    飞行时间
        @Override
        public double getFlyTime(double distance) {
            return (distance / flySpeed);
        }
    
        public Swallow() {
        }
    }
    
    
    package lsp;
    
    //几维鸟
    public class BrownKiwi extends Animal{
        protected double runSpeed;
    
    //    奔跑速度
        @Override
        public void setRunSpeed(double runSpeed) {
            this.runSpeed = runSpeed;
        }
    
    //    奔跑时间
        @Override
        public double getRunTime(double distance) {
            return (distance / runSpeed);
        }
    }
    
    
    package lsp;
    
    //访问类
    public class LSPTest {
        public static void main(String[] args) {
            Bird bird = new Swallow();
            Animal animal = new BrownKiwi();
    
            System.out.println("如果速度为120");
    
            bird.setSpeed(120);
            animal.setRunSpeed(120);
    
            System.out.println("飞行或奔跑300千米");
    
            double flyTime = bird.getFlyTime(300);
            double runTime = animal.getRunTime(300);
    
            System.out.println("燕子将飞行" + flyTime + "小时");
            System.out.println("几维鸟将奔跑" + runTime + "小时");
        }
    }
    
    
    展开全文
  • 主要介绍了PHP面向对象五大原则之里氏替换原则(LSP),较为详细的分析了里氏替换原则(LSP)的概念、原理并结合实例形式分析了php里氏替换原则(LSP)的简单使用方法,需要的朋友可以参考下
  • 里氏替换原则(Liskov Substitution Principle,简称LSP):子类可以替换父类继承有一些优点:1. 提高代码的重用性,子类拥有父类的方法和属性;2. 提高代码的可扩展性,子类可形似于父类,但异于父类,保留自我的特性;...

    里氏替换原则(Liskov Substitution Principle,简称LSP):子类可以替换父类

    继承有一些优点:

    1. 提高代码的重用性,子类拥有父类的方法和属性;

    2. 提高代码的可扩展性,子类可形似于父类,但异于父类,保留自我的特性;

    缺点:侵入性、不够灵活、高耦合

    1. 继承是侵入性的,只要继承就必须拥有父类的所有方法和属性,在一定程度上约束了子类,降低了代码的灵活性;

    2. 增加了耦合,当父类的常量、变量或者方法被修改了,需要考虑子类的修改,所以一旦父类有了变动,很可能会造成

    非常糟糕的结果,要重构大量的代码。

    任何基类可以出现的地方,子类一定可以出现。里氏替换原则是继承复用的基石,只有当衍生类可以替换基类,软件单位的功能不受到影响时,即基类随便怎么改动子类都不受此影响,那么基类才能真正被复用

    因为继承带来的侵入性,增加了耦合性,也降低了代码灵活性,父类修改代码,子类也会受到影响,此时就需要里氏替换原则。

    子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。

    子类中可以增加自己特有的方法。

    当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。

    当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

    a.子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    public class A {

    public void fun(int a,int b){

    System.out.println(a+"+"+b+"="+(a+b));

    }

    }

    public class B extends A{

    @Override

    public void fun(int a,int b){

    System.out.println(a+"-"+b+"="+(a-b));

    }

    }

    public class demo {

    public static void main(String[] args){

    System.out.println("父类的运行结果");

    A a=new A();

    a.fun(1,2);

    //父类存在的地方,可以用子类替代

    //子类B替代父类A

    System.out.println("子类替代父类后的运行结果");

    B b=new B();

    b.fun(1,2);

    }

    }

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    运行结果:

    父类的运行结果

    1+2=3

    子类替代父类后的运行结果

    1-2=-1

    b.子类中可以增加自己特有的方法。

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    public class A {

    public void fun(int a,int b){

    System.out.println(a+"+"+b+"="+(a+b));

    }

    }

    public class B extends A{

    public void newFun(){

    System.out.println("这是子类的新方法...");

    }

    }

    public class demo {

    public static void main(String[] args){

    System.out.print("父类的运行结果:");

    A a=new A();

    a.fun(1,2);

    //父类存在的地方,可以用子类替代

    //子类B替代父类A

    System.out.print("子类替代父类后的运行结果:");

    B b=new B();

    b.fun(1,2);

    //子类B的新方法

    b.newFun();

    }

    }

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    运行结果:

    父类的运行结果:1+2=3

    子类替代父类后的运行结果:1+2=3

    这是子类的新方法...

    c.当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    public class LSP {

    class A {

    public void fun(HashMap map){

    System.out.println("父类被执行...");

    }

    }

    class B extends A{

    public void fun(Map map){

    System.out.println("子类被执行...");

    }

    }

    public static void main(String[] args){

    System.out.print("父类的运行结果:");

    LSP lsp =new LSP();

    LSP.A a= lsp.new A();

    HashMap map=new HashMap();

    a.fun(map);

    //父类存在的地方,可以用子类替代

    //子类B替代父类A

    System.out.print("子类替代父类后的运行结果:");

    LSP.B b=lsp.new B();

    b.fun(map);

    }

    }

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    运行结果:

    父类的运行结果:父类被执行...

    子类替代父类后的运行结果:父类被执行...

    符合条件

    我们应当注意,子类并非重写了父类的方法,而是重载了父类的方法。因为子类和父类的方法的输入参数是不同的。

    子类方法的参数Map比父类方法的参数HashMap的范围要大,所以当参数输入为HashMap类型时,只会执行父类的方法,不会执行父类的重载方法。这符合里氏替换原则。

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    //将子类方法的参数范围缩小会怎样?

    import java.util.Map;

    public class A {

    public void fun(Map map){

    System.out.println("父类被执行...");

    }

    }

    import java.util.HashMap;

    public class B extends A{

    public void fun(HashMap map){

    System.out.println("子类被执行...");

    }

    }

    import java.util.HashMap;

    public class demo {

    static void main(String[] args){

    System.out.print("父类的运行结果:");

    A a=new A();

    HashMap map=new HashMap();

    a.fun(map);

    //父类存在的地方,都可以用子类替代

    //子类B替代父类A

    System.out.print("子类替代父类后的运行结果:");

    B b=new B();

    b.fun(map);

    }

    }

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    运行结果:

    父类的运行结果:父类被执行...

    子类替代父类后的运行结果:子类被执行...

    在父类方法没有被重写的情况下,子方法被执行了,这样就引起了程序逻辑的混乱。

    所以子类中方法的前置条件必须与父类中被覆写的方法的前置条件相同或者更宽松。不符合里式替换

    d.当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    public class LSP1 {

    abstract class A {

    public abstract Map fun();

    }

    class B extends A{

    @Override

    public HashMap fun(){

    HashMap b=new HashMap();

    b.put("b","子类被执行...");

    return b;

    }

    }

    public static void main(String[] args){

    LSP1 lsp =new LSP1();

    LSP1.A a=lsp.new B();

    System.out.println(a.fun());

    }

    }

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    运行结果:

    {b=子类被执行...}

    若在继承时,子类的方法返回值类型范围比父类的方法返回值类型范围大,在子类重写该方法时编译器会报错。

    01c9132a5efd2db59ad61120aba0eac9.png

    看上去很不可思议,因为我们会发现在自己编程中常常会违反里氏替换原则,程序照样跑的好好的。所以大家都会产生这样的疑问,假如我非要不遵循里氏替换原则会有什么后果?

    后果就是:你写的代码出问题的几率将会大大增加。

    展开全文
  • 里氏替换原则,OCP作为OO的高层原则,主张使用“抽象(Abstraction)”和“多态(Polymorphism)”将设计中的静态结构改为动态结构,维持设计的封闭性。“抽象”是语言提供的功能。“多态”由继承语义实现。里氏替换原则...

    里氏替换原则,OCP作为OO的高层原则,主张使用“抽象(Abstraction)”和“多态(Polymorphism)”将设计中的静态结构改为动态结构,维持设计的封闭性。“抽象”是语言提供的功能。“多态”由继承语义实现。

    里氏替换原则包含以下4层含义:

    子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。

    子类中可以增加自己特有的方法。

    当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。

    当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

    现在我们可以对以上四层含义进行讲解。

    子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法

    在我们做系统设计时,经常会设计接口或抽象类,然后由子类来实现抽象方法,这里使用的其实就是里氏替换原则。子类可以实现父类的抽象方法很好理解,事实上,子类也必须完全实现父类的抽象方法,哪怕写一个空方法,否则会编译报错。

    里氏替换原则的关键点在于不能覆盖父类的非抽象方法。父类中凡是已经实现好的方法,实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些规范,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。而里氏替换原则就是表达了这一层含义。

    在面向对象的设计思想中,继承这一特性为系统的设计带来了极大的便利性,但是由之而来的也潜在着一些风险。下面举例来说明继承的风险,我们需要完成一个两数相减的功能,由类A来负责。

    class A{

    public int func1(int a, int b){

    return a-b;

    }

    }

    public class Client{

    public static void main(String[] args){

    A a = new A();

    System.out.println("100-50="+a.func1(100, 50));

    System.out.println("100-80="+a.func1(100, 80));

    }

    }

    运行结果:

    100-50=50

    100-80=20

    后来,我们需要增加一个新的功能:完成两数相加,然后再与100求和,由类B来负责。即类B需要完成两个功能:

    两数相减。

    两数相加,然后再加100。

    由于类A已经实现了第一个功能,所以类B继承类A后,只需要再完成第二个功能就可以了,代码如下:

    class B extends A{

    public int func1(int a, int b){

    return a+b;

    }

    public int func2(int a, int b){

    return func1(a,b)+100;

    }

    }

    public class Client{

    public static void main(String[] args){

    B b = new B();

    System.out.println("100-50="+b.func1(100, 50));

    System.out.println("100-80="+b.func1(100, 80));

    System.out.println("100+20+100="+b.func2(100, 20));

    }

    }

    类B完成后,运行结果:

    100-50=150

    100-80=180

    100+20+100=220

    我们发现原本运行正常的相减功能发生了错误。原因就是类B在给方法起名时无意中重写了父类的方法,造成所有运行相减功能的代码全部调用了类B重写后的方法,造成原本运行正常的功能出现了错误。在本例中,引用基类A完成的功能,换成子类B之后,发生了异常。在实际编程中,我们常常会通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的几率非常大。如果非要重写父类的方法,比较通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合,组合等关系代替。

    展开全文
  • 基本介绍 1、里氏替换原则在1988年,由麻省理工学院的一位姓里的女士提出的。 2、如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得T1定义的所有程序p在所有的对象o1都代换成o2是,程序p的行为没有发生变化...

    oo中的继承性的思考和说明

    1、继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些七月,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。

    2、继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障。

    3、问题提出:在编程中,如何正确的使用继承? 答:尽可能遵循里氏替换原则。

    基本介绍

    1、里氏替换原则在1988年,由麻省理工学院的一位姓里的女士提出的。

    2、如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得T1定义的所有程序p在所有的对象o1都代换成o2是,程序p的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有,所有应用类型的地方必须能透明地使用其子类的对象。

    3、在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法。

    4、里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖来解决问题。

    应用案例

    没有使用里氏替换原则

    public class Liskov {

    public static void main(String[] args) {

    A a = new A();

    System.out.println("1-8=" + a.func1(1, 8));

    System.out.println("--------------------");

    B b = new B();

    System.out.println("11-3=" + b.func1(11, 3));

    System.out.println("1-8=" + b.func1(1, 8));

    System.out.println("11+3+9=" + b.func3(11, 3));

    }

    }

    class A {

    // 两个数的差

    public int func1(int num1, int num2) {

    return num1 - num2;

    }

    // 两个数的积

    public int funcBase(int num1, int num2) {

    return num1 * num2;

    }

    }

    //B类继承了A

    //增加了一个新功能,完成两个数相加,然后和9求和

    class B extends A {

    // 这里,重写了A的方法,可能是无意识

    public int func1(int a, int b) {

    return a + b;

    }

    public int func3(int a, int b) {

    return func1(a, b) + 9;

    }

    }

    使用里氏替换原则

    public class Liskov {

    public static void main(String[] args) {

    A a = new A();

    System.out.println("1-8=" + a.func1(1, 8));

    System.out.println("--------------------");

    B b = new B();

    System.out.println("11-3=" + b.func1(11, 3));

    System.out.println("1-8=" + b.func1(1, 8));

    System.out.println("11+3+9=" + b.func2(11, 3));

    // 使用组合来使用A的相关方法

    System.out.println("--------------------");

    System.out.println("11-3=" + b.func3(11, 3));

    System.out.println("1-8=" + b.func3(1, 8));

    System.out.println("11+3+9=" + b.func2(11, 3));

    }

    }

    class Base {

    // 把更加基础的方法和成员写到Base类

    // 两个数的积

    public int funcBase(int num1, int num2) {

    return num1*num2;

    }

    }

    class A extends Base {

    // 两个数的差

    public int func1(int num1, int num2) {

    return num1 - num2;

    }

    }

    //B类继承了A

    //增加了一个新功能,完成两个数相加,然后和9求和

    class B extends Base {

    // 如果B需要使用A类得方法,使用组合关系

    private A a = new A();

    // 这里,重写了A的方法,可能是无意识

    public int func1(int a, int b) {

    return a + b;

    }

    public int func2(int a, int b) {

    return func1(a, b) + 9;

    }

    // 使用A得方法

    public int func3(int a, int b) {

    return this.a.func1(a, b);

    }

    }

    上面这段代码中,使用了里氏替换原则,将更基础的方法funcBase()提取出来,然后将A类和B类继承这个更基础的Base类,采用依赖、聚合或耦合的方式来减少父类和子类的耦合

    展开全文
  • 本文主要讲解设计模式的里氏替换原则。肯定有不少人跟我刚看到这项原则的时候一样,对这个原则的名字充满疑惑。其实原因就是这项原则最早是在 1988 年,由麻省理工学院的一位姓里的女士(Barbara Liskov)提出来的。...
  • 里氏替换原则(Liskov Substitution Principel)是解决继承带来的问题。 继承的优点: 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性; 提高代码的重用性; 子类可以形似父类,但又异于父类; ...
  • 里氏替换原则的理解: 对于里氏替换原则的四个重要特性: 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。 子类中可以增加自己特有的方法。 当子类的方法重载父类的方法时,方法的前置条件(即方法的...
  • 1、单一职责原则 2、开闭原则 (1)原生 package Tread.test1; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent....
  • 一个违反里氏替换原则的例子、一个遵守里氏替换原则的例子。 // 绘制图形 void drawShape(Shape shape) { if (shape.type == Shape.Circle) { drawCircle((Circle) shape); } else if (shape.type == Shape....
  • 下面介绍一个经典的业务场景,用正方形、矩形和四边形的关系来说明里氏替换原则。我们都知道正方形是一个特殊的长方形,首先创建一个长方形父类 Rectangle,代码如下。public class Square extends Rectangle {...
  • 设计模式六大原则:里氏替换原则
  • 六大设计原则-里氏替换原则

    千次阅读 2020-03-22 23:51:18
    里氏替换原则 什么是里氏替换原则里氏替换原则的定义:任何使用基类的地方,都可以透明的使用其子类。从定义中可以看出,它和继承差不多,我们也可以这样理解,里氏替换原则=继承+透明,也就是继承的加强版,取...
  • 里氏替换原则(Liskov Substitution principle)是对子类型的特别定义的. 为什么叫里式替换原则呢?因为这项原则最早是在1988年,由麻省理工学院的一位姓里的女士(Barbara Liskov)提出来的。 里氏替换原则主要阐述...
  • 设计原则之里氏替换原则–子类可以替换父类吗 里氏替换原则(Liskov Substitution Principle)LSP,这个原则是说子类应该可以替换父类进行使用。 这个原则的英文描述是functions that use pointers of references to...
  • 一、里氏替换原则代码示例 ( 类的层级 | 反面示例 )、 二、里氏替换原则代码示例 ( 类的层级 | 正面示例 )、 三、里氏替换原则代码示例 ( 方法入参 | 正面示例 )、 四、里氏替换原则代码示例 ( 方法入参 | 反面示例 ...
  • 里氏替换原则 依赖倒置原则 接口分离原则 迪米特原则 1.单一职能原则 单一职责原则 其实就是开发人员经常说的”高内聚,低耦合” 也就是说,每个类或每个方法都只负责一件事情。 在设计模式中,所有的设计...
  • 设计模式六大原则(二)里氏替换原则里氏替换原则定义里氏替换原则针对的问题里氏替换原则的解决方案里氏替换原则的认识里氏替换原则案例 里氏替换原则定义 第一种定义:If for each object O1 of type S there is...
  • 最近在内部群里有一些讨论,主要是各种重构重构和重构遇到的困难,和里氏替换原则真的是一个可执行的原则么,特别是iOS日常业务开发上。我们的结论是,麻烦!但是可以解决问题!里氏替换原则是我们六大设计原则中...
  • 设计模式六大原则(2):里氏替换原则肯定有不少人跟我刚看到这项原则的时候一样,对这个原则的名字充满疑惑。其实原因就是这项原则最早是在1988年,由麻省理工学院的一位姓里的女士(Barbara Liskov)提出来的。定义1:...
  • } } } 如果使用了继承,子类重写了父类方法(抽象方法除外),就会违背里氏替换原则,会让程序增加出错的可能 总结一下里氏替换原则的特点,就是子类可以拓展父类的功能,但是不能改变父类原有的功能。子类可以重写...
  • golang 架构设计原则 里氏替换原则缘起最近复习设计模式拜读谭勇德的<>该书以java语言演绎了常见设计模式本系列笔记拟采用golang练习之里氏替换原则里氏替换原则(Liskov Substitution Principle, LSP):如果对...
  • 里氏替换原则是面向对象设计的基本原则之一,主张使用“抽象(Abstraction)”和“多态(Polymorphism)”将设计中的静态结构改为动态结构,维持设计的封闭性。“抽象”是语言提供的功能,“多态”由继承语义实现。里氏...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 32,669
精华内容 13,067
关键字:

里式替换原则