开发中我们需要遵循的几个开发原则!(一)

 

一、开发原则

S:单一职责SRP

O:开放封闭原则OCP

L:里氏替换原则LSP

I:接口隔离法则

D:依赖倒置原则DIP

合成/聚合复用原则

迪米特法则

在软件开发中,前人对软件系统的设计和开发总结了一些原则和模式, 不管用什么语言做开发,都将对我们系统设计和开发提供指导意义。本文主要将总结这些常见的原则和具体阐述意义。

面向对象的基本原则(solid)是五个,但是在经常被提到的除了这五个之外还有迪米特法则和合成复用原则等,所以在常见的文章中有表示写六大或七大原则的; 除此之外我还将给出一些其它相关书籍和互联网上出现的原则;

二、S单一职责SRP

Single-Responsibility Principle,一个类,最好只做一件事,只有一个引起它的变化。单一职责原则可以看做是低耦合、高内聚在面向对象原则的引申,将职责定义为引起变化的原因,以提高内聚性减少引起变化的原因。

1、定义

一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中。(Every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class.),即又定义有且仅有一个原因使类变更。

2、原则分析

一个类或者大到模块,小到方法,承担的职责越多,它被复用的可能性越小,而且如果一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作。

类的职责主要包括两个方面:数据职责和行为职责,数据职责通过其属性来体现,而行为职责通过其方法来体现。

单一职责原则是实现高内聚、低耦合的指导方针,在很多代码重构手法中都能找到它的存在,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关重构经验。

3、优点

降低类的复杂性,类的职责清晰明确。比如数据职责和行为职责清晰明确;

提高类的可读性和维护性;

变更引起的风险减低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的类有影响,对其他接口无影响,这对系统的扩展性、维护性都有非常大的帮助。

注意:单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计得是否合理,但是“职责”和“变化原因”都是没有具体标准的,一个类到底要负责那些职责?这些职责怎么细化?细化后是否都要有一个接口或类?这些都需从实际的情况考虑。因项目而异,因环境而异。

4、例子

SpringMVC中Entity、DAO、Service、Controller、Util等的分离。

三、O开放封闭原则OCP

Open – ClosedPrinciple,OCP对扩展开放,对修改关闭(设计模式的核心原则)

1、定义

一个软件实体(如类、模块和函数)应该对扩展开放,对修改关闭。意思是在一个系统或者模块中,对于扩展是开放的,对于修改是关闭的。一个 好的系统是在不修改源代码的情况下,可以扩展你的功能。而实现开闭原则的关键就是抽象化。

2、原则分析

当软件实体因需求要变化时, 尽量通过扩展已有软件实体,可以提供新的行为,以满足对软件的新的需求,而不是修改已有的代码,使变化中的软件有一定的适应性和灵活性 。已有软件模块,特别是最重要的抽象层模块不能再修改,这使变化中的软件系统有一定的稳定性和延续性。

实现开闭原则的关键就是抽象化 :在”开-闭”原则中,不允许修改的是抽象的类或者接口,允许扩展的是具体的实现类,抽象类和接口在”开-闭”原则中扮演着极其重要的角色..即要预知可能变化的需求.又预见所有可能已知的扩展..所以在这里”抽象化”是关键!

可变性的封闭原则:找到系统的可变因素,将它封装起来。这是对”开-闭”原则最好的实现。不要把你的可变因素放在多个类中,或者散落在程序的各个角落。你应该将可变的因素,封套起来..并且切忌不要把所用的可变因素封套在一起。最好的解决办法是,分块封套你的可变因素!避免超大类、超长类、超长方法的出现!!给你的程序增加艺术气息,将程序艺术化是我们的目标!

3、例子

设计模式中模板方法模式和观察者模式都是开闭原则的极好体现。

四、L里氏替换原则LSP

Liskov Substitution Principle,LSP:任何基类可以出现的地方,子类也可以出现;这一思想表现为对继承机制的约束规范,只有子类能够替换其基类时,才能够保证系统在运行期内识别子类,这是保证继承复用的基础。

1、定义

第一种定义方式相对严格:如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有变化,那么类型S是类型T的子类型。

第二种更容易理解的定义方式:所有引用基类(父类)的地方必须能透明地使用其子类的对象。即子类能够必须能够替换基类能够从出现的地方。子类也能在基类 的基础上新增行为。
里氏代换原则由2008年图灵奖得主、美国第一位计算机科学女博士、麻省理工学院教授BarbaraLiskov和卡内基.梅隆大学Jeannette Wing教授于1994年提出。其原文如下:Let q(x) be a property provableabout objects x of type T. Then q(y) should be true for objects y of type Swhere S is a subtype of T. 

2、原则分析

讲的是基类和子类的关系,只有这种关系存在时,里氏代换原则才存在。正方形是长方形是理解里氏代换原则的经典例子。

