设计原则学习之里氏替换原则

这篇具有很好参考价值的文章主要介绍了设计原则学习之里氏替换原则。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

以下内容均来自抖音号【it楠老师教java】的设计模式课程。

1、原理概述

子类对象(objectofsubtype/derivedclass)能够替换程序(program)中父类对象(objectofbase/parentclass)出现的任何地方,并且保证原来程序的逻辑行为(behavior)不变及正确性不被破坏。

2、简单的示例1

// 基类:鸟类
public class Bird {
public void fly () {
System . out . println ( "I can fly" );
} }
// 子类:企鹅类
public class Penguin extends Bird {
// 企鹅不能飞,所以覆盖了基类的 fly 方法,但这违反了里氏替换原则
public void fly () {
throw new UnsupportedOperationException ( "Penguins can't fly" );
}
}
为了遵循LSP,我们可以重新设计类结构,将能飞的行为抽象到一个接口中,让需要飞行能力的鸟类实现这个接口:
// 飞行行为接口
public interface Flyable {
void fly ();
}
// 基类:鸟类
public class Bird {
}
// 子类:能飞的鸟类
public class FlyingBird extends Bird implements Flyable {
@Override
public void fly () {
System . out . println ( "I can fly" );
}
}
// 子类:企鹅类,不实现 Flyable 接口
public class Penguin extends Bird {
}

这里就该明确那些方法是通用的,哪些方法是部分能用的。

比如通用的方法可以放到class bird里。

public void say(){ System.out.println("我属于鸟科")}

public void say(){ System.out.println("我又一双翅膀,尽管不能飞")}

不同的用的方法,可以放到接口里比如有的鸟很小 有的鸟很大

interface  BigBird{ double height()}

interface  SmallBird{ double height()}

上述可能不太准确,但是核心思想就是抽取公共的方法到类里,抽取特殊的方法到接口里。

再举个例 比如

class  door{

//核心方法 只要是门 不管你啥样的 你肯定又面积吧,有价格吧

int price();

int area();

}

但是有的门市防火门 有的是防盗门, 有的是....

interface FangHuo{ void canFangHuo()};

interface FangDao{ void canFangDao()};

3、示例2

我们再来看一个基于数据库操作的案例。假设我们正在开发一个支持多种数据库的程序,包括MySQL、PostgreSQL和SQLite。我们可以使用里氏替换原则来设计合适的类结构,确保代码的可维护性和扩展性。 首先,我们定义一个抽象的Database 基类,它包含一些通用的数据库操作方法, 如 connect() 、disconnect() 和 executeQuery() 。这些方法的具体实现将在 子类中完成。

public abstract class Database {
public abstract void connect ();
public abstract void disconnect ();
public abstract void executeQuery ( String query );
}

然后,为每种数据库类型创建一个子类,继承自 Database 基类。这些子类需要实现基类中定义的抽象方法,并可以添加特定于各自数据库的方法。

这里新手要思考下 为什么用abstract class 怎么不用class 怎么不用interface

不用class,因为我这里还没有具体到那类数据源,其实可以定义个jdbcDatabase ,定义属性driver,url,user ,password。就是更一部的抽取

不用interface 因为connect close 和query是所有数据源都有操作!!!

public class MySQLDatabase extends Database {
@Override
public void connect () {
// 实现 MySQL 的连接逻辑
}
@Override
public void disconnect () {
// 实现 MySQL 的断开连接逻辑
}
@Override
public void executeQuery ( String query ) {
// 实现 MySQL 的查询逻辑
}
// 其他针对 MySQL 的特定方法
}
public class PostgreSQLDatabase extends Database {
// 类似地,为 PostgreSQL 实现相应的方法
}
public class SQLiteDatabase extends Database {
// 类似地,为 SQLite 实现相应的方法
}

 

这样设计的好处是,我们可以在不同的数据库类型之间灵活切换,而不需要修改大量代码。只要这些子类遵循里氏替换原则,我们就可以放心地使用基类的引用来操作不同类型的数据库。例如: 

