004-封装性原理与实现

004-封装性原理与实现

🟡 难度: 中级 | ⏱️ 预计时间: 90分钟 | 📋 前置: 003-类与对象

章节导航

学习目标

通过本章学习,你将能够:

  • 🎯 深入理解封装性的概念、原理和重要性
  • 🔒 熟练掌握各种访问控制修饰符的使用场景
  • 🏗️ 学会设计安全、清晰的类接口
  • 🛡️ 理解信息隐藏的原理和具体实现技术
  • ⚡ 掌握属性封装的最佳实践和设计模式
  • 🌐 了解封装性在不同编程语言中的实现差异

1. 封装性概述

1.1 封装性定义

封装性(Encapsulation)是面向对象编程的三大特性之一,它是指将对象的状态(属性)和行为(方法)包装在一起,并对外隐藏对象的内部实现细节,只通过公共接口与外界交互。

classDiagram
    class BankAccount {
        -double balance
        -String accountNumber
        -String ownerName
        +deposit(amount: double)
        +withdraw(amount: double)
        +getBalance(): double
        +getAccountInfo(): String
    }
    
    note for BankAccount "私有属性隐藏内部状态\n公共方法提供安全访问"

1.2 封装性的核心原则

  1. 信息隐藏:隐藏对象的内部实现细节
  2. 接口统一:提供一致的公共接口
  3. 访问控制:控制对对象成员的访问权限
  4. 数据保护:保护对象状态的完整性

2. 访问控制修饰符

2.1 Java中的访问控制

// 文件路径: src/main/java/encapsulation/AccessControlDemo.java
public class AccessControlDemo {
    // public: 公共访问,任何地方都可以访问
    public String publicField = "公共字段";
    
    // protected: 受保护访问,同包或子类可以访问
    protected String protectedField = "受保护字段";
    
    // default(包访问): 默认访问,同包内可以访问
    String defaultField = "默认字段";
    
    // private: 私有访问,只有本类内部可以访问
    private String privateField = "私有字段";
    
    // 公共方法提供对私有字段的访问
    public String getPrivateField() {
        return privateField;
    }
    
    public void setPrivateField(String value) {
        // 可以在设置时添加验证逻辑
        if (value != null && !value.trim().isEmpty()) {
            this.privateField = value;
        }
    }
}

2.2 访问控制级别对比

修饰符同一类同一包子类其他包
public
protected
default
private

2.3 C++中的访问控制

// 文件路径: src/cpp/encapsulation/AccessControl.cpp
class AccessControlDemo {
public:
    // 公共成员:外部可以直接访问
    std::string publicMember;
    void publicMethod() {
        std::cout << "公共方法" << std::endl;
    }
    
protected:
    // 受保护成员:子类可以访问
    std::string protectedMember;
    void protectedMethod() {
        std::cout << "受保护方法" << std::endl;
    }
    
private:
    // 私有成员:只有本类可以访问
    std::string privateMember;
    void privateMethod() {
        std::cout << "私有方法" << std::endl;
    }
    
public:
    // 提供访问私有成员的公共接口
    std::string getPrivateMember() const {
        return privateMember;
    }
    
    void setPrivateMember(const std::string& value) {
        if (!value.empty()) {
            privateMember = value;
        }
    }
};

3. 属性封装最佳实践

3.1 标准的Getter和Setter模式

// 文件路径: src/main/java/encapsulation/Student.java
public class Student {
    // 私有属性
    private String name;
    private int age;
    private double score;
    private String email;
    
    // 构造方法
    public Student(String name, int age) {
        setName(name);  // 使用setter确保验证
        setAge(age);
    }
    
    // Getter方法:提供只读访问
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
    
    public double getScore() {
        return score;
    }
    
    public String getEmail() {
        return email;
    }
    
    // Setter方法:提供受控的写访问
    public void setName(String name) {
        if (name == null || name.trim().isEmpty()) {
            throw new IllegalArgumentException("姓名不能为空");
        }
        this.name = name.trim();
    }
    
