开闭原则与里氏代换原则实验 UML图及编程实现 C#

合集下载

程序设计6大原则

程序设计6大原则

程序设计6大原则程序设计是计算机科学中的重要领域,涵盖了开发、测试和维护计算机程序的过程。

为了编写高质量、可扩展、可维护的程序,程序员们必须遵循一些基本的原则。

在本文中,我将介绍6个程序设计的重要原则,它们是:单一责任原则、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则和最少知识原则。

一、单一责任原则单一责任原则(SRP)是指一个类应该只有一个引起它变化的原因。

换句话说,一个类只应该有一种职责,这样可以减少类的复杂性,并且使得类更加易于维护和测试。

通过将不同的功能拆分到不同的类中,可以实现单一职责原则。

这样的设计可以提高代码的可读性和可维护性。

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

这意味着在向系统中添加新功能时,不应该修改已有的代码,而是通过扩展现有代码来实现新的功能。

这样的设计可以减少对原有代码的影响,提高系统的稳定性和可维护性。

三、里氏替换原则里氏替换原则(LSP)是指子类必须能够替换其父类,并且在替换后不会导致程序的错误或异常。

这意味着子类应该完全符合其父类的契约,不能改变父类已经定义的行为。

这样的设计可以确保代码的可靠性和可扩展性。

四、依赖倒置原则依赖倒置原则(DIP)是指高层模块不应该依赖于低层模块,二者都应该依赖于抽象。

这意味着开发人员应该通过抽象接口来编程,而不是具体的实现类。

通过这种方式,可以减少模块之间的耦合,提高系统的灵活性和可测试性。

五、接口隔离原则接口隔离原则(ISP)是指客户端不应该依赖于它不需要的接口。

一个类不应该强迫其客户端实现那些它们不需要的方法。

这意味着开发人员应该将大的接口拆分为多个小的接口,以满足客户端的需求。

这样的设计可以减少代码的冗余,并且提高系统的可维护性和可扩展性。

六、最少知识原则最少知识原则(LOD)是指一个类不应该知道太多关于其他类的细节。

一个类应该尽可能少地了解其他类的方法和属性。

这样的设计可以减少类之间的依赖关系,提高系统的灵活性和可扩展性。

程序设计七大原则

程序设计七大原则

软件设计的七大原则设计模式遵循的一般原则:1.开-闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开发,对修改关闭.说的是,再设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展.换言之,应当可以在不必修改源代码的情况下改变这个模块的行为,在保持系统一定稳定性的基础上,对系统进行扩展。

这是面向对象设计(OOD)的基石,也是最重要的原则。

2.里氏代换原则(Liskov Substitution Principle,常缩写为.LSP)(1).由Barbar Liskov(芭芭拉.里氏)提出,是继承复用的基石。

(2).严格表达:如果每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换称o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型.换言之,一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且它根本不能察觉出基类对象和子类对象的区别.只有衍生类可以替换基类,软件单位的功能才能不受影响,基类才能真正被复用,而衍生类也能够在基类的基础上增加新功能。

(3).反过来的代换不成立(4).<墨子.小取>中说:"白马,马也; 乘白马,乘马也.骊马(黑马),马也;乘骊马,乘马也."(5).该类西方著名的例程为:正方形是否是长方形的子类(答案是"否")。

类似的还有椭圆和圆的关系。

