如何利用迪米特法则实现“高内聚、低耦合”?(二)
3.2 有依赖关系的类之间,尽量只依赖必要的接口。
Serialization类负责对象的序列化和反序列化:
单看类的设计,没问题。但若放到特定应用场景,假设项目中的有些类只用到序列化操作,而另一些类只用到反序列化。那么,基于 有依赖关系的类之间,尽量只依赖必要的接口:
- 只用到序列化操作的那部分类不应依赖反序列化接口
- 只用到反序列化操作的那部分类不应依赖序列化接口
据此,应将Serialization类
方案一:拆分为两个更小粒度的类
- 只负责序列化(Serializer类)
- 只负责反序列化(Deserializer类)
尽管拆分后的代码更能满足迪米特法则,但却违背高内聚。高内聚要求相近功能放到同一类中,方便功能修改时,修改的地方不太散乱。
针对本案例,若业务要求修改序列化实现方式,从JSON换成XML,则反序列化实现逻辑也要一起改。未拆分前,只需修改一个类,拆分后,却要修改两个类!
既不想违背高内聚,也不想违背迪米特法则,怎么办?
方案二:引入两个接口
尽管还是要往Demo1的构造器传入包含序列化和反序列化的Serialization实现类,但依赖的Serializable接口只包含序列化操作,Demo1无法使用Serialization类中的反序列化接口,对反序列化操作无感知,符合迪米特法则的“依赖有限接口”。
也体现了“面向接口编程”,结合迪米特法则,可总结出:“基于最小接口,而非最大实现来编程”。
多想一点点
本案例的重构方案,整个类只包含序列化、反序列化俩操作,只用到序列化操作的使用者,即便能够感知到仅有的一个反序列化方法,问题也不大。为满足迪米特法则,将一个简单的类,拆出两个接口,是过度设计吗?设计原则本身无对错,只有能否用对之说。不要为了用设计原则而用,应该具体问题具体分析。
Serialization类只包含两个操作,确实没啥必要拆成俩接口。但若对Serialization类添加更多功能,实现更多更好用的序列化、反序列化方法,重新考虑该问题:
这种场景下,方案二设计更好。因为本案例的应用场景,大部分代码只用到序列化功能,这些用户无需了解反序列化,而修改后的Serialization类,反序列化的“知识”,从一个方法变成三个。一旦任一反序列化操作有代码改动,都需要检查、测试所有依赖Serialization类的代码是否还能正常工作。
为减少耦合和降低测试的工作量,应按迪米特法则,隔离反序列化和序列化的功能。
4 总结
4.1 高内聚、低耦合
能有效提高代码的可读性和可维护性,缩小功能改动导致的代码改动范围:
- 高内聚,指导类本身的设计
就是指相近的功能应该放到同一个类中,不相近的功能不要放到同一类中。相近的功能往往会被同时修改,放到同一个类中,修改会比较集中
- 低耦合,指导类与类之间依赖关系的设计
在代码中,类与类之间的依赖关系简单清晰。即使两个类有依赖关系,一个类的代码改动也不会或者很少导致依赖类的代码改动。
4.2 迪米特法则
不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口。迪米特法则希望减少类之间的耦合,让类越独立越好。每个类都应该少了解系统的其他部分。一旦发生变化,需要了解这一变化的类就会比较少。
参考:
[1]. 《重构》
[2]. 《代码设计之丑》
文章转自公众号: JavaEdge