    public void setAge(int age) {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("年龄必须在0-150之间");
        }
        this.age = age;
    }
    
    public void setScore(double score) {
        if (score < 0 || score > 100) {
            throw new IllegalArgumentException("分数必须在0-100之间");
        }
        this.score = score;
    }
    
    public void setEmail(String email) {
        if (email != null && !isValidEmail(email)) {
            throw new IllegalArgumentException("邮箱格式不正确");
        }
        this.email = email;
    }
    
    // 私有辅助方法
    private boolean isValidEmail(String email) {
        return email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");
    }
    
    // 业务方法
    public String getGradeLevel() {
        if (score >= 90) return "优秀";
        if (score >= 80) return "良好";
        if (score >= 70) return "中等";
        if (score >= 60) return "及格";
        return "不及格";
    }
    
    @Override
    public String toString() {
        return String.format("Student{name='%s', age=%d, score=%.1f, grade='%s'}",
                name, age, score, getGradeLevel());
    }
}

3.2 不可变对象的封装

// 文件路径: src/main/java/encapsulation/ImmutablePerson.java
public final class ImmutablePerson {
    // 所有字段都是final的
    private final String name;
    private final int age;
    private final List<String> hobbies;
    
    // 构造方法是唯一修改对象状态的方式
    public ImmutablePerson(String name, int age, List<String> hobbies) {
        this.name = Objects.requireNonNull(name, "姓名不能为空");
        if (age < 0) {
            throw new IllegalArgumentException("年龄不能为负数");
        }
        this.age = age;
        // 防御性复制,避免外部修改影响内部状态
        this.hobbies = hobbies == null ? 
            Collections.emptyList() : 
            Collections.unmodifiableList(new ArrayList<>(hobbies));
    }
    
    // 只提供getter方法,没有setter方法
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
    
    // 返回不可修改的集合视图
    public List<String> getHobbies() {
        return hobbies;
    }
    
    // 如果需要"修改",返回新的对象实例
    public ImmutablePerson withAge(int newAge) {
        return new ImmutablePerson(this.name, newAge, 
            new ArrayList<>(this.hobbies));
    }
    
    public ImmutablePerson addHobby(String hobby) {
        List<String> newHobbies = new ArrayList<>(this.hobbies);
        newHobbies.add(hobby);
        return new ImmutablePerson(this.name, this.age, newHobbies);
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        ImmutablePerson that = (ImmutablePerson) obj;
        return age == that.age &&
               Objects.equals(name, that.name) &&
               Objects.equals(hobbies, that.hobbies);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age, hobbies);
    }
    
    @Override
    public String toString() {
        return String.format("ImmutablePerson{name='%s', age=%d, hobbies=%s}",
                name, age, hobbies);
    }
}

4. 封装性设计模式

4.1 Builder模式实现复杂对象封装

// 文件路径: src/main/java/encapsulation/Computer.java
public class Computer {
    // 必需参数
    private final String cpu;
    private final String memory;
    
    // 可选参数
    private final String graphics;
    private final String storage;
    private final String motherboard;
    private final boolean hasWifi;
    private final boolean hasBluetooth;
    
    // 私有构造方法,只能通过Builder创建
    private Computer(Builder builder) {
        this.cpu = builder.cpu;
        this.memory = builder.memory;
        this.graphics = builder.graphics;
        this.storage = builder.storage;
        this.motherboard = builder.motherboard;
        this.hasWifi = builder.hasWifi;
        this.hasBluetooth = builder.hasBluetooth;
    }
    
    // 静态内部Builder类
    public static class Builder {
        // 必需参数
        private final String cpu;
        private final String memory;
        
        // 可选参数 - 初始化为默认值
        private String graphics = "集成显卡";
        private String storage = "256GB SSD";
        private String motherboard = "标准主板";
        private boolean hasWifi = true;
        private boolean hasBluetooth = false;
        