public class DatabaseClient {
private Database database ;
public DatabaseClient ( Database database ) {
this . database = database ;
}
public void performDatabaseOperations () {
database . connect ();
database . executeQuery ( "SELECT * FROM users" );
database . disconnect ();
}
}
public class Main {
public static void main ( String [] args ) {
// 使用 MySQL 数据库
DatabaseClient client1 = new DatabaseClient ( new MySQLDatabase ());
client1 . performDatabaseOperations ();
// 切换到 PostgreSQL 数据库
DatabaseClient client2 = new DatabaseClient ( new PostgreSQLDatabase ());
client2 . performDatabaseOperations ();
// 切换到 SQLite 数据库
DatabaseClient client3 = new DatabaseClient ( new SQLiteDatabase ());
client3 . performDatabaseOperations ();
}
}

好了,我们稍微总结一下。虽然从定义描述和代码实现上来看,多态和里式替换有点类似,但它们关注的角度是不一样的。多态是面向对象编程的一大特性,也是面向对象编程语言的一种语法。它是一种代码实现的思路。而里式替换是一种设计原则,是用来指导继承关系中子类该如何设计的,子类的设计要保证在替换父类的时候,不改变原有程序的逻辑以及不破坏原有程序的正确性。 

2 、哪些代码明显违背了 LSP
1.子类覆盖或修改了基类的方法

当子类覆盖或修改基类的方法时,可能导致子类无法替换基类的实例而不引起问题。这违反了LSP,会导致代码变得脆弱和不易维护。

public class Bird {
public void fly () {
System . out . println ( "I can fly" );
}
}
public class Penguin extends Bird {
@Override
public void fly () {
throw new UnsupportedOperationException ( "Penguins can't fly" );
}
}
在这个例子中, Penguin 类覆盖了 Bird 类的 fly() 方法,抛出了一个异常。这违反了LSP,因为现在 Penguin 实例无法替换 Bird 实例而不引发问题。

 

2、子类违反了基类的约束条件

当子类违反了基类中定义的约束条件(如输入、输出或异常等),也会违反LSP

public class Stack {
private int top ;
private int [] elements ;
public Stack ( int size ) {
elements = new int [ size ];
top = - 1 ;
}
public void push ( int value ) {
if ( top >= elements . length - 1 ) {
throw new IllegalStateException ( "Stack is full" );
} elements [ ++ top ] = value ;
}
public int pop () {
if ( top < 0 ) {
throw new IllegalStateException ( "Stack is empty" );
}
return elements [ top -- ];
}
}
// 正数的栈
public class NonNegativeStack extends Stack {
public NonNegativeStack ( int size ) {
super ( size );
}
@Override
public void push ( int value ) {
if ( value < 0 ) {
throw new IllegalArgumentException ( "Only non-negative values are allowed" );
}
super . push ( value );
}
}
// 正确的写法
public class NonNegativeStack extends Stack {
public NonNegativeStack ( int size ) {
super ( size );
}
public void pushNonNegative ( int value ) {
if ( value < 0 ) {
throw new IllegalArgumentException ( "Only non-negative values are allowed" );
}
super . push ( value );
}
}

这里感觉给的资料有问题。。。等我看完视频在说 文章来源地址https://www.toymoban.com/news/detail-624353.html

