我正在努力理解单一责任原则,但我很难掌握这个概念。我正在阅读“Lucian-Paul Torje的Java设计模式和最佳实践; Adrian Ianculescu; Kamalmeet Singh。”
在本书中,我正在阅读单一责任原则章节,其中有一个汽车类,如下所示:
他们说Car拥有Car逻辑和数据库操作。将来,如果我们想要更改数据库,那么我们需要更改数据库逻辑,并且可能还需要更改汽车逻辑。反之亦然......
解决方案是创建两个类,如下所示:
我的问题是,即使我们创建了两个类,让我们考虑我们在CAR类中添加一个名为'price'的新属性[或者将属性'model'更改为'carModel']然后你认为我们还不需要更新CarDAO类就像改变SQL一样。
那么SRP在这里有什么用?
好问题。
首先,请记住,这是本书中的一个简单示例。读者可以稍微扩展一下,想象一下更复杂的场景。在所有这些场景中,进一步想象您不是团队中唯一的开发人员;相反,您正在一个大型团队中工作,开发人员之间的沟通通常采用协商类接口的形式,即API,公共方法,公共属性,数据库模式。此外,您经常需要担心回滚,向后兼容性以及同步版本和部署。
例如,假设您要将数据库换出,例如,从MySQL换到PostgreSQL。使用SRP,您将重新实现CarDAO
,更改所使用的任何特定于方言的SQL,并保持Car
逻辑不变。但是,您可能需要进行一些小的更改(可能在配置中),以告知Car使用新的PostgreSQL DAO。一个合理的DI框架会使这个变得简单。
在另一个示例中,假设您要将CarDAO委托给另一个开发人员以与memcached集成,以便读取尽管最终一致,但速度很快。同样,这个开发人员不需要了解Car
中的业务逻辑。相反,它们只需要在CarDAO
的CRUD方法之后运行,并且可能在CarDAO
API中声明一些具有不同一致性保证的方法。
假设,在另一个示例中,您的团队雇用了一名专门从事合规法的数据库工程师。为了准备即将到来的IPO,数据库工程师的任务是保存公司35个数据库中所有表的所有更改的审计日志。使用SRP,我们的无畏DBA不必担心使用任何表的任何业务逻辑;相反,他们的变异跟踪魔法可以使用装饰器或其他方面编程技术巧妙地注入到全部DAO中。 (顺便说一句,这也可以在SQL接口的另一端完成。)
最后一个 - 现在假设系统工程师被带到团队中,并且负责在AWS中的多个区域(数据中心)中分割这些数据。该工程师可以进一步采用SRP并添加一个组件,其唯一的作用是告诉我们每个ID,每个实体的归属区域。每次我们进行跨区域读取时,新组件都会碰到一个计数器;每周,一个自动化工具将频繁读取的数据迁移到新的家庭区域,以减少延迟。
现在,让我们进一步发挥想象力,并假设业务蓬勃发展 - 突然之间,您正在为一家财富500强公司工作,该公司的多个部门跨越多个国家。财务部的业务分析师希望利用您的表格在其首次公开募股后的投资者报告中描绘汽车销售的季度增长。而不是让他们访问Car
(因为用于报告的逻辑可能与用于准备在Web UI上呈现的数据的逻辑不同),您可以为CarDAO
创建一个只读接口,其中包含一个简短的列表精心策划的公共属性,您现在必须跨部门边界维护。上帝禁止你重命名其中一个属性:准备好3个月的日落计划和许多悲伤的仪表板和深夜升级。 (并且请不要让他们直接访问实际的SQL表,因为隐含的假设是整个表都是公共接口。)哎呀,我的伤疤可能正在显示。
一个必然结果是,如果你需要更改Car
中的业务逻辑(比如,在尴尬的召回之后添加一种计算每个特斯拉的较低销售价格的方法),你就不会触及CarDAO
,因为if car.brand == 'Tesla; price = price * 0.6
无关有数据访问权限。
补充阅读:CQRS
要添加新属性,只有在应将该属性保存到数据库时才需要更改这两个类。如果它是业务逻辑中使用的属性,那么您不需要更改DAO。此外,如果您将数据库从一个供应商更改为另一个供应商或从SQL更改为NoSQL,则必须仅在DAO类中进行更改。如果您需要更改某些业务逻辑,那么您只需要更改Car
类。
罗伯特C.马丁所说的Single responsibility principle意味着
一堂课应该只有一个改变的理由。
牢记这一原则通常会导致更小,更具凝聚力的类,这反过来意味着更少的人需要同时处理这些类,并且代码变得更加健壮。
在您的示例中,保持数据访问和业务逻辑(价格计算)逻辑分离意味着您在进行更改时不太可能破坏另一个。