        // Builder构造方法,只包含必需参数
        public Builder(String cpu, String memory) {
            this.cpu = Objects.requireNonNull(cpu, "CPU不能为空");
            this.memory = Objects.requireNonNull(memory, "内存不能为空");
        }
        
        // 可选参数的setter方法,返回Builder实现链式调用
        public Builder graphics(String graphics) {
            this.graphics = graphics;
            return this;
        }
        
        public Builder storage(String storage) {
            this.storage = storage;
            return this;
        }
        
        public Builder motherboard(String motherboard) {
            this.motherboard = motherboard;
            return this;
        }
        
        public Builder wifi(boolean hasWifi) {
            this.hasWifi = hasWifi;
            return this;
        }
        
        public Builder bluetooth(boolean hasBluetooth) {
            this.hasBluetooth = hasBluetooth;
            return this;
        }
        
        // 构建Computer对象
        public Computer build() {
            return new Computer(this);
        }
    }
    
    // Getter方法
    public String getCpu() { return cpu; }
    public String getMemory() { return memory; }
    public String getGraphics() { return graphics; }
    public String getStorage() { return storage; }
    public String getMotherboard() { return motherboard; }
    public boolean hasWifi() { return hasWifi; }
    public boolean hasBluetooth() { return hasBluetooth; }
    
    @Override
    public String toString() {
        return String.format(
            "Computer{cpu='%s', memory='%s', graphics='%s', storage='%s', " +
            "motherboard='%s', wifi=%s, bluetooth=%s}",
            cpu, memory, graphics, storage, motherboard, hasWifi, hasBluetooth);
    }
}

// 使用示例
class ComputerDemo {
    public static void main(String[] args) {
        // 创建基本配置的电脑
        Computer basicComputer = new Computer.Builder("Intel i5", "8GB")
                .build();
        
        // 创建高配置的电脑
        Computer gamingComputer = new Computer.Builder("Intel i9", "32GB")
                .graphics("RTX 4080")
                .storage("1TB NVMe SSD")
                .motherboard("高端游戏主板")
                .wifi(true)
                .bluetooth(true)
                .build();
        
        System.out.println("基本配置: " + basicComputer);
        System.out.println("游戏配置: " + gamingComputer);
    }
}

4.2 工厂模式封装对象创建

// 文件路径: src/main/java/encapsulation/DatabaseConnectionFactory.java
public class DatabaseConnectionFactory {
    // 私有构造方法,防止实例化
    private DatabaseConnectionFactory() {
        throw new UnsupportedOperationException("工厂类不能被实例化");
    }
    
    // 数据库连接类型枚举
    public enum DatabaseType {
        MYSQL, POSTGRESQL, ORACLE, SQLITE
    }
    
    // 工厂方法:根据类型创建相应的数据库连接
    public static DatabaseConnection createConnection(DatabaseType type, 
                                                    String host, 
                                                    int port, 
                                                    String database,
                                                    String username, 
                                                    String password) {
        switch (type) {
            case MYSQL:
                return new MySQLConnection(host, port, database, username, password);
            case POSTGRESQL:
                return new PostgreSQLConnection(host, port, database, username, password);
            case ORACLE:
                return new OracleConnection(host, port, database, username, password);
            case SQLITE:
                return new SQLiteConnection(database); // SQLite只需要数据库文件路径
            default:
                throw new IllegalArgumentException("不支持的数据库类型: " + type);
        }
    }
    
    // 便捷方法:使用默认端口创建连接
    public static DatabaseConnection createConnection(DatabaseType type, 
                                                    String host, 
                                                    String database,
                                                    String username, 
                                                    String password) {
        int defaultPort = getDefaultPort(type);
        return createConnection(type, host, defaultPort, database, username, password);
    }
    
    // 私有辅助方法:获取默认端口
    private static int getDefaultPort(DatabaseType type) {
        switch (type) {
            case MYSQL: return 3306;
            case POSTGRESQL: return 5432;
            case ORACLE: return 1521;
            default: return 0; // SQLite不需要端口
        }
    }
}

