.net学习之继承、里氏替换原则

合集下载

28第二十八讲里氏代换原则

28第二十八讲里氏代换原则

二、反过来的代换不成立
里氏代换原则(Liskov Substitution Principle): 一个软件实体如果使用的是一个子类的话,那 么它不能适用于其父类。
三、企鹅是鸟类吗??
鸟类
老鹰
麻雀
企鹅
四、正方形是一种长方形吗??
width height
side
正方形不是长方形的子类,因此之间不存在里氏代换关系。
北风网在线培训
设计模式系列课程 基础三 里氏代换原则
讲师:历风行
一、什么是里氏代换原则
里氏代换原则(Liskov Substitution Principle): 一个软件实体如果使用的是一个父类的话,那 么一定适用于其子类,而且它察觉不出父类和子 类对象的区别。也就是说,在软件里面,把父类 替换成它的子类,程序的行为没有变化。
能够骗过Java编译器,真的符合里氏代换原则吗?答案是否定的
欢迎访问北风学习在线
我们的网址是
五、好骗的Java编译器
长方形 width height
正方形 width height side 能够骗过Java编译器,真的符合里氏代换原则吗?答案是否定的
六、原来还有一个四边形的概念?
四边形 width height
ห้องสมุดไป่ตู้
长方形 width height
正方形 width height side

java组合五个原则

java组合五个原则

Java组合设计模式中的五个原则缩写为SOLID,分别是:
1. 单一职责原则(Single Responsibility Principle,SRP):一个类应该只有一个职责,即一个类只负责一件事情,一个类只负责一种变化。

2. 开放封闭原则(Open/Closed Principle,OCP):软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。

即在不修改原有功能的前提下,可以扩展功能。

3. 里氏替换原则(Liskov Substitution Principle,LSP):子类必须要能替换其父类,即如果一个类必须要继承某一个类,那么这个类必须要实现某一个类的所有功能。

4. 接口隔离原则(Interface Segregation Principle,ISP):客户端不应该依赖它不需要的接口,即客户端应该依赖它需要的接口。

5. 依赖倒置原则(Dependency Inversion Principle,DIP):高层模块不应该依赖于低层模块,两者都应该依赖于抽象接口,即高层模块应该依赖于抽象接口,而不是具体实现。

以上五个原则是Java设计模式中非常重要的原则,它们可以帮助开发人员在设计和实现Java 类时遵循最佳实践,提高代码的可读性、可维护性和可扩展性。

js 里氏替换原则

js 里氏替换原则

js 里氏替换原则里氏替换原则(Liskov Substitution Principle,简称LSP)是面向对象设计中最基本的原则之一,是一种用于衡量代码设计质量的基本标准。

LSR原则的主要思想是:子类对象应该能够替换其父类对象,并且程序不会出现异常或明显的错误。

简单的说,LSR原则是对继承的一种适用性原则,指导我们在使用继承时如何更好地设计子类和父类之间的关系,从而使代码更加灵活、可扩展和可维护。

下面我们将详细探讨里氏替换原则的含义、实现和应用。

里氏替换原则的含义LSR原则的核心思想是:派生类(子类)应该可以无缝地替换其基类(父类),且不会破坏程序的正确性和行为。

具体来说,一个基类的实例应该能够被其子类的实例代替,而且程序在不知情的情况下不会发生错误、异常或不可预测的行为。

这个实践虽然看起来很简单,但要实现它需要遵循如下原则:子类必须实现父类的所有方法,但不一定要实现父类的所有属性。

子类可以增加新的方法和属性,但不能修改基类的属性和方法。

子类不能改变基类已有方法的功能、事后条件(程序执行完方法后的结果保证)和前置条件(程序调用方法时的参数和状态要求),即它们的约束条件不能变。

子类可以扩展父类的方法,但不能执行比父类更严格的限制条件。

从上述原则中可以看出,LSR原则的核心在于遵循“父类不黄子类”的原则,即父类定义了一定的规范、协议和契约,而子类则必须遵循这些规范,不能胡乱改变父类的功能和语义。

