面向对象设计中的单一职责原则
面向对象设计中的单一职责原则一个优良的系统设计,强调模块间保持低耦合、高内聚的关系,在面向对象设计中这条规则同样适用,所以面向对象的第一个设计原则就是:单一职责原则(SRP,Single Responsibility Principle)。如果一个类承担的职责过多,就等于把这些职责耦合在了一起。当其中一个职责变化时,可能影响其他职责的运作。
单一职责,强调的是职责的分离,在某种程度上对职责的理解,构成了不同类之间耦合关系的设计关键,因此单一职责原则或多或少成为设计过程中一个必须考虑的基础性原则。
在软件编程中,谁也不希望因为修改了一个功能导致其他的功能发生故障。而避免出现这一问题的方法便是遵循单一职责原则。虽然单一职责原则如此简单,并且被认为是常识,但是即便是经验丰富的程序员写出的程序,也会有违背这一原则的代码存在。为什么会出现这种现象呢?因为有职责扩散。所谓职责扩散,就是因为某种原因,职责P被分化为粒度更细的职责P1和P2。
一、遵循单一职责原的优点有:
1、可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
2、提高类的可读性,提高系统的可维护性;
3、变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
4、需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。
二、关于单一职责原则,我们的建议是:
1、一个类只有一个引起它变化的原因,否则就应当考虑重构。
2、 SRP由引起变化的原因决定,而不由功能职责决定。虽然职责常常是引起变化的曲线,但是有时却未必,应该审时度势。
3、 测试驱动开发,有助于实现合理分离功能的设计。
4、 可以通过Facade模式或Proxy模式进行职责分离。
5、我们会自然地把职责结合在一起。如果能想到多于一个动机去改变一个类,那么这个类就具有多于一个的职责,就应该考虑类的职责分离。
6、接口一定做到单一职责,类的设计尽量做到只有一个原因引起变化。
7、分层架构模式实际上也体现了这一原则,它将整个系统按照职责的内聚性分为不同的层,层内的模块与类具有宏观的内聚性,它们关注的事情应该是一致的。例如,领域逻辑层就主要关注系统的业务逻辑与业务流程,而数据的持久化与访问则交由数据访问层来负责。
三、以订单的管理为例,我们在领域逻辑层中定义如下的类OrderManager
public class OrderManager { private IOrderRepository repository = RepositoryFactory.CreateOrderRepository(); public void Place(Order order) { if (order.IsValid()) { repository.Add(Order); } else { throw new InvalidOperationException("Order can't be placed. "); } } public void Cancel(Order order) { if (order.IsValid() && order.CanCancel(DateTime.Now)) { repository.Remove(Order); } else { throw new InvalidOperationException("Order can't be canceled. "); } } } public static class RepositoryFactory { public static IOrderRepository CreateOrderRepository() { return new OrderRepository(); } }
OrderManager类的实现体现了单一职责原则的思想。首先,OrderManager类中的Place()和Cancel()方法均属于订单管理的业务逻辑,与领域逻辑层关注的事情是一致的。在这两个方法的实现中,我们需要检验订单的正确性(检验订单是否包含了必要的信息,如联系人、联系地址与联系电话),以及判断当前时间是否在允许取消订单的时间范围内。虽然它们仍然属于订单处理的业务逻辑,但拥有这些检查信息的是Order对象,而不是OrderManager,即Order对象是检查订单的信息专家 。因此,IsValid()和CanCancel()方法应该被定义在Order类中。至于添加和移除订单的操作,虽然保证了下订单和取消订单的业务逻辑实现,但其实现却属于数据访问层的范畴,因而该职责被委派给了OrderRepository类 。至于RepositoryFactory类,则是负责创建OrderRepository对象的工厂类。
这些类的职责以及协作关系如图所示:
将数据访问的逻辑从领域对象中分离出去是有道理的,因为数据访问逻辑的变化方向与订单业务逻辑的变化方向是不一致的,引起职责发生变化的原因也不相同。这也是单一职责原则的核心思想。遵循该原则,就能够有效地分离对象的变与不变,将变化的职责以抽象的方式独立于原对象之外,原对象就更加稳定。我们对访问Order数据表的逻辑进行了封装与抽象,以隔离数据访问逻辑的变化,即使数据访问逻辑发生变化,它影响到的只是OrderRepository类而已。