(6).应当尽量从抽象类继承,而不从具体类继承,一般而言,如果有两个具体类A,B有继承关系,那么一个最简单的修改方案是建立一个抽象类C,然后让类A和B 成为抽象类C的子类.即如果有一个由继承关系形成的登记结构的话,那么在等级结构的树形图上面所有的树叶节点都应当是具体类;而所有的树枝节点都应当是抽象类或者接口.(7)."基于契约设计(Design By Constract),简称DBC"这项技术对LISKOV代换原则提供了支持.该项技术Bertrand Meyer伯特兰做过详细的介绍:使用DBC,类的编写者显式地规定针对该类的契约.客户代码的编写者可以通过该契约获悉可以依赖的行为方式.契约是通过每个方法声明的前置条件(preconditions)和后置条件(postconditions)来指定的.要使一个方法得以执行,前置条件必须为真.执行完毕后,该方法要保证后置条件为真.就是说,在重新声明派生类中的例程(routine)时,只能使用相等或者更弱的前置条件来替换原始的前置条件,只能使用相等或者更强的后置条件来替换原始的后置条件.3.依赖倒置原则(Dependence Inversion Principle),要求客户端依赖于抽象耦合.(1)表述:抽象不应当依赖于细节,细节应当依赖于抽象.(Program to an interface, not an implementaction)(2)表述二:针对接口编程的意思是说,应当使用接口和抽象类进行变量的类型声明,参量的类型声明,方法的返还类型声明,以及数据类型的转换等.不要针对实现编程的意思就是说,不应当使用具体类进行变量的类型声明,参量类型声明,方法的返还类型声明,以及数据类型的转换等.要保证做到这一点,一个具体的类应等只实现接口和抽象类中声明过的方法,而不应当给出多余的方法.只要一个被引用的对象存在抽象类型,就应当在任何引用此对象的地方使用抽象类型,包括参量的类型声明,方法返还类型的声明,属性变量的类型声明等. (3)接口与抽象的区别就在于抽象类可以提供某些方法的部分实现,而接口则不可以,这也大概是抽象类唯一的优点.如果向一个抽象类加入一个新的具体方法,那么所有的子类型一下子就都得到得到了这个新的具体方法,而接口做不到这一点.如果向一个接口加入了一个新的方法的话,所有实现这个接口的类就全部不能通过编译了,因为它们都没有实现这个新声明的方法.这显然是接口的一个缺点.(4)一个抽象类的实现只能由这个抽象类的子类给出,也就是说,这个实现处在抽象类所定义出的继承的登记结构中,而由于一般语言都限制一个类只能从最多一个超类继承,因此将抽象作为类型定义工具的效能大打折扣.反过来,看接口,就会发现任何一个实现了一个接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个接口.(5)从代码重构的角度上讲,将一个单独的具体类重构成一个接口的实现是很容易的,只需要声明一个接口,并将重要的方法添加到接口声明中,然后在具体类定义语句中加上保留字以继承于该接口就行了.而作为一个已有的具体类添加一个抽象类作为抽象类型不那么容易,因为这个具体类有可能已经有一个超类.这样一来,这个新定义的抽象类只好继续向上移动,变成这个超类的超类,如此循环,最后这个新的抽象类必定处于整个类型等级结构的最上端,从而使登记结构中的所有成员都会受到影响.(6)接口是定义混合类型的理想工具,所为混合类型,就是在一个类的主类型之外的次要类型.一个混合类型表明一个类不仅仅具有某个主类型的行为,而且具有其他的次要行为.(7)联合使用接口和抽象类:由于抽象类具有提供缺省实现的优点,而接口具有其他所有优点,所以联合使用两者就是一个很好的选择.首先,声明类型的工作仍然接口承担的,但是同时给出的还有一个抽象类,为这个接口给出一个缺省实现.其他同属于这个抽象类型的具体类可以选择实现这个接口,也可以选择继承自这个抽象类.如果一个具体类直接实现这个接口的话,它就必须自行实现所有的接口;相反,如果它继承自抽象类的话,它可以省去一些不必要的的方法,因为它可以从抽象类中自动得到这些方法的缺省实现;如果需要向接口加入一个新的方法的话,那么只要同时向这个抽象类加入这个方法的一个具体实现就可以了,因为所有继承自这个抽象类的子类都会从这个抽象类得到这个具体方法.这其实就是缺省适配器模式(Defaule Adapter).(8)什么是高层策略呢?它是应用背后的抽象,是那些不随具体细节的改变而改变的真理. 它是系统内部的系统____隐喻.4.接口隔离原则(Interface Segregation Principle, ISP) (1)一个类对另外一个类的依赖是建立在最小的接口上。

程序设计6大原则

程序设计6大原则

程序设计6大原则程序设计六大原则,也被称为SOLID原则,是面向对象程序设计中的基本原则,它们的实施能够提高程序的可维护性、可扩展性、可复用性和可读性。

这六大原则是:1. 单一职责原则(SRP)单一职责原则指的是一个类只应该有一个单一的功能或职责。

这意味着在一个类的设计中,应该避免将过多的职责集成到一个类中,每个类应该只关心一个单一的功能或职责。

这样做可以提高代码的可维护性和可读性,因为当我们需要修改或调试代码时,我们只需要关注单一职责的那一部分即可。

2. 开放封闭原则 (OCP)开放封闭原则指的是,在扩展一个类的功能时,应该通过增加新的代码来实现,而不是通过修改已有的代码来实现。