LSR原则的实现从代码实现来看,要实现LSP原则,必须满足以下几点:继承关系:如果派生类和其基类不存在继承关系,则无法实现LSP 原则。

重载关系:派生类和其基类中方法的参数和返回类型必须相同,以保证其可互换。

异常关系:子类不能抛出比父类更多的异常,只能抛出和基类异常相同或更少的异常。

前置条件:子类调用的基类方法必须满足父类的前置条件,即子类调用方法的参数和状态保持和父类一致。

后置条件:子类调用基类方法后所得的结果必须满足父类的后置条件,即子类不能改变父类已定义的约束条件。

里氏替换原则(LSP)

里氏替换原则(LSP)

⾥⽒替换原则(LSP)⼀、定义(1)、所有使⽤基类的地⽅必须能够使⽤⼦类进⾏替换,⽽程序的⾏为不会发⽣任何变化(替换为⼦类之后不会产⽣错误或者异常)。

只有这样,⽗类才能真正被复⽤,⼦类能够在⽗类的基础上增减新的属性和⾏为。

才能真正的实现多态⾏为。

(2)、当⼦类继承⽗类的时候,⼦类就拥有了⽗类的属性和⾏为。

(注意:只是类型⽽已) 但是如果⼦类覆盖⽗类的某些⽅法,那么原来使⽤⽗类的地⽅就可能出现错误。

(如何理解呢?表⾯上看是调⽤的是⽗类的⽅法,实际运⾏的时候⼦类⽅法覆盖了⽗类的⽅法,注意⽗类⽅法其实是存在的,通过作⽤域限定符可以访问到,两个⽅法的实现可能不⼀样,这样不符合LSP⾥⽒替换原则。

) (3)、⾥⽒替换原则是实现开闭原则的重要⽅式之⼀。

由于使⽤基类对象的地⽅可以使⽤⼦类对象,因此程序中尽量使⽤基类类型进⾏定义,⽽在运⾏的时候确定⼦类类型,⼦类对象替换⽗类对象。

(有点⾯向接⼝编程的味道,对外提供接⼝,⽽不是实现类)。

或者可以实现公共⽗类(⽗类中公共属性和⾏为)。

编程实验:长⽅形和正⽅形的驳论1、正⽅形是⼀种特殊的长⽅形(is-a关系):类图:正⽅形类继承于长⽅形类。