// 数据库连接接口
interface DatabaseConnection {
    void connect();
    void disconnect();
    boolean isConnected();
    String getConnectionInfo();
}

// MySQL连接实现
class MySQLConnection implements DatabaseConnection {
    private final String host;
    private final int port;
    private final String database;
    private final String username;
    private final String password;
    private boolean connected = false;
    
    // 包级别构造方法,只能通过工厂创建
    MySQLConnection(String host, int port, String database, String username, String password) {
        this.host = host;
        this.port = port;
        this.database = database;
        this.username = username;
        this.password = password;
    }
    
    @Override
    public void connect() {
        // 模拟连接逻辑
        System.out.println("正在连接到MySQL数据库...");
        connected = true;
        System.out.println("MySQL连接成功: " + getConnectionInfo());
    }
    
    @Override
    public void disconnect() {
        if (connected) {
            System.out.println("断开MySQL连接");
            connected = false;
        }
    }
    
    @Override
    public boolean isConnected() {
        return connected;
    }
    
    @Override
    public String getConnectionInfo() {
        return String.format("MySQL://%s:%d/%s (用户: %s)", host, port, database, username);
    }
}

5. 封装性的高级应用

5.1 属性验证和约束

// 文件路径: src/main/java/encapsulation/BankAccount.java
public class BankAccount {
    // 私有属性
    private String accountNumber;
    private String ownerName;
    private double balance;
    private AccountType accountType;
    private boolean isActive;
    private List<Transaction> transactionHistory;
    
    // 账户类型枚举
    public enum AccountType {
        SAVINGS("储蓄账户", 0.02),
        CHECKING("支票账户", 0.001),
        BUSINESS("商业账户", 0.015);
        
        private final String description;
        private final double interestRate;
        
        AccountType(String description, double interestRate) {
            this.description = description;
            this.interestRate = interestRate;
        }
        
        public String getDescription() { return description; }
        public double getInterestRate() { return interestRate; }
    }
    
    // 构造方法
    public BankAccount(String accountNumber, String ownerName, AccountType accountType) {
        setAccountNumber(accountNumber);
        setOwnerName(ownerName);
        this.accountType = Objects.requireNonNull(accountType, "账户类型不能为空");
        this.balance = 0.0;
        this.isActive = true;
        this.transactionHistory = new ArrayList<>();
    }
    
    // 账户号码验证和设置
    public void setAccountNumber(String accountNumber) {
        if (accountNumber == null || !isValidAccountNumber(accountNumber)) {
            throw new IllegalArgumentException("账户号码格式不正确");
        }
        this.accountNumber = accountNumber;
    }
    
    // 账户持有人姓名验证和设置
    public void setOwnerName(String ownerName) {
        if (ownerName == null || ownerName.trim().isEmpty()) {
            throw new IllegalArgumentException("账户持有人姓名不能为空");
        }
        if (ownerName.length() > 50) {
            throw new IllegalArgumentException("账户持有人姓名不能超过50个字符");
        }
        this.ownerName = ownerName.trim();
    }
    
    // 存款方法
    public void deposit(double amount) {
        validateAccountActive();
        validatePositiveAmount(amount);
        
        this.balance += amount;
        recordTransaction("存款", amount, balance);
        System.out.printf("存款成功:%.2f元,当前余额:%.2f元%n", amount, balance);
    }
    
    // 取款方法
    public void withdraw(double amount) {
        validateAccountActive();
        validatePositiveAmount(amount);
        
        if (amount > balance) {
            throw new IllegalArgumentException("余额不足,当前余额:" + balance);
        }
        
        this.balance -= amount;
        recordTransaction("取款", -amount, balance);
        System.out.printf("取款成功:%.2f元,当前余额:%.2f元%n", amount, balance);
    }
    
