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 封装性的核心原则
- 信息隐藏:隐藏对象的内部实现细节
- 接口统一:提供一致的公共接口
- 访问控制:控制对对象成员的访问权限
- 数据保护:保护对象状态的完整性
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 封装性的优势
-
数据安全性
- 防止外部直接修改对象内部状态
- 通过验证确保数据的有效性和一致性
- 控制访问权限,保护敏感信息
-
代码维护性
- 内部实现变更不影响外部调用
- 降低模块间的耦合度
- 便于代码重构和优化
-
接口稳定性
- 提供稳定的公共接口
- 隐藏复杂的内部逻辑
- 支持版本兼容性
-
代码复用性
- 封装良好的类更容易被重用
- 减少代码重复
- 提高开发效率
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
类,要求:
- 包含书名、作者、ISBN、价格、出版日期等属性
- 实现适当的封装,包括数据验证
- 提供借阅和归还功能
- 计算图书的库存天数
练习2:实现一个购物车类
设计一个ShoppingCart
类,要求:
- 管理购物车中的商品列表
- 提供添加、删除、修改商品数量的方法
- 计算总价、优惠后价格
- 确保购物车状态的一致性
练习3:创建一个配置管理类
设计一个ConfigurationManager
类,要求:
- 使用单例模式确保全局唯一
- 支持不同类型的配置项(字符串、数字、布尔值)
- 提供配置验证和默认值机制
- 支持配置的持久化和加载
总结
封装性是面向对象编程的核心特性之一,它通过以下方式提升代码质量:
核心要点
- 信息隐藏:隐藏内部实现细节,只暴露必要的接口
- 访问控制:使用适当的访问修饰符控制成员访问权限
- 数据验证:在setter方法中添加数据验证逻辑
- 接口设计:提供清晰、一致的公共接口
- 状态保护:确保对象状态的完整性和一致性
最佳实践
- 默认私有:成员默认设为私有,必要时才公开
- 不变性优先:尽可能使用final/const修饰符
- 防御性编程:验证输入参数,保护对象状态
- 返回副本:返回集合或可变对象的副本
- 业务方法:提供有意义的业务方法而不是简单的getter/setter
设计原则
- 单一职责:每个类只负责一个明确的职责
- 最小权限:只提供必要的访问权限
- 接口稳定:保持公共接口的稳定性
- 实现隐藏:隐藏具体实现,便于后续修改
掌握封装性不仅能提高代码的安全性和可维护性,还是理解其他面向对象特性(继承、多态)的基础。在实际开发中,良好的封装设计是构建高质量软件系统的重要保障。
下一步
在掌握了封装性的原理和实现后,建议继续学习:
- 005-继承性原理与实现 - 了解类之间的继承关系
- 006-多态性原理与实现 - 学习多态的实现机制
- 007-抽象类与接口 - 深入理解抽象和接口设计