这意味着应该避免在项目的后期对代码进行大量修改,而是应该在设计时尽可能考虑到未来可能的扩展需求。

这样做可以提高代码的可维护性和可复用性。

3. 里氏替换原则(LSP)里氏替换原则指的是,任何一个基类可以被它的子类所替换,而不影响程序的正确性。

这意味着,在使用继承时,应该遵循基类与子类的正确关系,即子类应该继承基类的所有属性和方法,并且不应该修改原有的行为。

这样做可以提高代码的可维护性、可扩展性和可重用性。

4. 依赖倒置原则(DIP)依赖倒置原则指的是,高层次的模块不应该依赖低层次的模块,而是应该通过抽象来依赖。

这意味着在设计时应该使用接口等抽象层次,使得高层次的模块可以方便地调用低层次的模块。

这样做可以提高代码的可重用性、可扩展性和可维护性。

5. 接口隔离原则(ISP)接口隔离原则指的是,建立单一的接口,而不是多个不同的接口。

这样做可以避免实现类需要实现太多的接口而导致接口冗余。

这也可以提高代码的可维护性。

6. 迪米特原则(LoD)迪米特原则也被称为最少知识原则,指的是在设计和实现中,模块不能了解它所操作的模块的细节信息。

这意味着在一个模块中,对于它所依赖的模块,应该尽可能减少对其细节信息的依赖。

这样做可以提高代码的灵活性、可维护性和可复用性。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

C#设计模式(2)

C#设计模式(2)

public class Rectangle { private long width; private long height;
public void setWidth(long width) { this.width = width; } public long getWidth() { return this.width; } public void setHeight(long height) { this.height = height; } public long getHeight() { return this.height;
}
public class Square { private long side;
public void setSide(long side) { this.side = side; }
public long getSide() { return side; } } 正方形不可以做长方形的子类 using System;
public long getHeight() { return getSide(); } }
} }
public class Square : Rectangle { private long side;
public void setWidth(long width) { setSide(width); }
ቤተ መጻሕፍቲ ባይዱ
public long getWidth() { return getSide(); }
反过来的代换不成立 《墨子·小取》说:"娣,美人也,爱娣,非爱美人也……"娣便是妹妹, 哥哥喜爱妹妹,是因为两人是兄妹关系,而不是因为妹妹是个美人。因 此,喜爱妹妹不等同于喜爱美人。用面向对象语言描述,美人是基类, 妹妹是美人的子类。哥哥作为一个有"喜爱()"方法,接受妹妹作为参 数。那么,这个"喜爱()"方法一般不能接受美人的实例。

软件设计六大原则

软件设计六大原则

软件设计六大原则一、开闭原则(Open-Closed Principle)开闭原则是经典软件设计中最基础的原则,它规定软件实体(类、模块、函数等)应该可以扩展,但是不可修改。

在实际的开发中,开发人员必须遵循这样的设计:当软件需要变化时,应该通过增加代码以及对现有代码的修改来完成。

可以将这一原则理解为:在尽可能少地改动原有代码的前提下让程序扩展更大的灵活性。

单一职责原则是说一个类应该只有一个引起它变化的原因,它不应该同时处理多样的职责,即一个类要负责一项职责。

它遵循的原则简而言之就是:一个类或模块只负责一个功能,它只完成一项工作,而在需要完成两个功能的情况下,就要使用两个不同的类或模块来完成。

三、里氏替换原则(Liskov Substitution Principle)里氏替换原则是指如果一个基类的所有子类都能够替换掉该基类,那么客户端程序不应该受到影响,也就是说对于任何一个基类,它的客户端程序不必关心它的子类,只要知道它基类的接口,以及如何调用它的方法即可。

实现里氏替换原则是在软件架构中以多态形式实现程序模块之间相互替代通信的一种技术手段。

依赖倒转原则是指:高层模块不应该依赖于低层模块,两者都应该依赖于一个抽象的概念,即上层组件不应该依赖下层组件,而是要依赖抽象,实现上下之间的解耦,它可以使上层组件很容易地和不同下层实现变得更加灵活。

可以使得系统架构更简单、更热情地抵抗变化,比如类的替换、类的功能的增强等,而高层的模块也不会随着低层模块的改变而改变。

五、接口隔离原则(Interface Segregation Principle)接口隔离原则是说,客户端不应该依赖于它不需要的接口;如果一个接口包含的方法越多,它也就越脆弱,它越可能因为客户端的变化而变化。

因此,软件设计者应该尽量将可抽象出多个单独接口的接口拆分为多个接口,每个接口只提供一种能力,这样客户端只需要依赖那些它需要的接口,而不会不小心依赖了它不需要的接口。

面向对象编程的五大原则

面向对象编程的五大原则

面向对象编程的五大原则单一职责原则开放封闭原则里氏替换原则依赖倒置原则接口隔离原则高层的实现不应该依赖底层,(父类可以替换掉任何子类),具体说就是我们要针对接口抽象来编程,不要针对实现来编程,这样程序才能解耦。

一、"开-闭"原则(Open-Closed Principle,OCP)1.1"开-闭"原则的定义及优点1)定义:一个软件实体应当对扩展开放,对修改关闭(Software entities should be open for extension,but closed for modification.)。

