设计模式六大原则—— 单一职责原则(SRP,Single Responsibility Principle)

前端之家收集整理的这篇文章主要介绍了设计模式六大原则—— 单一职责原则(SRP,Single Responsibility Principle)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

转自 http://baike.baidu.com/item/%E5%8D%95%E4%B8%80%E8%81%8C%E8%B4%A3%E5%8E%9F%E5%88%99
转自 http://www.jb51.cc/article/p-obarfzju-bme.html

简介

单一职责原则(SRP,Single responsibility principle)又称单一功能原则,面向对象五个基本原则(SOLID)之一。它规定一个类应该只有一个发生变化的原因。该原则由罗伯特·C·马丁(Robert C. Martin)于《敏捷软件开发:原则、模式和实践》一书中给出的。马丁表示此原则是基于汤姆·狄马克(Tom DeMarco)和Meilir Page-Jones的著作中的内聚性原则发展出的。

所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。如果一个类有一个以上的职责,这些职责就耦合在了一起。这会导致脆弱的设计。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。例如:要实现逻辑和界面的分离。而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。

原理

如果一个类承担的职责过多,就等于把这些职责耦合在一起了。一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当发生变化时,设计会遭受到意想不到的破坏。而如果想要避免这种现象的发生,就要尽可能的遵守单一职责原则。此原则的核心就是解耦和增强内聚性。

问题由来

之所以会出现单一职责原则就是因为在软件设计时会出现以下类似场景:

T本来只负责一个职责P,但由于职责扩散,就是因为某种原因,职责P被分化为粒度更细的职责P1和P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。也就是说职责P1和P2被耦合在了一起。

解决办法

遵守单一职责原则,将当将不同的职责封装到不同的类或模块中。但是在程序已经写好的情况下,这样做简直太费时间了。所以,简单的修改类T,用它来负责两个职责是一个比较不错的选择,虽然这样做有悖于单一职责原则。(这样做的风险在于职责扩散的不确定性,因为我们不会想到这个职责P,在未来可能会扩散为P1,P2,P3,P4……Pn。所以记住,在职责扩散到我们无法控制的程度之前,立刻对代码进行重构。)

举例说明

用一个类描述动物呼吸这个场景:

class Animal{  
    public void breathe(String animal){  
        System.out.println(animal+"呼吸空气");  
    }  
}  
public class Client{  
    public static void main(String[] args){  
        Animal animal = new Animal();  
        animal.breathe("牛");  
        animal.breathe("羊");  
        animal.breathe("猪");  
    }  
}

运行结果:

牛呼吸空气
羊呼吸空气
猪呼吸空气

程序上线后,发现问题了,并不是所有的动物都呼吸空气的,比如鱼就是呼吸水的。

遵循单一职责原则的修改方案

修改时如果遵循单一职责原则,需要将Animal类细分为陆生动物类Terrestrial,水生动物Aquatic,代码如下:

class Terrestrial{  
    public void breathe(String animal){  
        System.out.println(animal+"呼吸空气");  
    }  
}  
class Aquatic{  
    public void breathe(String animal){  
        System.out.println(animal+"呼吸水");  
    }  
}  

public class Client{  
    public static void main(String[] args){  
        Terrestrial terrestrial = new Terrestrial();  
        terrestrial.breathe("牛");  
        terrestrial.breathe("羊");  
        terrestrial.breathe("猪");  

        Aquatic aquatic = new Aquatic();  
        aquatic.breathe("鱼");  
    }  
}

运行结果:

牛呼吸空气
羊呼吸空气
猪呼吸空气
鱼呼吸水

我们会发现如果这样修改花销是很大的,除了将原来的类分解之外,还需要修改客户端。

违背了单一职责原则,但改动最小的方案

而直接修改类Animal来达成目的虽然违背了单一职责原则,但花销却小的多,代码如下:

class Animal{  
    public void breathe(String animal){  
        if("鱼".equals(animal)){  
            System.out.println(animal+"呼吸水");  
        }else{  
            System.out.println(animal+"呼吸空气");  
        }  
    }  
}  

public class Client{  
    public static void main(String[] args){  
        Animal animal = new Animal();  
        animal.breathe("牛");  
        animal.breathe("羊");  
        animal.breathe("猪");  
        animal.breathe("鱼");  
    }  
}

可以看到,这种修改方式要简单的多。但是却存在着隐患:有一天需要将鱼分为呼吸淡水的鱼和呼吸海水的鱼,则又需要修改Animal类的breathe方法,而对原有代码修改会对调用“猪”“牛”“羊”等相关功能带来风险,也许某一天你会发现程序运行的结果变为“牛呼吸水”了。这种修改方式直接在代码级别上违背了单一职责原则,虽然修改起来最简单,但隐患却是最大的。还有一种修改方式:

在类级别上违背单一职责,但方法级别上却是符合的方案

class Animal{  
    public void breathe(String animal){  
        System.out.println(animal+"呼吸空气");  
    }  

    public void breathe2(String animal){  
        System.out.println(animal+"呼吸水");  
    }  
}  

public class Client{  
    public static void main(String[] args){  
        Animal animal = new Animal();  
        animal.breathe("牛");  
        animal.breathe("羊");  
        animal.breathe("猪");  
        animal.breathe2("鱼");  
    }  
}

可以看到,这种修改方式没有改动原来的方法,而是在类中新加了一个方法,这样虽然也违背了单一职责原则,但在方法级别上却是符合单一职责原则的,因为它并没有动原来方法代码。这三种方式各有优缺点,那么在实际编程中,采用哪一中呢?其实这真的比较难说,需要根据实际情况来确定。我的原则是:只有逻辑足够简单,才可以在代码级别上违反单一职责原则;只有类中方法数量足够少,才可以在方法级别上违反单一职责原则。

例如本文所举的这个例子,它太简单了,它只有一个方法,所以,无论是在代码级别上违反单一职责原则,还是在方法级别上违反,都不会造成太大的影响。实际应用中的类都要复杂的多,一旦发生职责扩散而需要修改类时,除非这个类本身非常简单,否则还是遵循单一职责原则的好。

遵循单一职责原的优点

  • 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
  • 提高类的可读性,提高系统的可维护性;
  • 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。

需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。

相关知识

单一职责原则并不是一个孤立的面向对象设计原则,它是面向对象设计五个基本原则(SOLID)之一。这些原则是:单一职责原则、开闭原则、接口隔离原则、里氏替换原则和依赖倒置原则。这些原则被一起应用时可以使一个软件系统更易被维护和扩展。这些原则被典型的应用在测试驱动开发上,并且是敏捷开发以及自适应软件开发等指导思想的重要组成部分。

猜你在找的设计模式相关文章