内聚和耦合基础
在软件设计中有两个重要的概念:耦合(coupling)和内聚(cohesion)。这些概念能帮助你更好地理解代码,以及你的操作如何影响代码。它们定义了类之间和类内部的关系。理解它们能帮助你建立更合理的代码结构,使代码更灵活、更有目的性。
耦合(Coupling)
耦合是指软件组件之间相互依赖的程度。耦合大致分为两种:紧耦合(tight coupling)和松耦合(loose coupling)。
-
松耦合意味着类与类之间依赖性较低。
-
紧耦合则相反,一个类强烈依赖另一个类,并且对它的方法了解过多。
在紧耦合中,如果一个类发生变化,另一个类也不得不随之修改。而在松耦合中,一个类对另一个类的对象创建和方法所知甚少,因此类与类之间的依赖性更低。
最简单的降低耦合度的方法是使用接口。除此之外,**依赖注入(dependency injection)**也是降低耦合的常见方式。
紧耦合示例
假设你正在制造一个机器人。机器人的核心部件是一个引擎(Engine),而且这个引擎很难被替换。如果你想换一个新的引擎,就必须拆解整个机器人并修改它。这就是紧耦合。
class Engine {method run() { ... }
}class Robot {Engine eng = new Engine();eng.run();
}
解释
这里 Robot
类依赖于 Engine
,并且需要自己去创建一个 Engine
实例。这样一来,依赖类(Robot)对被依赖类(Engine)的实现知道太多,导致耦合过紧。任何对 Engine
的修改都可能迫使你修改 Robot
。一旦 Engine
出现问题,整个机器人都会失效。
松耦合示例
再来看松耦合的例子。假设你可以给机器人设置默认的涂装颜色,比如银色或金色。无论是哪种颜色,你都可以在不修改机器人本身的情况下更换。这就是松耦合。
interface Paint {method paint();
}class Silver implements Paint {method paint() {print("silver");}
}class Gold implements Paint {method paint() {print("gold");}
}class Robot {Paint col = new Gold();col.paint();
}
解释
Robot
类通过 Paint
接口来使用不同的涂装类。在这里,你可以通过接口来注入不同的涂装实现。这样 Robot
类对 Silver
和 Gold
的实现细节知之甚少,它只知道 paint()
方法的存在。如果其中一个实现不可用,你可以很容易替换为另一个实现。
内聚(Cohesion)
内聚是对类功能的度量,衡量的是一个类内部各个职责之间的相关性。
-
低内聚:一个类试图承担过多不相关的任务,导致难以维护和理解。
-
高内聚:一个类只负责单一任务,或者一组相关任务,使代码更容易理解和维护。
举个例子,你正在开发一个机器人制造工厂的应用。如果你创建了一个 RobotFactory
类,并让它既负责制造机器人,又负责工厂物流,还要管理员工,这就是低内聚。
class RobotFactory {method createRobots() { ... }method maintainLogistics() { ... }method manage() { ... }
}
解释
这是一个低内聚的例子,因为一个类承担了多种不同的职责。这样的类难以复用和测试。
如果你将不同职责分散到不同类中,就能提高内聚性:
class BuildDepart {method createRobots() { ... }
}class Logistics {method maintainLogistics() { ... }
}class Management {method manage() { ... }
}
解释
现在,每个类只负责一种职责,类之间更清晰,内聚性更高。
耦合与内聚的结合
下图展示了耦合与内聚不同组合下的可能结果(2×2 矩阵):
-
低耦合 + 高内聚 → 理想状态
-
高耦合 + 低内聚 → 上帝对象(God Object):一个对象包揽所有功能
-
高耦合 + 高内聚 → 边界划分不当(Poorly selected boundaries):模块划分有边界,但类之间依赖过多
-
低耦合 + 低内聚 → 破坏性解耦(Destructive decoupling):虽然耦合很低,但代码缺乏重点
理想情况下,你的代码应当追求 低耦合、高内聚。
总结
-
尽量实现松耦合。松耦合能让代码的不同部分彼此独立,减少相互影响。实现方式包括接口和依赖注入。
-
保持高内聚。高内聚让类更专注于单一职责,使其更易于复用和测试。
-
理想状态是低耦合 + 高内聚。这样代码会更清晰、可读、易于维护和扩展。