即在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展。

2)满足"开-闭"原则的系统的优点a)通过扩展已有的软件系统,可以提供新的行为,以满足对软件的新需求,使变化中的软件系统有一定的适应性和灵活性。

b)已有的软件模块,特别是最重要的抽象层模块不能再修改,这就使变化中的软件系统有一定的稳定性和延续性。

c)这样的系统同时满足了可复用性与可维护性。

1.2如何实现"开-闭"原则在面向对象设计中,不允许更改的是系统的抽象层,而允许扩展的是系统的实现层。

换言之,定义一个一劳永逸的抽象设计层,允许尽可能多的行为在实现层被实现。

解决问题关键在于抽象化,抽象化是面向对象设计的第一个核心本质。

对一个事物抽象化,实质上是在概括归纳总结它的本质。

抽象让我们抓住最最重要的东西,从更高一层去思考。

这降低了思考的复杂度,我们不用同时考虑那么多的东西。

换言之,我们封装了事物的本质,看不到任何细节。

在面向对象编程中,通过抽象类及接口,规定了具体类的特征作为抽象层,相对稳定,不需更改,从而满足"对修改关闭";而从抽象类导出的具体类可以改变系统的行为,从而满足"对扩展开放"。

对实体进行扩展时,不必改动软件的源代码或者二进制代码。

软件工程八大设计原则

软件工程八大设计原则

软件工程八大设计原则软件工程八大设计原则是指在软件开发过程中,要遵守的八个设计原则,它们是:单一职责原则、开放封闭原则、里氏替换原则、依赖倒转原则、接口隔离原则、迪米特法则、合成复用原则以及最少知道原则。

这些原则能够帮助软件开发人员在设计开发过程中避免常见的问题,提高软件的可维护性、可修改性以及可扩展性。

首先,单一职责原则(SRP)要求一个类或者模块只负责一件事情。

这个原则的核心思想是将一个类的职责限制在一个方向上,避免出现过于复杂的类或模块。

这样可以提高代码的可读性和可维护性。

其次,开放封闭原则(OCP)要求系统中的类和模块可以扩展,但是不可以修改已有的代码。

这个原则可以提高系统的可扩展性,同时避免对已有的代码产生影响。

第三,里氏替换原则(LSP)要求子类能够替换掉父类并且不产生任何异常或者错误。

这个原则可以提高代码的健壮性和可维护性。

第四,依赖倒转原则(DIP)要求高层模块不应该依赖于底层模块,而是应该依赖于抽象。

这个原则可以提高代码的灵活性和可扩展性。

第五,接口隔离原则(ISP)要求一个类或者模块应该只暴露必要的接口。

这个原则可以避免类或者模块内部的接口过于复杂,提高可读性和可维护性。

第六,迪米特法则(LoD)要求一个对象应该对其他对象保持最少的了解。

这个原则可以提高代码的封装性和灵活性。

第七,合成复用原则(CARP)要求尽量使用对象组合而不是继承。

这个原则可以提高代码的可复用性和可维护性。

最后,最少知道原则(LKP)要求一个对象时一个黑盒子,尽量不要暴露太多的信息。

这个原则可以提高代码的封装性和灵活性。

总的来说,这八个设计原则能够帮助软件开发人员开发高质量的代码,提高代码的可读性、可维护性以及可扩展性。

在实际开发过程中,开发人员应该充分运用这些原则,并根据实际情况做出相应的调整。

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

实验2 开闭原则与里氏代换原则综合训练
一、实验目的:加深对开闭原则与里氏代换原则的理解
二、实验环境:C#.Net
三、实验内容:
1、问题(一)“长方形和正方形“思辨题:最早来自于C++和Smalltalk 领域。

