[Object-oriented] 相依性
前言 :
写程序的时候都会听到说,要降低程序之间的相依性。
程序之间的「相依性」,可以用下面简单的范例来理解。FunctionA里面使用了FunctionB,当FunctionB功能变更的时候,FunctionA就必须跟着做修改。这也就是说,「FunctionA相依FunctionB」。
1 2 3 4 5 6 7 8 9 | static void FunctionA() { FunctionB(); } static void FunctionB() { } |
以上面这个范例看起来,相依性不会是很大的问题,改就是了。但是当我们把问题放大,假设系统里有1000个Function。Function之间互相相依。当要更改1个Function内容并且维持整个系统正确运作,就必须要去检查其他999个Function是否需要跟着修改。这样的修改会是一场灾难,也是造成很多软件系统,改这边坏那边的主要原因。所以减少相依性,是提高程序代码质量很重要的一个环节。
在「结构化导向程序设计」Function与Function之间的相依性,可以透过追踪程序代码的方式来识别。但到了「面向对象程序设计」的环境下,虽然说也是可以使用追踪程序代码的方式,来识别对象之间的相依性。但却会受到继承、接口等等面向对象特性的影响,让识别对象之间相依性的这件工作变得非常复杂。
本篇使用UML类别图里的「关系」当作工具,阐述该如何透过UML类别图里的「关系」,来辅助开发人员识别面向对象程序的对象相依性。
说明 :
识别对象相依性一个简单的方法,就是「当两个类别A与B,分别存在不同的项目内。大幅修改了B之后,A如果需要跟着重新编译,那就是A相依B」。我们依照这个原则,来检视UML类别图里所定义的各种关系:
关联关系(Association)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | using Association.ProjectB; namespace Association.ProjectA { public class ClassA { private readonly ClassB _b; public ClassA(ClassB b) { _b = b; } public ClassB B { get { return _b; } } } } |
1 2 3 4 5 6 7 | namespace Association.ProjectB { public class ClassB { } } |
上图与范例程序是UML类别图里定义的关联关系,代表图形与范例程序。依照原则去检视程序代码可以看出,当变更ClassB的时候,ClassA需要跟着重新编译。所以ClassA相依于ClassB。
依赖关系(Dependency)
1 2 3 4 5 6 7 8 9 10 11 12 | using Dependency.ProjectB; namespace Dependency.ProjectA { public class ClassA { public void MethodXXX(ClassB b) { // TODO } } } |
1 2 3 4 5 6 7 | namespace Dependency.ProjectB { public class ClassB { } } |
上图与范例程序是UML里定义的依赖关系,代表图形与范例程序。依照原则去检视程序代码可以看出,当变更ClassB的时候,ClassA需要跟着重新编译。所以ClassA相依于ClassB。
概括关系(Generalization)
1 2 3 4 5 6 7 8 9 10 11 12 13 | using Generalization.ProjectB; namespace Generalization.ProjectA { public class ClassA : ClassB { public ClassA() : base () { } } } |
1 2 3 4 5 6 7 | namespace Generalization.ProjectB { public class ClassB { } } |
上图与范例程序是UML里定义的概括关系,代表图形与范例程序。依照原则去检视程序代码可以看出,当变更ClassB的时候,ClassA需要跟着重新编译。所以ClassA相依于ClassB。
实现关系(Realization)
1 2 3 4 5 6 7 8 9 | using Realization.ProjectB; namespace Realization.ProjectA { public class ClassA : IntefaceB { } } |
1 2 3 4 5 6 7 | namespace Realization.ProjectB { public interface IntefaceB { } } |
上图与范例程序是UML里定义的实现关系,代表图形与范例程序。依照原则去检视程序代码可以看出,当变更IntefaceB的时候,ClassA需要跟着重新编译。所以ClassA相依于IntefaceB。
聚合关系(Aggregate)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | using Aggregate.ProjectB; namespace Aggregate.ProjectA { public class ClassA { private readonly ItemB[] _itemBCollection; public ClassA() { _itemBCollection = new ItemB[2]; _itemBCollection[0] = new ItemB(); _itemBCollection[1] = new ItemB(); } public ItemB[] ItemBCollection { get { return _itemBCollection; } } } } |
1 2 3 4 5 6 7 | namespace Aggregate.ProjectB { public class ItemB { } } |
上图与范例程序是UML里定义的聚合关系,代表图形与范例程序。依照原则去检视程序代码可以看出,当变更ItemB的时候,ClassA需要跟着重新编译。所以ClassA相依于ItemB。
*聚合关系语意较为复杂,范例程序只是简单示意。
组成关系(Composition)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | using Composition.ProjectB; namespace Composition.ProjectA { public class ClassA { private readonly ItemB[] _itemBCollection; public ClassA(ItemB[] itemBCollection) { _itemBCollection = itemBCollection; } public ItemB[] ItemBCollection { get { return _itemBCollection; } } } } |
1 2 3 4 5 6 7 | namespace Composition.ProjectB { public class ItemB { } } |
上图与范例程序是UML里定义的组成关系,代表图形与范例程序。依照原则去检视程序代码可以看出,当变更ItemB的时候,ClassA需要跟着重新编译。所以ClassA相依于ItemB。
*组成关系语意较为复杂,范例程序只是简单示意。
后记 :
本篇的文章描述了,如何透过UML类别图里的「关系」,来辅助开发人员识别面向对象程序的对象相依性。当将对象依照职责分类成为独立Package的时候,对象之间的相依性也需要考虑进去,避免在Package之间有相依性杂乱的问题。当发现相依性杂乱时,则可以透过IoC、Facade等等手法来整理相依性。透过不断的整理相依性,就能慢慢提高程序代码的品质。
参考数据 :
期許自己~
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?