    // 转账方法
    public void transfer(BankAccount targetAccount, double amount) {
        validateAccountActive();
        targetAccount.validateAccountActive();
        validatePositiveAmount(amount);
        
        if (amount > balance) {
            throw new IllegalArgumentException("余额不足,无法转账");
        }
        
        this.balance -= amount;
        targetAccount.balance += amount;
        
        recordTransaction("转出到" + targetAccount.accountNumber, -amount, balance);
        targetAccount.recordTransaction("转入自" + this.accountNumber, amount, targetAccount.balance);
        
        System.out.printf("转账成功:向账户%s转账%.2f元%n", 
                targetAccount.accountNumber, amount);
    }
    
    // 计算利息
    public double calculateInterest() {
        return balance * accountType.getInterestRate();
    }
    
    // 获取账户信息
    public String getAccountInfo() {
        return String.format(
            "账户信息:%n" +
            "账户号码:%s%n" +
            "持有人:%s%n" +
            "账户类型:%s%n" +
            "当前余额:%.2f元%n" +
            "账户状态:%s%n" +
            "年利率:%.2f%%%n",
            accountNumber, ownerName, accountType.getDescription(),
            balance, isActive ? "活跃" : "冻结",
            accountType.getInterestRate() * 100
        );
    }
    
    // 获取交易历史(返回不可修改的副本)
    public List<Transaction> getTransactionHistory() {
        return Collections.unmodifiableList(new ArrayList<>(transactionHistory));
    }
    
    // 冻结账户
    public void freezeAccount() {
        this.isActive = false;
        recordTransaction("账户冻结", 0, balance);
        System.out.println("账户已冻结");
    }
    
    // 激活账户
    public void activateAccount() {
        this.isActive = true;
        recordTransaction("账户激活", 0, balance);
        System.out.println("账户已激活");
    }
    
    // Getter方法
    public String getAccountNumber() { return accountNumber; }
    public String getOwnerName() { return ownerName; }
    public double getBalance() { return balance; }
    public AccountType getAccountType() { return accountType; }
    public boolean isActive() { return isActive; }
    
    // 私有验证方法
    private boolean isValidAccountNumber(String accountNumber) {
        // 账户号码必须是10-16位数字
        return accountNumber.matches("\\d{10,16}");
    }
    
    private void validateAccountActive() {
        if (!isActive) {
            throw new IllegalStateException("账户已冻结,无法进行操作");
        }
    }
    
    private void validatePositiveAmount(double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("金额必须大于0");
        }
        if (amount > 1000000) {
            throw new IllegalArgumentException("单次操作金额不能超过100万元");
        }
    }
    
    private void recordTransaction(String type, double amount, double balanceAfter) {
        Transaction transaction = new Transaction(
            System.currentTimeMillis(),
            type,
            amount,
            balanceAfter
        );
        transactionHistory.add(transaction);
    }
    
    // 内部交易记录类
    public static class Transaction {
        private final long timestamp;
        private final String type;
        private final double amount;
        private final double balanceAfter;
        
        private Transaction(long timestamp, String type, double amount, double balanceAfter) {
            this.timestamp = timestamp;
            this.type = type;
            this.amount = amount;
            this.balanceAfter = balanceAfter;
        }
        
        public long getTimestamp() { return timestamp; }
        public String getType() { return type; }
        public double getAmount() { return amount; }
        public double getBalanceAfter() { return balanceAfter; }
        
        @Override
        public String toString() {
            Date date = new Date(timestamp);
            return String.format("%s | %s | %.2f | 余额: %.2f", 
                    date, type, amount, balanceAfter);
        }
    }
}

6. 不同语言中的封装实现

6.1 Python中的封装

