面向对象设计原则(一):单一职责原则(SRP)
单一职责原则(Single responsibility principle,SRP)是面向对象设计(OOD)中比较重要、常见的一种,下面来总结单一职责原则的知识点,包括:
1、什么是单一职责原则、什么是职责?
2、为什么需要遵守单一职责原则,违反单一职责有什么坏处?
3、单一职责原则需要注意什么?
4、单一职责原则的应用案例。
1、什么是单一职责原则(SRP)
1-1、原则的定义
单一职责原则(Single responsibility principle,SRP)可以表示为:
一个类应该仅有一个引起它变化的原因。
1-2、什么是职责
职责定义为:
变化的原因"(a reason for change)。
如果你能够想到多于一个的动机去改变类,那么这个类就具有多于一个的职责。
这条原则曾经被认为与内聚性(cohesion)相关。内聚性可以定义为:一个模块的组合元素之间的功能相关性。即对一个类来说,这个类是否只专心做一件事。
Robert Cecil Martin("Bob"大叔)把职责定义为变化的原因,更能表达出该原则的特性。
2、为什么需要单一职责原则
即单一职责原则能带来什么,可以从下面两个问题来描述。
2-1、为何要把这两个职责分离到单独的类中呢?
因为每个职责都是一个变化的原因。当需求变化时,该变化会反映到类的职责的变化。
如果一个类承担了多于一个的职责,那么引起它变化的原因就会有多个。
而单一职责原则正是反映了《代码大全》中所说的:软件的首要技术使命--管理复杂度,而找出容易变化改变的区域,隔离变化,就是一种很好的管理复杂度的启发方法。
2-2、违反单一职责原则有什么坏处?
如果一个类承担的职责过多,等于把这些职责耦合在了一起;一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。
这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。
3、单一职责原则需要注意什么
3-1、一个类实现多个接口的情况
一个类可能需要实现多个接口,这会产生职责耦合;
但多个接口就是一种解耦表现,一个接口相关职责的变化一般不会引起其他接口的变化。
后面我们会介绍到接口隔离原则(ISP):
不应该强迫客户依赖于它们不用的方法。
其中一种方法就是使用多重继承分离接口,即一个类实现了多个接口,分别提供给不同客户的接口只有该客户需要的操作。
更多信息可以参考:
3-2、不必分离两个总是同时变化的职责
如果应用程序的变化总是导致这两方面职责同时变化,那么就不必分离他们。
实际上,分离他们就会具有不必要的复杂性。
3-3、不要预先设计
仅当变化发生时,针对该变化的设计才具有实际意义。
如果没有前兆,那么应用SRP或者任何其他原则都是不明智的。
4、单一职责原则的应用
常见的容易变化的区域包括:业务规则、对硬件的依赖性、输入和输出、非标准的语言特性、困难的设计区域和构建区域。
例如:
通常,业务规则和对于持久化的控制这两个职责不应该混合在一起。
因为业务规则往往会频繁地变化,而持久化的方式却不会如此频繁地变化,并且变化的原因也是完全不同的。
这在平时开发中是很常见的分层应用,如用户管理的业务规则为增/删/改/查对应UserManageService接口:
public interface UserManageService { public ResultMsg create(Integer tokenId, String sign, User user); public ResultMsg delete(Integer tokenId, String sign, Integer userId); public ResultMsg update(Integer tokenId, String sign, User user); public ResultMsg get(Integer tokenId, String sign, Integer userId); }
而用户管理的持久化也为增/删/改/查对应UserDao接口:
public interface UserDao{ public boolean add(User user); public boolean delete( Integer userId); public boolean update(User user); public User get(Integer userId); }
实现UserManageService接口的UserManageServiceImpl类中处理完业务后,判断是否需要调用UserDao接口的相应实现,一些代码如下:
@Service("userManageService") public class UserManageServiceImpl implements UserManageService { @Resource(name="UserDaoImpl") private UserDao<User> userDao; …… @Override public ResultMsg create(Integer tokenId, String sign, User user) { ….. //处理完业务后,判断是否需要调用UserDao接口的相应实现 userDao.add(user); …… } ….. }
另外,一般应该尽早分离两个职责,后期才发现的可以考虑使用FACADE(外观)、PROXY(代理)模式对设计进行重构,分离两个职责。
5、总结
单一职责原则:
一个类应该仅有一个引起它变化的原因。
即:
管理复杂度,找出容易变化/改变的区域,隔离变化。
单一职责原则是最简单的原则之一,也是最难正确运用的原则之一,因为我们很容易把职责结合一起。
软件设计很重要的工作就是发现职责并把那些职责相互分离,我们平时或多或少的都使用到单一职责原则,如果开发中能关注到就更好了。
更多SRP的信息请参考:
到这里,我们对单一职责原则有了一个大体的了解,还需要平时开发中多注意,后面我们将了解其他的面向对象设计原则......
【参考资料】
1、《敏捷软件开发:原则、模式与实践》第8章 单一职责原则(SRP)
2、维基百科"Single responsibility principle":https://en.wikipedia.org/wiki/Single_responsibility_principle
3、《代码大全》第二版 第5章 软件构件中的设计
4、《大话设计模式》第3章