链接:
理解“偶然内聚”
理解“逻辑内聚”
理解“时间内聚”
理解“过程内聚”
理解“通信内聚”
理解“顺序内聚”
理解“功能内聚”
过程内聚是指在一个模块中,各个功能按照某种过程或步骤执行,即它们一起完成一系列相关的操作。这意味着模块内部的每个操作都有一定的顺序性,并且这些操作通常是为了完成一个特定的任务。尽管过程内聚比偶然内聚(Coincidental Cohesion)要好一些,但它仍然不是理想的内聚形式。这是因为模块内部的功能虽然有一定的执行顺序上的联系,但这些功能并不总是紧密相关。
过程内聚的特点:
- 基于过程:模块中的功能按照一定的顺序执行,通常是为了完成一个特定的任务。
- 部分相关性:模块中的功能具有一定的顺序依赖关系,但它们之间并没有直接的逻辑依赖。
- 较低的内聚度:由于模块内的功能并不是完全相关的,导致其内聚度相对较低。
过程内聚的问题
- 低可读性:由于模块内部的功能并不是完全相关的,代码的可读性和可理解性较差,开发人员需要仔细阅读和理解模块中每个操作的目的以及它们之间的顺序关系。
- 高维护成本:当需要修改某个功能时,由于模块内部的功能不完全相关,开发人员可能需要检查整个模块以确保不会引入不必要的副作用。
- 脆弱性:由于模块内部的功能不完全相关,任何对模块的修改都可能导致其他不相关的部分出现问题,增加了系统的脆弱性。
- 难以重用:由于模块内部的功能不完全相关,很难将模块中的某个功能单独提取出来进行重用。
举例说明过程内聚
假设我们正在开发一个用户注册系统,其中有一个模块 UserRegistration 被设计用来处理用户的注册流程,例如验证用户输入、保存用户信息到数据库并发送确认邮件。这个模块的所有功能都需要按特定顺序执行来完成整个注册流程。
public class UserRegistration
{
public void registerUser(String username, String email)
{
if (validateInput(username, email))
{
saveUserToDatabase(username, email);
sendConfirmationEmail(email);
}
else
{
System.out.println("Invalid input.");
}
}
private boolean validateInput(String username, String email)
{
// 假设这里有一些验证用户输入的逻辑
return !username.isEmpty() && email.contains("@");
}
private void saveUserToDatabase(String username, String email)
{
// 假设这里有一些保存用户信息到数据库的逻辑
System.out.println("Saving user: " + username + ", " + email);
}
private void sendConfirmationEmail(String email)
{
// 假设这里有一些发送确认邮件的逻辑
System.out.println("Sending confirmation email to: " + email);
}
}
在这个例子中,UserRegistration 类包含了一个 registerUser 方法,该方法依次调用了三个不同的操作:验证用户输入、保存用户信息到数据库以及发送确认邮件。尽管这些操作都是为了完成用户注册这一任务,但它们之间并没有直接的逻辑依赖关系,只是因为它们需要按照一定顺序执行而被组合在一起。
这就是典型的过程内聚,因为模块内部的功能是基于它们需要按顺序执行来完成一个特定任务而组合在一起的。
改进方案:提高模块的内聚性
为了提高模块的内聚性,可以采用以下几种策略:
- 分离功能:将不相关的功能拆分到不同的模块中,使每个模块专注于特定的任务。
- 封装相关功能:将相关的功能封装在一起,形成高内聚的模块。
- 使用面向对象设计原则:利用面向对象的设计原则,如单一职责原则,来优化模块的设计。
改进后的示例代码:
我们可以将上述不相关的注册操作拆分成独立的模块,并利用接口和面向对象设计原则来实现不同的操作。
// 定义用户注册操作接口
public interface RegistrationTask {
void execute();
}
// 验证用户输入操作实现类
public class ValidateInputTask implements RegistrationTask {
private final String username;
private final String email;
public ValidateInputTask(String username, String email) {
this.username = username;
this.email = email;
}
@Override
public void execute() {
if (!username.isEmpty() && email.contains("@")) {
System.out.println("Input is valid.");
} else {
throw new IllegalArgumentException("Invalid input.");
}
}
}
// 保存用户信息到数据库操作实现类
public class SaveUserTask implements RegistrationTask {
private final String username;
private final String email;
public SaveUserTask(String username, String email) {
this.username = username;
this.email = email;
}
@Override
public void execute() {
// 假设这里有一些保存用户信息到数据库的逻辑
System.out.println("Saving user: " + username + ", " + email);
}
}
// 发送确认邮件操作实现类
public class SendConfirmationEmailTask implements RegistrationTask {
private final String email;
public SendConfirmationEmailTask(String email) {
this.email = email;
}
@Override
public void execute() {
// 假设这里有一些发送确认邮件的逻辑
System.out.println("Sending confirmation email to: " + email);
}
}
// 用户注册器类,负责根据任务列表执行注册操作
public class UserRegistration {
private final List<RegistrationTask> tasks;
public UserRegistration(List<RegistrationTask> tasks) {
this.tasks = tasks;
}
public void registerUser() {
for (RegistrationTask task : tasks) {
task.execute();
}
}
}
改进后的分析:
分离功能:
- 将不相关的注册操作(验证用户输入、保存用户信息到数据库、发送确认邮件)拆分到不同的类中,每个类只负责一个明确的任务。这符合单一职责原则,使得每个类更加专注和清晰。
封装相关功能:
- 使用接口 RegistrationTask 来封装所有注册操作的共同行为(即 execute 方法),并通过具体的实现类(如 ValidateInputTask、SaveUserTask 和 SendConfirmationEmailTask)来实现不同的注册操作。这种方式提高了模块的内聚性,使得每个类的功能更加紧密相关。
提高可读性和可维护性:
- 通过将不相关的功能拆分到不同的类中,代码的可读性和可维护性得到了显著提升。开发人员可以更容易地理解和修改每个类的功能。
降低脆弱性:
- 由于每个类只负责一个明确的任务,修改其中一个类不会影响到其他类,降低了系统的脆弱性。
便于重用:
- 现在每个类都可以独立使用,便于在其他地方重用。例如,ValidateInputTask 可以在其他需要验证用户输入的地方直接使用,而不需要依赖于其他不相关的功能。