# 文件路径: src/python/encapsulation/student.py
class Student:
    """学生类演示Python中的封装"""
    
    def __init__(self, name, age):
        self._name = name  # 受保护属性(约定)
        self._age = age
        self.__student_id = None  # 私有属性(名称改写)
        self.__scores = {}  # 私有属性
    
    @property
    def name(self):
        """姓名属性的getter"""
        return self._name
    
    @name.setter
    def name(self, value):
        """姓名属性的setter,包含验证"""
        if not isinstance(value, str) or not value.strip():
            raise ValueError("姓名必须是非空字符串")
        self._name = value.strip()
    
    @property
    def age(self):
        """年龄属性的getter"""
        return self._age
    
    @age.setter
    def age(self, value):
        """年龄属性的setter,包含验证"""
        if not isinstance(value, int) or value < 0 or value > 150:
            raise ValueError("年龄必须是0-150之间的整数")
        self._age = value
    
    @property
    def student_id(self):
        """学号属性的getter(只读)"""
        return self.__student_id
    
    def set_student_id(self, student_id):
        """设置学号(只能设置一次)"""
        if self.__student_id is not None:
            raise ValueError("学号只能设置一次")
        if not isinstance(student_id, str) or len(student_id) != 10:
            raise ValueError("学号必须是10位字符串")
        self.__student_id = student_id
    
    def add_score(self, subject, score):
        """添加科目成绩"""
        if not isinstance(subject, str) or not subject.strip():
            raise ValueError("科目名称不能为空")
        if not isinstance(score, (int, float)) or score < 0 or score > 100:
            raise ValueError("成绩必须是0-100之间的数字")
        self.__scores[subject.strip()] = float(score)
    
    def get_score(self, subject):
        """获取指定科目成绩"""
        return self.__scores.get(subject)
    
    def get_all_scores(self):
        """获取所有成绩(返回副本)"""
        return self.__scores.copy()
    
    def get_average_score(self):
        """计算平均成绩"""
        if not self.__scores:
            return 0.0
        return sum(self.__scores.values()) / len(self.__scores)
    
    def __str__(self):
        """字符串表示"""
        avg_score = self.get_average_score()
        return f"Student(name='{self._name}', age={self._age}, " \
               f"student_id='{self.__student_id}', avg_score={avg_score:.1f})"
    
    def __repr__(self):
        """开发者友好的字符串表示"""
        return self.__str__()

# 使用示例
if __name__ == "__main__":
    # 创建学生对象
    student = Student("张三", 20)
    student.set_student_id("2023001001")
    
    # 添加成绩
    student.add_score("数学", 95)
    student.add_score("英语", 87)
    student.add_score("物理", 92)
    
    print(student)
    print(f"数学成绩: {student.get_score('数学')}")
    print(f"平均成绩: {student.get_average_score():.1f}")

6.2 C#中的封装

// 文件路径: src/csharp/encapsulation/Employee.cs
using System;
using System.Collections.Generic;
using System.Linq;

public class Employee
{
    // 私有字段
    private string _name;
    private decimal _salary;
    private DateTime _hireDate;
    private Department _department;
    private List<string> _skills;
    
    // 构造函数
    public Employee(string name, decimal salary, Department department)
    {
        Name = name;  // 使用属性确保验证
        Salary = salary;
        Department = department;
        HireDate = DateTime.Now;
        _skills = new List<string>();
    }
    