到了这里,关于设计原则学习之里氏替换原则的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 设计原则学习之里氏替换原则

    以下内容均来自抖音号【it楠老师教java】的设计模式课程。 1 、原理概述 子类对象(objectofsubtype/derivedclass)能够替换程序(program)中父类对象(objectofbase/parentclass)出现的任何地方,并且保证原来程序的逻辑行为(behavior)不变及正确性不被破坏。 2、简单的示例1 // 基类:

    2024年02月14日
    浏览(27)
  • 设计模式:里氏代换原则(Liskov Substitution Principle,LSP)介绍

    里氏代换原则(Liskov Substitution Principle,LSP)是面向对象设计原则的一部分,它强调 子类对象应该能够替换其父类对象而不影响程序的正确性 。换句话说,子类对象应该可以在不改变程序正确性的前提下替换掉父类对象。 该原则的实现原理可以通过以下几点来说明: 子类必

    2024年04月29日
    浏览(30)
  • 深入理解设计原则之里氏替换原则(LSP)【软件架构设计】

    C++高性能优化编程系列 深入理解软件架构设计系列 深入理解设计模式系列 高级C++并发线程编程 里氏替换原则(Liskov Substitution Principle, LSP)于1986年有Barbara Liskov提出,他当时是这样描述这条原则的: 如果S是T的子类型,那么T的对象可以被S的对象所替换,并不影响代码的运行

    2024年02月07日
    浏览(72)
  • 基于面向对象基础设计——里氏替换原则

    在Java中,支持抽象和多态的关键机制之一是继承。正是使用了继承,我们才可以创建实现父类中抽象方法的子类。那么,是什么规则在支配着这种特殊的继承用法呢?最佳的继承层次的特征又是什么呢?在什么情况下会使我们创建的类层次结构掉进不符合开闭原则的陷阱中呢

    2024年02月14日
    浏览(32)
  • 里氏替换原则

    里氏替换原则 OOP(Object Oriented Programming) 面向对象编程 OO中的继承性的思考 1.继承包含这样一层含义,父类中凡是已经写好的方法,实际上就是设定规范。虽然不强制要求所有子类必须遵守规范(不重写方法),但是如果子类对这些方法,任意修改就会对继承体系造成破坏。 2.继

    2024年02月11日
    浏览(29)
  • 里氏替换原则究竟如何理解?

    介 绍 里 氏 替 换 原 则 的 文 章 非 常 多 , 但 可 能 大 家 看 完 之 后 , 心 中 仍 然 留 有 疑 问 , 如 何 去 落 地 实 现 , 如 何 判 断 是 否 影 响 程 序 功 能 。 本 文 将 带 领 大 家 深 入 理 解 里 氏 替 换 , 一 起 领 略 下 它 的 真 正 面 目 。 但 在 此 之 前 , 有 必

    2024年02月08日
    浏览(44)
  • (学习打卡2)重学Java设计模式之六大设计原则

       前言:听说有本很牛的关于Java设计模式的书——重学Java设计模式,然后买了(*^▽^*) 开始跟着小傅哥学Java设计模式吧,本文主要记录笔者的学习笔记和心得。 打卡!打卡! 单一职责原则、开闭原则、里氏替换原则、迪米特法则、接口隔离原则、依赖倒置原则。 (引读:

    2024年02月03日
    浏览(33)
  • Spring学习笔记(二)Spring的控制反转(设计原则)与依赖注入(设计模式)

    是一种设计原则,降低程序代码之间的耦合度 对象由Ioc容器统一管理,当程序需要使用对象时直接从IoC容器中获取。这样对象的控制权就从应用程序转移到了IoC容器 依赖注入是一种消除类之间依赖关系的设计模式。例如,A类要依赖B类,A类不再直接创建B类,而是把这种依赖

    2024年02月19日
    浏览(28)
  • 【设计模式】设计原则-开闭原则

    定义 作用 1、方便测试;测试时只需要对扩展的代码进行测试。 2、提高代码的可复用性;粒度越小,被复用的可能性就越大。 3、提高软件的稳定性和延续性,易于扩展和维护。 实现方式 通过“抽象约束、封装变化”来实现开闭原则。通过接口或者抽象类为软件实体定义一

    2024年02月15日
    浏览(26)
  • 前端设计模式和设计原则之设计原则

    1 开闭原则 该原则指出软件实体(类、模块、函数等)应该 对扩展开放,对修改关闭 。也就是说,在添加新功能时,应该通过扩展现有代码来实现,而不是直接修改已有的代码。这样可以确保现有代码的稳定性,并且减少对其他部分的影响。 在上述例子中,有一个原始功能

    2024年02月07日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包