正方形是否是长方形的子类的问题,西方一个很著名的思辨题。

试写一个求面积与周长的解决方案,要求画UML图并编程实现,并体现出开闭原则与里氏代换原则。

提示:
长方形类:两个属性,宽度和高度;
正方形类:一个属性,边。

1、UML类图如下:
2、运用的设计原则:
开闭原则:一个软件实体应当对扩展开放,对修改关闭。

定义一个抽象类Stringle类,抽象类中有求面积和周长的函数,当想再次扩展求圆、菱形等的面积,继承抽象类,实现其功能,很方便就对其进行扩展。

里氏代换原则:所有引用基类(父类)的地方必须能透明地使用其子类的对象。

Stringle类为基类,Square类和Rectangular为其子类,在引用基类的地方就能使用子类实现,代码如下。

3、C#实现代码如下:
using System;
using System .Collections .Generic;
using System .Linq;
using System .Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string [] args)
{
Stringle square = new Square(5);
Stringle rectangular = new Rectangular(3 , 5);
Accounter accounter = new Accounter();
accounter .calculate(square);
accounter .calculate(rectangular);
Console .ReadLine();
}
}
public class Accounter
{
public Accounter() { }
public void calculate(Stringle stringle) {
stringle .Area();
stringle .ZhouChang();
}
}
///<summary>
///抽象类,定义了求面积和周长的方法,没有实现///</summary>
public abstract class Stringle
{
public abstract void Area();
public abstract void ZhouChang();
}
///<summary>
///正方形类继承自抽象类
///</summary>
public class Square :Stringle
{
public Square(int w)
{
width = w;
}
public override void Area()
{
int area = width * width;
Console .WriteLine("正方形的面积为:" + area); }
public override void ZhouChang()
{
int scircle = width * 4;
Console.WriteLine("正方形的周长为:"+ scircle); }
}
///<summary>
///长方形类继承自抽象类
///</summary>
public class Rectangular :Stringle
{
int width;
public Rectangular(int w , int h)
{
width = w;
height = h;
}
public override void Area()
{
int area = width * height;
Console .WriteLine("长方形的面积为:" + area); }
public override void ZhouChang()
{
int scircle = width * 2 + height * 2;
Console.WriteLine("长方形的周长为:"+ scircle); }
}
}
4、实验结果:
2、问题(二)“士兵用枪杀敌”UML图理解与编程实现
(1)CS游戏中的枪械类图
(2)如果我们有一个玩具手枪,该如何定义呢?先在上图增加一个类ToyGun,然后继承于AbstractGun类
(3)玩具枪与真实枪分离的类图
用C#实现代码如下:
using System;
using System .Collections .Generic;
using System .Linq;
using System .Text;
namespace ConsoleApplicationGun
{
class Program
{
static void Main(string [] args)
{
AbstractGun handgun = new HandGun();//手枪
AbstractGun riflegun = new RifleGun();//步枪
AbstractGun machinegun = new MachineGun();//机枪
AbstractGun toygun=new ToyGun();//
// AbstractToy toygun = new ToyGun();//
Soldier soldier = new Soldier();
soldier .killEnemy(handgun);//士兵拿手枪杀敌人 soldier .killEnemy(riflegun);//士兵拿步枪杀敌人 soldier .killEnemy(machinegun);//士兵拿机枪杀敌人
soldier .killEnemy(toygun);//士兵拿玩具枪杀敌人 }
}
public class Soldier
{
public Soldier() { }
public void killEnemy(AbstractGun abstractgun)
{
abstractgun .shoot();
}
}
public abstract class AbstractGun
{
public AbstractGun() { }
public abstract void shoot();
}
public abstract class AbstractToy
{
public AbstractToy() { }
public abstract void shoot();
}
public class HandGun :AbstractGun
{
public HandGun() { }
public override void shoot()
{
Console .WriteLine("Handgun shoot!"); }
}
public class RifleGun :AbstractGun
{
public RifleGun() { }
public override void shoot()
{
Console .WriteLine("RifleGun shoot!"); }
}
public class MachineGun :AbstractGun
{
public MachineGun() { }
public override void shoot()
{
Console .WriteLine("MachineGun shoot!"); }
}
public class ToyGun :AbstractGun
{
public ToyGun() { }
public override void shoot()
{
Console .WriteLine("ToyGun shoot!");
}
}
}
实验结果:。

相关文档
最新文档