内聚、耦合

内聚

image-20250309154350970

内聚的种类(从低到高)

内聚性(Cohesion)衡量模块内部各部分之间的关联程度,高内聚是优秀设计的核心原则。以下是7种内聚类型(按质量从低到高排序),附代码示例说明:


1. 偶然内聚(Coincidental Cohesion)

特点:模块内代码无逻辑关联,仅因巧合被放在一起。
示例:一个工具类包含随机方法:

class Utils {
    public static void printDate() { ... }  // 打印日期
    public static void encryptData() { ... } // 加密数据
    public static void connectDB() { ... }   // 连接数据库
}

问题:方法之间毫无关系,难以维护。


2. 逻辑内聚(Logical Cohesion)

特点:代码通过参数控制执行不同逻辑,但操作无本质联系。
示例:用一个函数处理多种文件操作:

def file_operation(operation, filename):
    if operation == "read":
        return read_file(filename)
    elif operation == "delete":
        return delete_file(filename)  # 删除和读取逻辑无关

问题:调用者需知道内部逻辑,新增操作需修改函数。


3. 时间内聚(Temporal Cohesion)

特点:代码因同一时间段执行被放在一起,但无功能关联。
示例:程序初始化时调用的函数:

void startup() {
    init_logger();  // 初始化日志
    load_config();   // 加载配置
    clear_cache();   // 清空缓存
}

问题:虽在启动时调用,但各函数职责分离更合理。


4. 过程内聚(Procedural Cohesion)

特点:代码按特定流程顺序执行,但无数据传递。
示例:用户注册流程:

function registerUser() {
    validateInput();  // 验证输入
    createUser();     // 创建用户
    sendWelcomeEmail(); // 发邮件(依赖前两步成功)
}

改进方向:可拆分为更小单元(如验证服务、邮件服务)。


5. 通信内聚(Communicational Cohesion)

特点:代码操作相同数据,但无严格功能联系。
示例:处理用户数据的函数:

class UserData {
    public void updateProfile(User user) { ... }  // 更新资料
    public void logActivity(User user) { ... }   // 记录活动
}

优点:比前几种内聚性更高,但仍可细分(如日志模块独立)。


6. 顺序内聚(Sequential Cohesion)

特点:代码前一步输出是后一步输入,形成流水线。
示例:数据处理流水线:

def process_data(data):
    cleaned = clean_data(data)     # 清洗数据
    transformed = transform(cleaned)  # 转换格式
    save_to_db(transformed)        # 存储结果

优点:逻辑清晰,符合单一职责原则。


7. 功能内聚(Functional Cohesion)★ 理想状态

特点:模块仅完成单一功能,所有代码紧密相关。
示例:计算圆的面积:

class Circle {
    private double radius;
    public double calculateArea() {
        return Math.PI * radius * radius;  // 只做一件事
    }
}

优点:高复用性、低耦合、易测试维护。


总结对比表

内聚类型 关键特征 质量 示例
偶然内聚 代码随机拼凑 最差 工具类包含无关方法
逻辑内聚 通过参数分支执行不同逻辑 多合一文件操作函数
时间内聚 同一时间段执行但无功能关联 较低 程序初始化函数
过程内聚 按流程顺序执行但无数据传递 中等 用户注册流程
通信内聚 操作相同数据但功能独立 良好 用户数据更新和日志记录
顺序内聚 前一步输出是后一步输入 优秀 数据清洗→转换→存储流水线
功能内聚 仅完成单一功能 最优 计算圆的面积

耦合


无数的鸡控制在外公的容器里(无、数、记、控制、外、公、内容)

耦合

耦合是指软件模块之间相互依赖的程度,耦合度越低,系统越容易维护和修改。以下是常见的耦合类型及Java代码示例:

1. 无耦合/无依赖 (No Coupling)

两个模块之间没有任何依赖关系。

// Module A
public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
}

// Module B
public class Logger {
    public void log(String message) {
        System.out.println(message);
    }
}

2. 数据耦合 (Data Coupling)

模块之间通过参数传递基本数据类型进行通信。

public class OrderProcessor {
    public void processOrder(String orderId, double amount) {
        // 处理订单逻辑
    }
}

public class Main {
    public static void main(String[] args) {
        OrderProcessor processor = new OrderProcessor();
        processor.processOrder("ORD123", 99.99);
    }
}

3. 标记耦合 (Stamp Coupling)

模块之间通过传递复合数据结构(如对象)进行通信,但只使用其中部分数据。

public class Customer {
    private String name;
    private String address;
    private String phone;
    // getters and setters
}

public class EmailService {
    public void sendWelcomeEmail(Customer customer) {
        // 只使用customer的name和email
        String emailContent = "Dear " + customer.getName() + ", welcome!";
        // 发送邮件...
    }
}

4. 控制耦合 (Control Coupling)

一个模块通过传递控制信息(如标志)来影响另一个模块的内部逻辑。

public class ReportGenerator {
    public void generateReport(boolean isDetailed) {
        if (isDetailed) {
            generateDetailedReport();
        } else {
            generateSummaryReport();
        }
    }
    // 其他方法...
}

public class Main {
    public static void main(String[] args) {
        ReportGenerator generator = new ReportGenerator();
        generator.generateReport(true); // 控制生成详细报告
    }
}

5. 外部耦合 (External Coupling)

模块依赖于外部系统或工具的接口。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DatabaseAccess {
    public Connection getConnection() throws SQLException {
        return DriverManager.getConnection(
            "jdbc:mysql://localhost:3306/mydb", "user", "password");
    }
}

6. 公共耦合 (Common Coupling)

多个模块共享同一个全局数据。

public class GlobalConfig {
    public static String DATABASE_URL = "jdbc:mysql://localhost:3306/mydb";
    public static int TIMEOUT = 30;
}

public class OrderService {
    public void processOrder() {
        String url = GlobalConfig.DATABASE_URL;
        // 使用全局配置
    }
}

public class UserService {
    public void getUser() {
        String url = GlobalConfig.DATABASE_URL;
        // 使用全局配置
    }
}

7. 内容耦合 (Content Coupling)

一个模块直接修改或依赖另一个模块的内部数据或实现细节。

public class BankAccount {
    public double balance; // 不应该公开字段
    
    // 其他方法...
}

public class AccountManager {
    public void manipulateAccount(BankAccount account) {
        account.balance += 1000; // 直接修改另一个类的内部字段
    }
}

最佳实践

在Java开发中,应该:

  1. 尽量使用接口耦合而非实现耦合
  2. 遵循依赖倒置原则(DIP)
  3. 使用依赖注入(DI)来降低耦合
// 接口耦合示例
public interface MessageService {
    void sendMessage(String message);
}

public class EmailService implements MessageService {
    @Override
    public void sendMessage(String message) {
        // 发送邮件实现
    }
}

public class NotificationService {
    private MessageService messageService;
    
    // 依赖注入
    public NotificationService(MessageService messageService) {
        this.messageService = messageService;
    }
    
    public void notifyUser(String message) {
        messageService.sendMessage(message);
    }
}

低耦合的设计可以使系统更易于维护、测试和扩展。

posted @ 2025-04-14 20:40  deyang  阅读(335)  评论(0)    收藏  举报