软件设计的原则

软件设计一共有七大原则,这七大原则分别是: 
一,单一原则 
1、什么是单一原则 
单一原则就是:就一个类而言,应该仅有一个引起它变化的原因 
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会消弱或者抑制这个类完成其他职责的的能力。 
把职责定义为“变化的原因”。如果你能想到N个动机去改变一个类,那么这个类就具有多于一个的职责。这里说的“变化的原因”,只有实际发生时才有意义。可能预测到会有多个原因引起这个类的变化,但这仅仅是预测,并没有真的发生,这个类仍可看做具有单一职责,不需要分离职责。如果分离,会带来不必要的复杂性。 
如果发现一个类有多于一个的职责,应该尽量解耦。如果很难解耦,也要分离接口,在概念上解耦。

二,开-闭原则(Open-Closed Principle, OCP) 
1、什么是开-闭原则 
开闭原则就是对扩展开放,对修改关闭 
那么怎么来理解这个开-闭原则呢,就是在软件系统设计的过程中,软件系统中包含的各种组件,应该在不修改现有代码的基础上,引入新功能。开闭原则中“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中“闭”,是指对于原有代码的修改是封闭的,即不应该修改原有的代码。这样就避免了修改原有逻辑而带来次生问题和风险。

2、如何实现开-闭原则 
实现开闭原则的关键就在于“抽象”。把系统的所有可能的行为抽象成一个抽象底层,这个抽象底层规定出所有的具体实现必须提供的方法的特征。作为系统设计的抽象层,要预见所有可能的扩展,从而使得在任何扩展情况下,系统的抽象底层不需修改;同时,由于可以从抽象底层导出一个或多个新的具体实现,可以改变系统的行为,因此系统设计对扩展是开放的。 
我们在软件开发的过程中,一直都是提倡需求导向的。这就要求我们在设计的时候,要非常清楚地了解用户需求,判断需求中包含的可能的变化,从而明确在什么情况下使用开闭原则。

3、开-闭原则的优点 
1)可扩展性 
在软件完成以后,仍然可以对软件进行扩展,加入新的功能和扩展点,非常灵活。因此,这个软件系统就可以通过不断地增加新的组件,来满足不断变化的需求。 
2)可维护性 
由于对于已有的软件系统的组件,特别是它的抽象底层不去修改,因此,我们不用担心软件系统中原有组件的稳定性,这就使变化中的软件系统有一定的稳定性和延续性,避免因为修改原有组件而带来次生风险。

三,里氏代换原则(Liskov Substitution Principle,LSP) 
1,什么是里氏代换原则 
里氏代换原则就是,任何子类型都可以替换掉它们的父类型

2,如何使用里氏代换原则 
1)子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。 
2)子类中可以增加自己特有的方法。 
3)当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。 
4)当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格

四,依赖倒置原则(Dependence Inversion Principle,DIP) 
1,什么是依赖倒置原则 
依赖倒置就是,要依赖于抽象,不要依赖于实现;要针对接口编程,不要针对实现编程。 
面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性降低而且大大提高了开发的成本。 
面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变动,只要抽象不变,客户程序就不需要变化。这大大降低了客户程序与实现细节的耦合度。

2、如何做到依赖倒置原则 
以抽象方式耦合是依赖倒转原则的关键。抽象耦合关系总要涉及具体类从抽象类继承,并且需要保证在任何引用到基类的地方都可以改换成其子类,因此,里氏代换原则是依赖倒转原则的基础。 
在抽象层次上的耦合虽然有灵活性,但也带来了额外的复杂性,如果一个具体类发生变化的可能性非常小,那么抽象耦合能发挥的好处便十分有限,这时可以用具体耦合反而会更好。 
层次化:所有结构良好的面向对象构架都具有清晰的层次定义,每个层次通过一个定义良好的、受控的接口向外提供一组内聚的服务。 
依赖于抽象:建议不依赖于具体类,即程序中所有的依赖关系都应该终止于抽象类或者接口。尽量做到: 
1)任何变量都不应该持有一个指向具体类的指针或者引用。 
2)任何类都不应该从具体类派生。 
3)任何方法都不应该覆写它的任何基类中的已经实现的方法。

五,接口隔离原则(Interface Segregation Principle, ISP) 
1,什么是接口隔离原则 
接口隔离原则就是,客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。 
建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。 
说到这里,很多人会觉得接口隔离原则跟之前的单一职责原则很相似,其实不然。 
其一,单一职责原则原注重的是职责;而接口隔离原则注重对接口依赖的隔离。 
其二,单一职责原则主要是约束类,其次才是接口和方法,它针对的是程序中的实现和细节;而接口隔离原则主要约束接口接口,主要针对抽象,针对程序整体框架的构建。

2,如何做到接口隔离 
1)接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。 
2)为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。 
3)提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。