    // 属性:姓名(带验证)
    public string Name
    {
        get { return _name; }
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("姓名不能为空");
            if (value.Length > 50)
                throw new ArgumentException("姓名长度不能超过50个字符");
            _name = value.Trim();
        }
    }
    
    // 属性:薪资(带验证)
    public decimal Salary
    {
        get { return _salary; }
        set
        {
            if (value < 0)
                throw new ArgumentException("薪资不能为负数");
            if (value > 1000000)
                throw new ArgumentException("薪资不能超过100万");
            _salary = value;
        }
    }
    
    // 只读属性:入职日期
    public DateTime HireDate
    {
        get { return _hireDate; }
        private set { _hireDate = value; }
    }
    
    // 属性:部门
    public Department Department
    {
        get { return _department; }
        set { _department = value ?? throw new ArgumentNullException(nameof(value)); }
    }
    
    // 只读属性:工作年限
    public int YearsOfService
    {
        get { return (DateTime.Now - _hireDate).Days / 365; }
    }
    
    // 只读属性:技能列表(返回副本)
    public IReadOnlyList<string> Skills
    {
        get { return _skills.AsReadOnly(); }
    }
    
    // 方法:添加技能
    public void AddSkill(string skill)
    {
        if (string.IsNullOrWhiteSpace(skill))
            throw new ArgumentException("技能名称不能为空");
        
        skill = skill.Trim();
        if (!_skills.Contains(skill, StringComparer.OrdinalIgnoreCase))
        {
            _skills.Add(skill);
        }
    }
    
    // 方法:移除技能
    public bool RemoveSkill(string skill)
    {
        if (string.IsNullOrWhiteSpace(skill))
            return false;
        
        return _skills.RemoveAll(s => 
            string.Equals(s, skill.Trim(), StringComparison.OrdinalIgnoreCase)) > 0;
    }
    
    // 方法:计算年度奖金
    public decimal CalculateAnnualBonus()
    {
        decimal baseBonus = _salary * 0.1m;  // 基础奖金为薪资的10%
        decimal experienceBonus = YearsOfService * 1000m;  // 每年工作经验1000元
        decimal skillBonus = _skills.Count * 500m;  // 每项技能500元
        
        return baseBonus + experienceBonus + skillBonus;
    }
    
    // 方法:升职加薪
    public void Promote(decimal salaryIncrease, Department newDepartment = null)
    {
        if (salaryIncrease < 0)
            throw new ArgumentException("加薪金额不能为负数");
        
        Salary += salaryIncrease;
        if (newDepartment != null)
        {
            Department = newDepartment;
        }
        
        Console.WriteLine($"{Name} 升职成功!新薪资:{Salary:C}");
    }
    
    public override string ToString()
    {
        return $"Employee {{ Name: {Name}, Salary: {Salary:C}, " +
               $"Department: {Department.Name}, YearsOfService: {YearsOfService}, " +
               $"Skills: [{string.Join(", ", _skills)}] }}";
    }
}

// 部门类
public class Department
{
    public string Name { get; }
    public string Description { get; }
    
    public Department(string name, string description = "")
    {
        Name = string.IsNullOrWhiteSpace(name) 
            ? throw new ArgumentException("部门名称不能为空") 
            : name.Trim();
        Description = description?.Trim() ?? "";
    }
    
    public override string ToString()
    {
        return Name;
    }
}

7. 封装性的优势与注意事项

7.1 封装性的优势

  1. 数据安全性

    • 防止外部直接修改对象内部状态
    • 通过验证确保数据的有效性和一致性
    • 控制访问权限,保护敏感信息
  2. 代码维护性

    • 内部实现变更不影响外部调用
    • 降低模块间的耦合度
    • 便于代码重构和优化
  3. 接口稳定性

    • 提供稳定的公共接口
    • 隐藏复杂的内部逻辑
    • 支持版本兼容性
  4. 代码复用性

    • 封装良好的类更容易被重用
    • 减少代码重复
    • 提高开发效率

7.2 设计注意事项

// 文件路径: src/main/java/encapsulation/EncapsulationBestPractices.java
public class EncapsulationBestPractices {
    
    // ❌ 错误示例:过度暴露内部状态
    public class BadExample {
        public List<String> items = new ArrayList<>();  // 直接暴露可变集合
        public String status;  // 没有验证的公共字段
        public int count;  // 可能与items.size()不一致
    }
    
    // ✅ 正确示例:适当的封装
    public class GoodExample {
        private final List<String> items = new ArrayList<>();
        private String status = "ACTIVE";
        
        // 提供不可变视图
        public List<String> getItems() {
            return Collections.unmodifiableList(items);
        }
        
        // 提供受控的修改方法
        public void addItem(String item) {
            if (item != null && !item.trim().isEmpty()) {
                items.add(item.trim());
            }
        }
        
        public boolean removeItem(String item) {
            return items.remove(item);
        }
        