里氏代换原则可以通俗表述为:在软件中如果能够使用基类对象,那么一定能够使用其子类对象。把基类都替换成它的子类,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类的话,那么它不一定能够使用基类。

里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。

五、I接口隔离法则

(Interface Segregation Principle,ISL):客户端不应该依赖那些它不需要的接口。(这个法则与迪米特法则是相通的)

1、定义

客户端不应该依赖那些它不需要的接口。

另一种定义方法:一旦一个接口太大,则需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。
注意,在该定义中的接口指的是所定义的方法。例如外面调用某个类的public方法。这个方法对外就是接口。

2、原则分析:

(1)接口隔离原则是指使用多个专门的接口,而不使用单一的总接口。每一个接口应该承担一种相对独立的角色,不多不少,不干不该干的事,该干的事都要干。
• 一个接口就只代表一个角色,每个角色都有它特定的一个接口,此时这个原则可以叫做“角色隔离原则”。
• 接口仅仅提供客户端需要的行为,即所需的方法,客户端不需要的行为则隐藏起来,应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口。
(2)使用接口隔离原则拆分接口时,首先必须满足单一职责原则,将一组相关的操作定义在一个接口中,且在满足高内聚的前提下,接口中的方法越少越好。
(3)可以在进行系统设计时采用定制服务的方式,即为不同的客户端提供宽窄不同的接口,只提供用户需要的行为,而隐藏用户不需要的行为。

六、D依赖倒置原则DIP

Dependency-Inversion Principle 要依赖抽象,而不要依赖具体的实现, 具体而言就是高层模块不依赖于底层模块,二者共同依赖于抽象。抽象不依赖于具体,具体依赖于抽象。

1、定义

高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。简单的说,依赖倒置原则要求客户端依赖于抽象耦合。原则表述:

(1)抽象不应当依赖于细节;细节应当依赖于抽象;

(2)要针对接口编程,不针对实现编程。

2、原则分析

(1)如果说开闭原则是面向对象设计的目标,依赖倒转原则是到达面向设计”开闭”原则的手段..如果要达到最好的”开闭”原则,就要尽量的遵守依赖倒转原则. 可以说依赖倒转原则是对”抽象化”的最好规范! 我个人感觉,依赖倒转原则也是里氏代换原则的补充..你理解了里氏代换原则,再来理解依赖倒转原则应该是很容易的。

(2)依赖倒转原则的常用实现方式之一是在代码中使用抽象类,而将具体类放在配置文件中。

(3)类之间的耦合:零耦合关系,具体耦合关系,抽象耦合关系。依赖倒转原则要求客户端依赖于抽象耦合,以抽象方式耦合是依赖倒转原则的关键。

3、例子1

理解这个依赖倒置,首先我们需要明白依赖在面向对象设计的概念:
依赖关系(Dependency):是一种使用关系,特定事物的改变有可能会影响到使用该事物的其他事物,在需要表示一个事物使用另一个事物时使用依赖关系。(假设A类的变化引起了B类的变化,则说名B类依赖于A类。)大多数情况下,依赖关系体现在某个类的方法使用另一个类的对象作为参数。在UML中,依赖关系用带箭头的虚线表示,由依赖的一方指向被依赖的一方。

4、例子2

某系统提供一个数据转换模块,可以将来自不同数据源的数据转换成多种格式,如可以转换来自数据库的数据(DatabaseSource)、也可以转换来自文本文件的数据(TextSource),转换后的格式可以是XML文件(XMLTransformer)、也可以是XLS文件(XLSTransformer)等。

《开发中我们需要遵循的几个开发原则!(一)》

由于需求的变化,该系统可能需要增加新的数据源或者新的文件格式,每增加一个新的类型的数据源或者新的类型的文件格式,客户类MainClass都需要修改源代码,以便使用新的类,但违背了开闭原则。现使用依赖倒转原则对其进行重构。

《开发中我们需要遵循的几个开发原则!(一)》

当然根据具体的情况,也可以将AbstractSource注入到AbstractStransformer,依赖注入的方式有以下三种:

/** 
 * 依赖注入是依赖AbstractSource抽象注入的,而不是具体 
 * DatabaseSource 
 * 
 */  
abstract class AbstractStransformer {  
    private AbstractSource source;   
    /** 
     * 构造注入(Constructor Injection):通过构造函数注入实例变量。 
     */  
    public void AbstractStransformer(AbstractSource source){  
        this.source = source;           
    }  
    /**      
     * 设值注入(Setter Injection):通过Setter方法注入实例变量。 
     * @param source : the sourceto set        
     */       
    public void setSource(AbstractSource source) {            
        this.source = source;             
    }  
    /** 
     * 接口注入(Interface Injection):通过接口方法注入实例变量。 
     * @param source 
     */  
    public void transform(AbstractSource source ) {    
        source.getSource();  
        System.out.println("Stransforming ...");    
    }      
}

 

    原文作者:码蚁吃大象
    原文地址: https://blog.csdn.net/qq_38755561/article/details/82254469
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