六,合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP) 
1,什么是合成/聚合复用原则 
合成/聚合复用原则就是,在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分; 
新的对象通过向这些对象的委派达到复用这些对象的目的。应首先使用合成/聚合,合成/聚合则使系统灵活,其次才考虑继承,达到复用的目的。而使用继承时,要严格遵循里氏代换原则。有效地使用继承会有助于对问题的理解,降低复杂度,而滥用继承会增加系统构建、维护时的难度及系统的复杂度。 
如果两个类是“Has-a”关系应使用合成、聚合,如果是“Is-a”关系可使用继承。”Is-A”是严格的分类学意义上定义,意思是一个类是另一个类的”一种”。而”Has-A”则不同,它表示某一个角色具有某一项责任。 
2 什么是合成?什么是聚合? 
合成(Composition)和聚合(Aggregation)都是关联(Association)的特殊种类。 
聚合表示整体和部分的关系,表示“拥有”。如奔驰S360汽车,对奔驰S360引擎、奔驰S360轮胎的关系是聚合关系,离开了奔驰S360汽车,引擎、轮胎就失去了存在的意义。在设计中, 聚合不应该频繁出现,这样会增大设计的耦合度。 
合成则是一种更强的“拥有”,部分和整体的生命周期一样。合成的新的对象完全支配其组成部分,包括它们的创建和湮灭等。一个合成关系的成分对象是不能与另一个合成关系共享的。 
换句话说,合成是值的聚合(Aggregation by Value),而一般说的聚合是引用的聚合(Aggregation by Reference)。 
明白了合成和聚合关系,再来理解合成/聚合原则应该就清楚了,要避免在系统设计中出现,一个类的继承层次超过3层,则需考虑重构代码,或者重新设计结构。当然最好的办法就是考虑使用合成/聚合原则。

3 通过合成/聚合来进行复用的优缺点 
优点: 
1) 新对象存取成分对象的唯一方法是通过成分对象的接口。 
2) 这种复用是黑箱复用,因为成分对象的内部细节是新对象所看不见的。 
3) 这种复用支持包装。 
4) 这种复用所需的依赖较少。 
5) 每一个新的类可以将焦点集中在一个任务上。 
6) 这种复用可以在运行时间内动态进行,新对象可以动态的引用与成分对象类型相同的对象。 
7) 作为复用手段可以应用到几乎任何环境中去。 
缺点:就是系统中会有较多的对象需要管理。

4 通过继承来进行复用的优缺点 
优点: 
1)新的实现较为容易,因为超类的大部分功能可以通过继承的关系自动进入子类。 
2)修改和扩展继承而来的实现较为容易。 
缺点: 
1) 继承复用破坏包装,因为继承将超类的实现细节暴露给子类。由于超类的内部细节常常是对于子类透明的,所以这种复用是透明的复用,又称“白箱”复用。 
2) 如果超类发生改变,那么子类的实现也不得不发生改变。 
3)从超类继承而来的实现是静态的,不可能在运行时间内发生改变,没有足够的灵活性。 
4)继承只能在有限的环境中使用。

七,迪米特法则(Law of Demeter LoD) 
1,什么是迪米特法则 
迪米特法则就是,一个软件实体应当尽可能少的与其他实体发生相互作用。 
这样,当一个模块修改时,就会尽量少的影响其他的模块。扩展会相对容易。 
这是对软件实体之间通信的限制。它要求限制软件实体之间通信的宽度和深度。 
1)只与你直接的朋友们通信。 
2)不要跟“陌生人”说话。 
3)每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。 
2,狭义的迪米特法则 
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。 
朋友圈的确定 
“朋友”条件: 
1)当前对象本身(this) 
2)以参量形式传入到当前对象方法中的对象 
3)当前对象的实例变量直接引用的对象 
4)当前对象的实例变量如果是一个聚集,那么聚集中的元素也都是朋友 
5)当前对象所创建的对象 
任何一个对象,如果满足上面的条件之一,就是当前对象的“朋友”;否则就是“陌生人”。 
缺点:会在系统里造出大量的小方法,散落在系统的各个角落。 
与依赖倒转原则互补使用 
3,狭义的迪米特法则的缺点: 
在系统里造出大量的小方法,这些方法仅仅是传递间接的调用,与系统的商务逻辑无关。 
遵循类之间的迪米特法则会是一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接的关联。但是,这也会造成系统的不同模块之间的通信效率降低,也会使系统的不同模块之间不容易协调。 
4,迪米特法则与设计模式 
门面(外观)模式和调停者(中介者)模式实际上就是迪米特法则的具体应用。 
5,广义的迪米特法则 
迪米特法则的主要用意是控制信息的过载。在将迪米特法则运用到系统设计中时,要注意下面的几点: 
1)在类的划分上,应当创建有弱耦合的类。 
2)在类的结构设计上,每一个类都应当尽量降低成员的访问权限。 
3)在类的设计上,只要有可能,一个类应当设计成不变类。 
4)在对其他类的引用上,一个对象对其对象的引用应当降到最低。 
6,广义迪米特法则在类的设计上的体现 
1)优先考虑将一个类设置成不变类 
2)尽量降低一个类的访问权限 
3)谨慎使用Serializable 
4)尽量降低成员的访问权限 
5)取代C Struct 
迪米特法则又叫作最少知识原则(Least Knowledge Principle或简写为LKP),就是说一个对象应当对其他对象有尽可能少的了解。 
7,如何实现迪米特法则 
1) 在类的划分上,应当创建有弱耦合的类。类之间的耦合越弱,就越有利于复用。 
2) 在类的结构设计上,每一个类都应当尽量降低成员的访问权限。一个类不应当public自己的属性,而应当提供取值和赋值的方法让外界间接访问自己的属性。 
3) 在类的设计上,只要有可能,一个类应当设计成不变类。 
4) 在对其它对象的引用上,一个类对其它对象的引用应该降到最低。
--------------------- 
作者:零_壹 
来源:CSDN 
原文:https://blog.csdn.net/xinxiaoyong100440105/article/details/59561621 
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/yanxilou/article/details/85145292