        // 计算属性而不是存储
        public int getItemCount() {
            return items.size();
        }
        
        public String getStatus() {
            return status;
        }
        
        public void setStatus(String status) {
            if (isValidStatus(status)) {
                this.status = status;
            } else {
                throw new IllegalArgumentException("无效的状态: " + status);
            }
        }
        
        private boolean isValidStatus(String status) {
            return status != null && 
                   (status.equals("ACTIVE") || status.equals("INACTIVE") || status.equals("PENDING"));
        }
    }
    
    // 封装设计原则示例
    public class DesignPrinciples {
        
        // 原则1:最小权限原则 - 默认私有,必要时才公开
        private String internalData;
        
        // 原则2:不变性优先 - 尽可能使用final
        private final String id;
        private final List<String> tags;
        
        public DesignPrinciples(String id) {
            this.id = Objects.requireNonNull(id);
            this.tags = new ArrayList<>();
        }
        
        // 原则3:防御性编程 - 验证输入,保护状态
        public void setInternalData(String data) {
            this.internalData = validateAndClean(data);
        }
        
        private String validateAndClean(String data) {
            if (data == null) {
                return "";
            }
            String cleaned = data.trim();
            if (cleaned.length() > 100) {
                throw new IllegalArgumentException("数据长度不能超过100个字符");
            }
            return cleaned;
        }
        
        // 原则4:返回副本而不是原始引用
        public List<String> getTags() {
            return new ArrayList<>(tags);
        }
        
        // 原则5:提供业务方法而不是简单的getter/setter
        public void addTag(String tag) {
            if (tag != null && !tag.trim().isEmpty() && !tags.contains(tag)) {
                tags.add(tag.trim());
            }
        }
        
        public boolean hasTag(String tag) {
            return tag != null && tags.contains(tag.trim());
        }
        
        public int getTagCount() {
            return tags.size();
        }
    }
}

实践练习

练习1:设计一个图书类

设计一个Book类,要求:

  1. 包含书名、作者、ISBN、价格、出版日期等属性
  2. 实现适当的封装,包括数据验证
  3. 提供借阅和归还功能
  4. 计算图书的库存天数

练习2:实现一个购物车类

设计一个ShoppingCart类,要求:

  1. 管理购物车中的商品列表
  2. 提供添加、删除、修改商品数量的方法
  3. 计算总价、优惠后价格
  4. 确保购物车状态的一致性

练习3:创建一个配置管理类

设计一个ConfigurationManager类,要求:

  1. 使用单例模式确保全局唯一
  2. 支持不同类型的配置项(字符串、数字、布尔值)
  3. 提供配置验证和默认值机制
  4. 支持配置的持久化和加载

总结

封装性是面向对象编程的核心特性之一,它通过以下方式提升代码质量:

核心要点

  1. 信息隐藏:隐藏内部实现细节,只暴露必要的接口
  2. 访问控制:使用适当的访问修饰符控制成员访问权限
  3. 数据验证:在setter方法中添加数据验证逻辑
  4. 接口设计:提供清晰、一致的公共接口
  5. 状态保护:确保对象状态的完整性和一致性

最佳实践

  1. 默认私有:成员默认设为私有,必要时才公开
  2. 不变性优先:尽可能使用final/const修饰符
  3. 防御性编程:验证输入参数,保护对象状态
  4. 返回副本:返回集合或可变对象的副本
  5. 业务方法:提供有意义的业务方法而不是简单的getter/setter

设计原则

  1. 单一职责:每个类只负责一个明确的职责
  2. 最小权限:只提供必要的访问权限
  3. 接口稳定:保持公共接口的稳定性
  4. 实现隐藏:隐藏具体实现,便于后续修改

掌握封装性不仅能提高代码的安全性和可维护性,还是理解其他面向对象特性(继承、多态)的基础。在实际开发中,良好的封装设计是构建高质量软件系统的重要保障。

下一步

在掌握了封装性的原理和实现后,建议继续学习:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值