1int main()2 {3//LSP原则:⽗类出现的地⽅必须能⽤⼦类替换4 Rectangle* r = new Rectangle();//Square *r = new Square();5 r->setWidth(5);6 r->setHeight(4);7 printf("Area = %d\n",r->getArea()); //当⽤⼦类时,结果是16。

⽤户就不8//明⽩为什么长5,宽4的结果不是20,⽽是16.9//所以正⽅形不能代替长⽅形。

即正⽅形不能10//继承⾃长⽅形的⼦类11return0;12 }2、改进的继承关系---符合LSP原则(⾯向接⼝编程)类图:1int main()2 {3//LSP原则:⽗类出现的地⽅必须能⽤⼦类替换4 QuadRangle* q = new Rectangle(5, 4); //Rectangle* q = new Rectangle(5, 4);或Square *q = new Square(5);56 printf("Area = %d, Perimeter = %d\n",q->getArea(), q->getPerimeter());78return0;9 }3、鸵鸟不是鸟类1//⾯向对象设计原则:LSP⾥⽒替换原则2//鸵鸟不是鸟的测试程序34 #include <stdio.h>56//鸟类7class Bird8 {9private:10double velocity; //速度11public:12virtual void fly() {printf("I can fly!\n");}13virtual void setVelocity(double v){velocity = v;}14virtual double getVelocity(){return velocity;}15 };1617//鸵鸟类Ostrich18class Ostrich : public Bird19 {20public:21void fly(){printf("I can\'t fly!");}22void setVelocity(double v){Bird::setVelocity(0);}23double getVelocity(){return Bird::getVelocity();}24 };2526//测试函数27void calcFlyTime(Bird& bird) //参数是引⽤⽗类引⽤⼦类的时候,会有多态的⾏为28 {29try30 {31double riverWidth = 3000;3233if(bird.getVelocity()==0) throw0;3435 printf("Velocity = %f\n", bird.getVelocity());36 printf("Fly time = %f\n", riverWidth /bird.getVelocity());37 }38catch(int) //异常处理39 {40 printf("An error occured!") ;41 }42 }4344int main()45 {46//遵守LSP原则时,⽗类对象出现的地⽅,可⽤⼦类替换47 Bird b; //⽤⼦类Ostrich替换Bird4849 b.setVelocity(100); //替换之后,会直接调⽤⼦类的⽅法5051 calcFlyTime(b); //⽗类测试时是正常的,⼦类时会抛出异常,违反LSP5253return0;54 }⼆、历史替换原则的4层含义(良好的继承定义规范,主要包括4层含义)1、⼦类必须实现⽗类中声明的所有⽅法。

编程技巧:提高代码质量的十大原则

编程技巧:提高代码质量的十大原则

编程技巧:提高代码质量的十大原则1. 遵循单一职责原则(SRP)单一职责原则指的是一个类或模块应该有且只有一个改变的原因。

这意味着每个类或模块负责完成一件特定的任务,不涉及其他无关的逻辑。

遵循SRP可以提高代码的可维护性和复用性。

2. 开闭原则(OCP)开闭原则要求软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。

通过使用抽象和多态来实现,可以方便地添加新功能而不需要修改现有代码。

3. 里氏替换原则(LSP)里氏替换原则要求派生类必须能够替换其基类出现的任何地方,而不会产生任何错误或异常行为。

遵循LSP可以保证代码在不同层次上进行正确并且无缝切换。

4. 接口隔离原则(ISP)接口隔离原则指出客户端不应该强迫依赖于它们不使用的接口。

通过将大型接口拆分为更小和更具体的接口,可以使系统更加灵活、可维护和可扩展。

5. 依赖倒置原则(DIP)依赖倒置原则要求高层模块不应该依赖于低层模块的具体实现,而应该依赖于抽象。

这可以通过使用接口或抽象类来实现,从而提高系统的可测试性和可维护性。

6. 迪米特法则(LoD)迪米特法则也被称为最少知识原则,它要求一个对象对其他对象的引用越少越好。

通过减少对象之间的耦合性,可以降低代码的复杂性和风险。

7. 单一职责函数(SRF)单一职责函数即每个函数只负责一项任务。

功能简洁明确的函数易于理解和测试,并且提高了代码可读性和可维护性。

8. 避免重复代码重复代码是造成逻辑错误和困扰程序员的主要原因之一。

通过封装公共功能、使用函数库或设计模式等方法来避免重复代码,可以提高代码复用率并减少错误发生的可能性。

9. 使用有意义的命名良好的命名可以使代码更加清晰易懂,增加可维护性。

命名应该能够准确地描述变量、函数、类等实体的作用和功能,避免使用缩写或无意义的名称。

10. 编写清晰的注释和文档良好的注释和文档可以帮助其他开发人员理解代码,并提高代码的可读性。

注释应该解释代码的意图而不是重复代码;文档应该包括API接口、使用示例、注意事项等内容。

编程中设计模式的六大原则

编程中设计模式的六大原则

编程中设计模式的六⼤原则设计模式的六⼤原则1、开闭原则(Open Close Principle)开闭原则的意思是:对扩展开放,对修改关闭。

在程序需要进⾏拓展的时候,不能去修改原有的代码,实现⼀个热插拔的效果。

简⾔之,是为了使程序的扩展性好,易于维护和升级。

想要达到这样的效果,我们需要使⽤接⼝和抽象类,后⾯的具体设计中我们会提到这点。

2、⾥⽒代换原则(Liskov Substitution Principle)⾥⽒代换原则是⾯向对象设计的基本原则之⼀。

⾥⽒代换原则中说,任何基类可以出现的地⽅,⼦类⼀定可以出现。

LSP 是继承复⽤的基⽯,只有当派⽣类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复⽤,⽽派⽣类也能够在基类的基础上增加新的⾏为。

⾥⽒代换原则是对开闭原则的补充。

实现开闭原则的关键步骤就是抽象化,⽽基类与⼦类的继承关系就是抽象化的具体实现,所以⾥⽒代换原则是对实现抽象化的具体步骤的规范。

3、依赖倒转原则(Dependence Inversion Principle)这个原则是开闭原则的基础,具体内容:针对接⼝编程,依赖于抽象⽽不依赖于具体。

4、接⼝隔离原则(Interface Segregation Principle)这个原则的意思是:使⽤多个隔离的接⼝,⽐使⽤单个接⼝要好。

它还有另外⼀个意思是:降低类之间的耦合度。

由此可见,其实设计模式就是从⼤型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。

5、迪⽶特法则,⼜称最少知道原则(Demeter Principle)最少知道原则是指:⼀个实体应当尽量少地与其他实体之间发⽣相互作⽤,使得系统功能模块相对独⽴。

6、合成复⽤原则(Composite Reuse Principle)合成复⽤原则是指:尽量使⽤合成/聚合的⽅式,⽽不是使⽤继承。

面向对象编程中的代码设计原则

面向对象编程中的代码设计原则

面向对象编程中的代码设计原则面向对象编程(Object-oriented Programming, OOP)是一种编程范式,它将现实世界中的对象模型与计算机程序结合在一起。

在面向对象编程中,代码的设计至关重要,良好的设计可以提高代码的可读性、可维护性和可扩展性。

以下是面向对象编程中常用的几个代码设计原则。

1.单一职责原则(Single Responsibility Principle, SRP)单一职责原则认为,一个类应该只有一个责任。

换句话说,类应该只有一个引起它变化的原因。

这样,当需求发生变化时,只有与该变化相关的类需要进行修改,而不需要修改与之无关的其他类。

这样可以减少代码的耦合性,提高代码的可维护性和可扩展性。

2.开放封闭原则(Open-Closed Principle, OCP)开放封闭原则认为,代码应该对扩展开放,对修改封闭。

换句话说,当需求发生变化时,应该通过添加新的代码来扩展功能,而不是修改已有的代码。

这样可以保证原有的代码稳定性,减少引入错误的风险。

3.里氏替换原则(Liskov Substitution Principle, LSP)里氏替换原则认为,子类对象应该能够替换父类对象并且保持程序的正确性。

换句话说,父类定义的规范在子类中应该得以遵守。

只有符合里氏替换原则,才能保证代码的正确性和可扩展性。

4.依赖倒置原则(Dependency Inversion Principle, DIP)依赖倒置原则认为,高层模块不应该依赖于低层模块,而是应该依赖于抽象。

具体来说,高层模块应该通过抽象接口与底层模块进行交互,而不是直接依赖于底层模块的实现。

这样可以降低模块之间的耦合性,提高代码的可扩展性。

5.接口隔离原则(Interface Segregation Principle, ISP)接口隔离原则认为,客户端不应该依赖于它不需要的接口。

换句话说,接口的设计应该精简,只包含客户端所需的方法。

开闭原则、里氏替换原则和依赖倒置原则的定义及其相互关系

开闭原则、里氏替换原则和依赖倒置原则的定义及其相互关系

开闭原则、里氏替换原则和依赖倒置原则的定义及其相互关系开闭原则(Open/Closed Principle,OCP):开闭原则是面向对象设计中的一个基本原则,由勃兰特·梅耶(Bertrand Meyer)于1988年提出。

该原则定义为:软件中的对象(类、模块、函数等)应该对扩展开放,对修改封闭。

这意味着当需要添加新功能时,应该通过扩展已有的代码来实现,而不是修改已有的代码。

通过遵循开闭原则,可以减少对已有代码的影响,提高系统的稳定性和可维护性。

里氏替换原则(Liskov Substitution Principle,LSP):里氏替换原则是由芭芭拉·利斯科夫(Barbara Liskov)于1987年提出的,其定义为:如果对于每一个类型S的对象o1,都有类型T的对象o2,使得以T 定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型S是类型T的子类型。

简单来说,任何基类可以被其子类所替代而不影响程序的正确性。

这确保了派生类可以完全替代基类并继承其所有行为。

依赖倒置原则(Dependency Inversion Principle,DIP):依赖倒置原则是由罗伯特·C·马丁(Robert C. Martin)提出的,其定义为:高层模块不应该依赖于低层模块,两者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。

依赖倒置原则强调使用抽象接口定义系统的高层模块,而将具体实现推迟到低层模块中。

这有助于降低模块之间的耦合度,提高系统的灵活性和可维护性。

相互关系:•开闭原则、里氏替换原则和依赖倒置原则是SOLID设计原则的一部分,共同促使设计和编写更加灵活、可扩展和易维护的软件系统。

•依赖倒置原则有助于实现开闭原则,通过依赖于抽象而不是具体实现,高层模块可以对抽象进行扩展而不影响底层模块。

•里氏替换原则是开闭原则的基础,确保通过子类扩展而不破坏原有代码的正确性。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

.net学习之继承、里氏替换原则
想索取更多相关资料请加qq:649085085或登录
PS;本文档由北大青鸟广安门收集自互联网,仅作分享之用。

今天来认识.net学习中关于继承、里氏替换原则LSP、虚方法、多态、抽象类、Equals方法、接口、装箱拆箱、字符串等知识!
1.继承
(1)创建子类对象的时候,在子类对象中会为子类对象的字段开辟空间,也会为父类的所有字段开辟空间,只不过父类私有的成员访问不到
(2)子类从父类继承父类所有的非私有成员,但是父类的所有字段也会创建,只不过父类私有的成员访问不到
(3)base关键字可以调用父类的非私有成员
(4)子类的访问级别不能比父类高,原因是访问子类的同时也访问了父类,如果子类的访问级别不能比父类高,就矛盾了
(5)创建子类的时候,会先调用子类的构造函数,然后调用父类的构造函数,然后执行父类的构造函数,最后再执行子类的构造函数
(6)子类的构造函数后面默认加了一个:base()通过这个调用父类的无参构造函数,如果父类没有无参数的构造函数,将会报错,因为子
类的构造函数默认会调用父类的无参数的构造函数
(7)使用base关键字可以显示的指定子类构造函数调用父类的构造函数
(8)继承的特征:
单根性:类只能有一个父类
传递性:子类继承父类所有的非私有成员
(9)父类与子类存在同名成员的时候,如果创建一个子类对象,调用这个子类对象的同名方法会调用子类的
(10)new关键字的第2作用隐藏父类的同名成员
2.里氏替换原则LSP
子类可以替换父类的位置,并且程序的功能不受影响
Person p = new Student();
p.SayHi();//这个是调用Person的SayHi()
如果一个父类变量指向的是子类对象,将这个父类对象转换为子类对象不会报错
Person p = new Student();
Student s = (Student)p;这种类型转换不会报错
如果一个父类变量指向的就是一个父类对象,将这个父类对象转换为子类对象会报错
Person p = new Person();
Student s = (Student)p;这种类型转换会报错
3.虚方法 virtual关键字
如果子类重写了父类的虚方法,那么通过父类变量来调用这个方法的时候会调用子类的,如果没有,则会调用父类的
4.多态
同一种行为,对于不同的事物,有不同的表现形式
多态的表现形式之一:将父类类型作为方法的参数,屏蔽多个子类的不同,将多个子类当成父类来统一处理
多态的表现形式之二:将父类类型作为方法的返回值
5.抽象类、抽象方法
(1)抽象方法用abstract关键字修饰,抽象方法不能有方法体,抽象方法必须在抽象类中
(2)抽象类不能实例化,因为有抽象成员,而抽象成员没有方法体的
(3)如果子类继承抽象类,子类必须重写父类的抽象方法
(4)抽象类中可以拥有非抽象成员,为了继承给子类
(5)当子类必须重写父类的方法或者父类没有必要实例化就用抽象类
6.Equals
object类里面的equals方法是比较两个对象的引用地址,如果引用地址是一样的返回true
string str1 = "abc";
string str2 = "abc";
str1.Equals(str2);//返回True,这个Equals方法是string类的,string 类的Equals方法是比较两个字符串的内容是否相同
int a = 1;
int b = 1;
a.Equals(b);//返回True
值类型Equals方法比较的是两个结构对象里字段的值(这个时候不存在重写,只是值类型自己新增的一个Equals方法)
所以:
引用类型的Equals方法默认比较的是两个对象的引用地址
string类型,值类型的Equals方法比较的是两个结构对象里字段的值
7.接口
(1)接口表示具有某种能力
(2)接口的本质是一个特殊的抽象类
(3)接口中的成员默认就是抽象的
(4)接口中只能定义方法、属性、事件、索引器
(5)接口中抽象成员不能有访问修饰符,默认就是public
(6)接口就是一个纯粹的为了规范实现类的
(7)string Name{get;set;}这个在接口中不是一个自动属性,是一个普通的属性,只不过get set方法没有实现
(8)什么时候使用抽象类:可以找到父类,并且希望通过父类继承给子类一些成员
什么时候使用接口:多个类具有相同的方法,但是却找不出父类
(9)显示实现接口:是为了解决方法名冲突的问题,显示实现的接口的方法是私有的,所以不能通过对象的变量来调用
(10)显示实现接口:这个接口的实现方法只能通过接口变量来调用
(11)要避免定义多功能接口,以免造成接口污染
8.装箱拆箱
装箱:值类型转化为引用类型int i = 12; object obj = i;
拆箱:引用类型转换为值类型 int j = (int)obj;
装箱和拆箱是比较消耗性能的,要尽量去避免装箱和拆箱操作
9.字符串
(1)字符串是特殊的引用类型
(2)字符串我们可以看做是一个字符数组string str = "abcd";char c = str[0];
(3)字符串对象一旦创建,这个对象就不能被修改
(4)在创建一个字符串对象的时候,会先去字符串拘留池中寻找是否有相同字符串内容的对象,如果有就直接让变量指向这个对象,如果没
有再创建新的对象
比如:string s1 = "a"; string s2 = "b"; s1 = "b"; s1的引用地址和s2的引用地址是相同的,都指向字符串拘留池中的“b”,不会再创
建一个“b”
(5)字符串对象一旦创建,不会被GC回收,因为微软就认为字符串是常用的
(6)字符串常用的方法、属性:
String s = new String(new char[]{'a','b'});//构造函数只能传递字符数组
int i = s.Length
string s = string.Empty代表一个空的字符串 "",不是指的null string.Empty等于"",推荐使用string.Empty;为了防止不会写错
int i = pare(s1,s2);比较两个字符串的大小,返回-1,0,1
string s = string.Concat(s1,s2)连接字符串并组成一个新的字符串
bool b = s.Contains("ab");判断字符串里面是否包含指定的字符串
b = s.EndWith("b");判断字符串是否以指定的字符串结尾
b = s.StartsWith("a");判断字符串是否以指定的字符串开始
int i = s.IndexOf('a');查找指定的字符或者字符串在字符串中的索引,如果没有返回-1
int i = stIndexOf('!');从字符串的结尾往前面查,第一次字符串出现的索引
string s = s.Insert(1,"c");在字符串的指定位置插入字符串
char[] c = s.ToCharArray()将字符串转换为字符数组
还有好多方法就不写了
(7)StringBuilder
StringBuilder这个类的对象是可变的,当改变这个对象的字符串时,不会新开辟空间,而是直接改变。

10.Stopwatch watch = new Stopwatch();//计时器
watch.Start();
watch.Stop();
watch.ElapsedMilliseconds毫秒数
想索取更多相关资料请加qq:649085085或登录 PS;本文档由北大青鸟广安门收集自互联网,仅作分享之用。

相关文档
最新文档