C# 实例解释面向对象编程中的里氏替换原则

在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解、灵活和可维护。这些原则是由美国软件工程师和讲师罗伯特·C·马丁(Robert Cecil Martin)提出的许多原则的子集,在他2000年的论文《设计原则与设计模式》中首次提出。

SOLID 原则包含:

  • S:单一功能原则(single-responsibility principle)

  • O:开闭原则(open-closed principle)

  • L:里氏替换原则(Liskov substitution principle)

  • I:接口隔离原则(Interface segregation principle)

  • D:依赖反转原则(Dependency inversion principle)

本文我们来介绍里氏替换原则

里氏替换原则

在面向对象的程序设计中,里氏替换原则(Liskov Substitution principle)是对子类型的特别定义。它由芭芭拉·利斯科夫(Barbara Liskov)在1987年的一次会议上,在名为“数据的抽象与层次”的演说中首次提出。

里氏替换原则的内容可以描述为:派生类(子类)对象可以在程序中代替其基类(超类)对象。

也就是说,程序中的对象不管出现在什么地方,都应该可以使用其派生类(子类)的对象进行替换,而不影响程序运行的正确性。

C# 示例

我们看这样一个示例,假设一个企业有三种员工,一种是拿铁饭碗的永久雇员,一种是合同工,一种是临时工。我们设计几个类来表示这三种员工。

§糟糕的示范

先定义一个 Employee 基类。

public abstract class Employee
{public string Name { get; set; }/// <summary>/// 计算奖金/// </summary>/// <returns></returns>public abstract decimal CalculateBonus();
}

再定义该基类的三个子类:

/// <summary>
/// 永久雇员
/// </summary>
public class PermanentEmployee : Employee
{public override decimal CalculateBonus(){return 80000;}
}/// <summary>
/// 合同工
/// </summary>
public class ContractEmployee : Employee
{public override decimal CalculateBonus(){return 2000;}
}/// <summary>
/// 临时工(临时工没有奖金)
/// </summary>
public class TemporaryEmployee : Employee
{public override decimal CalculateBonus(){throw new NotImplementedException(); //违反里氏替换原则}
}

接下来在 Main 方法中调用它们。

先定义一个类型为基类 Employee 的变量 e,再分别使用其子类 PermanentEmployeeContractEmployee 和 TemporaryEmployee 创建对象赋值给基类变量 e,然后调用 e 的 CalculateBonus() 方法。

static void Main(string[] args)
{Employee e;e = new PermanentEmployee() { Name = "张三" };Console.WriteLine($"{e.Name} 的年终奖是 {e.CalculateBonus()} 元");e = new ContractEmployee() { Name = "李四" };Console.WriteLine($"{e.Name} 的年终奖是 {e.CalculateBonus()} 元");e = new TemporaryEmployee() { Name = "王五" };Console.WriteLine($"{e.Name} 的年终奖是 {e.CalculateBonus()} 元");
}

运行一下可以观察到(显而易见的),当使用 PermanentEmployee 和 ContractEmployee 类创建的对象替换基类型 Employee 的变量 e 时,调用 CalculateBonus() 方法可以正常运行,但是使用 TemporaryEmployee 类创建的对象替换变量 e 时,调用 CalculateBonus() 方法抛出了异常,导致程序无法正常运行。这就明显违反了里氏替换原则

那么,应该如何改进一下呢?

§正确的示范

我们看到,每种员工都有基本信息 Name 属性,但是由于临时工 TemporaryEmployee 没有奖金,所以不需要计算奖金。因此我们应该把计算奖金的方法 CalculateBonus 单独抽象出去,而不是让它们都继承于同一个基类,并将 TemporaryEmployee 子类中的 CalculateBonus 方法抛出一个异常。

改进后的代码:

interface IEmployee
{/// <summary>/// 计算年终奖/// </summary>/// <returns></returns>public decimal CalculateBonus();
}public abstract class Employee
{public string Name { get; set; }
}/// <summary>
/// 永久雇员
/// </summary>
public class PermanentEmployee : Employee, IEmployee
{public decimal CalculateBonus(){return 80000;}
}/// <summary>
/// 合同工
/// </summary>
public class ContractEmployee : Employee, IEmployee
{public decimal CalculateBonus(){return 2000;}
}/// <summary>
/// 临时工
/// </summary>
public class TemporaryEmployee : Employee
{
}

在 Main 方法中,将调用它们的测试代码改为:

static void Main(string[] args)
{Employee e;IEmployee ie;var p = new PermanentEmployee() { Name = "张三" };e = p;ie = p;Console.WriteLine($"{e.Name} 的年终奖是 {ie.CalculateBonus()} 元");var c = new ContractEmployee() { Name = "李四" };e = c;ie = c;Console.WriteLine($"{e.Name} 的年终奖是 {ie.CalculateBonus()} 元");e = new TemporaryEmployee() { Name = "王五" };Console.WriteLine($"{e.Name} 是临时工,无年终奖。");
}

程序运行正常。

这样,这些子类的设计便遵循了里氏替换原则

总结

本文我介绍了 SOLID 原则中的里氏替换原则(Liskov substitution principle),并通过 C# 代码示例简明地诠释了它的含意和实现,希望对您有所帮助。

作者 :技术译民
出品 :技术译站(https://ITTranslator.cn/)

参考文档:

  • https://en.wikipedia.org/wiki/SOLID

  • https://www.c-sharpcorner.com/blogs/liskov-substitution-principle-in-c-sharp

3a6843597a42ee473d7a934085c22a8b.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/291042.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android之error: void value not ignored as it ought to be(In function ‘callMethod2‘)解决办法

1、问题 chenyu@chenyu:~/Android_dev/Test/app/jni$ ndk-build [armeabi] Compile thumb : FirstJni <= JniClient.c /home/chenyu/Android_dev/Test/app/jni/JniClient.c: In function callMethod2: /home/chenyu/Android_dev/Test/app/jni/JniClient.c:74:18: error: vo…

JavaScript面向对象-静态方法-私有方法-公有方法-特权方法,学习

var baseClass function(){function show(){//私有方法alert("访问私有方法");}function showName(){alert(this.name);}this.showw function (){//特权方法showName();} } //静态方法 baseClass.showStatic function(){alert("访问静态方法"); }//公有方…

linux docker nginx,CentOS7上Docker简单安装及nginx部署

安装如果原来安装过docker&#xff0c;先把原来的删掉&#xff0c;再安装(如果是首次安装docker忽略第一步&#xff0c;直接在第二步看起)1.1先查看下已经安装了那些dockeryum list installed | grep docker查看结果docker.x86_64 2:1.13.1-94.gitb2f74b2.el7.centosdocker-cli…

linux下的常用命令

一、Linux、CentOS下重命名文件和文件夹 mv&#xff1a;move 用移动文件命令就可以了&#xff0c;因为linux系统没有专门的重命名命令。 基本格式&#xff1a; 移动文件&#xff1a;mv 文件名 移动目的地文件名 重命名文件&#xff1a;mv 文件名 修改后的文件名 示例&#xff1…

Android之JNI ERROR (app bug): accessed stale global reference 0xb39533f2 (index 19708 in a table of s

1、问题 2、原因 我在jni里面是这样写的 (*env)->CallVoidMethod(env, obj, method3, "chenyu"); 3、解决办法 把这个 (*env)->CallVoidMethod(env, obj, method3, "chenyu"); 改为这个 (*env)->CallVoidMethod(env, obj, method3, (*env)-&g…

Nim教程【十二】

排除指定符号 一般情况下使用import语句&#xff0c;会把一个模块内的符号都导入进来 如果你像排除特定的符号&#xff08;不想让某些符号被导入进来&#xff09; 可以使用except子句 就像下面这样 import mymodule except y 这行代码排除了mymodule模块中的y符号&#xff0c;其…

工业互联网的最后一公里

最后一公里&#xff0c;出自中国共产党十八大以来的新名词之一&#xff0c;指政策始终“走在路上”&#xff0c;服务始终“停在嘴上”&#xff0c;实惠没有真正“落在身上”的“末梢堵塞”问题。要让人民群众真正得实惠&#xff0c;就要切实解决好“最后一公里”问题。1、移动互…

Android 编程下设置 Activity 切换动画

为 Activity 设置切换动画 我们知道&#xff0c;我们可以在 AndroidManifest.xml 文件中&#xff0c;通过 android:theme 属性设置 Activity 的主题。主题中定义了关于 Activity 外观的很多特性。同时&#xff0c;主题中还可以定义 Activity 的切换动画。这是应用 Activity 切换…

Android之jni调用java函数总结

1、先看之前jni的如何实现动态注册 先看我之间的例子 http://blog.csdn.net/u011068702/article/details/71375920 Android之JNI动态注册native方法和JNI数据简单使用 因为这里演示的jni调用java函数是基于这个例子改的,然后还有如何在jni里面加上日志可以看这篇博客 http…

linux nfs 无法写入,无法写入挂载点(nfs-server),获得“权限被拒绝”

root_squash — Prevents root users connected remotely from having rootprivileges and assigns them the user ID for the user nfsnobody. Thiseffectively “squashes” the power of the remote root user to the lowestlocal user,preventing unauthorized alteration …

介绍这个库:C# Blazor中显示Markdown文件

1 讲目的 前几天上线了一个在线Icon转换工具[1]&#xff0c;为了让大家使用放心&#xff0c;改了点代码&#xff0c;在转换下载Icon图标后立即删除临时文件&#xff0c;并在工具下面贴上了工具的开发步骤和代码&#xff0c;大家看这样改是否合适&#xff0c;见Issue 1[2]。这篇…

Linux 信号量 生产者消费者小例题

菜鸟偶遇信号量&#xff0c;擦出火花&#xff08;只有不熟才会有火花&#xff09;。于是上网搜资料和看《Unix环境高级编程》实现了几个小例题&#xff0c;高手请勿喷&#xff01;这几位写得非常好啊&#xff1a; 题目来源&#xff1a; http://www.it165.net/os/html/201312/70…

Redhat 5.1 install PHP 5.3.6

Redhat 5.1 install PHP 5.3.6现在各种版本的坑。。导致没有低版本的php 可能是使用的问题导致。系统版本没有提升导致的没有包可用....yum install gcc libxml2-devel bzip2-devel zlib-devel \ curl-devel libmcrypt-devel libjpeg-devel \ libpng-devel gd-devel mysql-deve…

C/C++语言之通过定义指针函数方式来实现在一个cpp文件里面获取另外一个cpp文件函数的返回值

1、定义函数指针 typedef int (* fun) (); static fun f; 2、代码实现 3、结果 4、总结 我们可以这样使用 在a.h文件里面里面定义函数指针,并且有个传递函数指针的方法 typedef std::string (*fun)();void f2(fun f 1); 然后在a.cpp文件里面实现f2方法 static fun f;…

讀後感

看別人的故事&#xff0c;寫自己的故事&#xff0c;寫自己的感受。 在學習的過程中&#xff0c;其實會遇到很多的人、事、物&#xff0c;而處在我們這個階段的青年&#xff0c;往往是無知的。有些人或許還看不到自己前方的路是怎麼樣&#xff0c;在迷茫之中探索未來的些許星光&…

linux系统做的小游戏,2007最新 100个Linux系统上的小游戏汇集

希望了解Linux小游戏的朋友可以阅读下。给大家介绍一下&#xff0c;Linux下的小游戏&#xff0c;一共100个&#xff0c;有游戏的截图。有人说Linux缺乏娱乐性&#xff0c;其实不然&#xff01;在Linux下玩游戏要比Windows下表现力更好&#xff0c;Linux有非常多的小游戏&#x…

Dapr 中文社区汇总

Dapr 于 2019 年在微软创建。随着时间的推移&#xff0c;许多社区成员加入该项目并做出贡献&#xff0c;扩展并帮助它在 2021 年 2 月达到了稳定的 1.0 版本。2021年3 月提交给 CNCF&#xff0c;在2021年11月被接受 Dapr 作为 CNCF 的孵化项目。关于CNCF 的每个级别的成熟度要求…

逻辑查询优化

为什么80%的码农都做不了架构师&#xff1f;>>> 1、逻辑查询优化 基于关系代数理论&#xff0c;启发式规则&#xff0c;对查询进行等价重写。 2、查询重写规则 &#xff08;1&#xff09;子查询优化 &#xff08;2&#xff09;视图重写 &#xff08;3&#xff09;等…

CentOS 7 Root用户密码重置 2017-04-02

跨平台系列汇总&#xff1a;http://www.cnblogs.com/dunitian/p/4822808.html#linux 异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 开机的时候按e &#xff08;如果正在使用&#xff0c;你可以输入reboot&#xff0c;然后赶紧按回车键&#xff0c;也…

Android之解决布局文件图片太大同步放缩后挤掉其它布局的问题

1、问题 局文件图片太大同步放缩后挤掉其它布局的问题 比如有个图片logo1.png,很大,在平板上图片布局写成这样 <ImageViewandroid:src="@drawable/logo1"android:layout_width="wrap_content"android:layout_height="wrap_content"andro…