name: oop-encapsulation user-invocable: false description: 当在面向对象设计中应用封装和信息隐藏原则时使用。当控制对对象状态和行为的访问时使用。 allowed-tools:
- Bash
- Read
OOP 封装
掌握封装和信息隐藏,以创建健壮、可维护的面向对象系统。本技能专注于控制对对象内部的访问和暴露定义良好的接口。
理解封装
封装是将数据和对该数据进行操作的方法捆绑在一个单元内,同时限制对对象某些组件的直接访问。这一原则保护对象完整性并减少耦合。
Java 封装
// 带有验证的强封装
public class BankAccount {
private String accountNumber;
private BigDecimal balance;
private final List<Transaction> transactions;
public BankAccount(String accountNumber, BigDecimal initialBalance) {
if (accountNumber == null || accountNumber.isEmpty()) {
throw new IllegalArgumentException("账户号码必填");
}
if (initialBalance.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("初始余额不能为负");
}
this.accountNumber = accountNumber;
this.balance = initialBalance;
this.transactions = new ArrayList<>();
}
// 对账户号码的只读访问
public String getAccountNumber() {
return accountNumber;
}
// 对余额的只读访问
public BigDecimal getBalance() {
return balance;
}
// 集合的防御性副本
public List<Transaction> getTransactions() {
return Collections.unmodifiableList(transactions);
}
// 带有验证的受控变异
public void deposit(BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("存款金额必须为正");
}
balance = balance.add(amount);
transactions.add(new Transaction(TransactionType.DEPOSIT, amount));
}
// 带有业务逻辑的受控变异
public void withdraw(BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("取款金额必须为正");
}
if (amount.compareTo(balance) > 0) {
throw new InsufficientFundsException("余额不足");
}
balance = balance.subtract(amount);
transactions.add(new Transaction(TransactionType.WITHDRAWAL, amount));
}
}
Python 封装
class Employee:
"""Employee with encapsulated salary information."""
def __init__(self, name: str, salary: float, department: str):
if not name:
raise ValueError("姓名必填")
if salary < 0:
raise ValueError("薪水不能为负")
self._name = name # 受保护属性
self.__salary = salary # 私有属性(名称重整)
self._department = department
self.__performance_rating = 0.0
@property
def name(self) -> str:
"""对姓名的只读访问。"""
return self._name
@property
def department(self) -> str:
"""对部门的只读访问。"""
return self._department
@property
def salary(self) -> float:
"""对薪水的受控访问。"""
return self.__salary
@salary.setter
def salary(self, value: float) -> None:
"""带有验证的受控变异。"""
if value < 0:
raise ValueError("薪水不能为负")
if value < self.__salary * 0.9:
raise ValueError("薪水不能下降超过10%")
self.__salary = value
@property
def performance_rating(self) -> float:
"""对绩效评级的只读访问。"""
return self.__performance_rating
def update_performance(self, rating: float) -> None:
"""带有验证和副作用的受控更新。"""
if not 0 <= rating <= 5:
raise ValueError("评级必须在0到5之间")
self.__performance_rating = rating
# 业务逻辑:对高绩效者自动加薪
if rating >= 4.5:
self.__salary *= 1.10
def give_raise(self, percentage: float) -> None:
"""应用带有验证的百分比加薪。"""
if percentage < 0:
raise ValueError("加薪百分比不能为负")
if percentage > 20:
raise ValueError("单次加薪不能超过20%")
self.__salary *= (1 + percentage / 100)
def __repr__(self) -> str:
return f"Employee(name={self._name}, department={self._department})"
TypeScript 封装
// 带有私有字段的基于类的封装
class UserAccount {
readonly #id: string;
#username: string;
#email: string;
#passwordHash: string;
#lastLoginAt: Date | null = null;
#failedLoginAttempts = 0;
#isLocked = false;
constructor(username: string, email: string, passwordHash: string) {
if (!username || username.length < 3) {
throw new Error("用户名必须至少3个字符");
}
if (!this.isValidEmail(email)) {
throw new Error("无效的邮箱格式");
}
this.#id = crypto.randomUUID();
this.#username = username;
this.#email = email;
this.#passwordHash = passwordHash;
}
// 只读访问
get id(): string {
return this.#id;
}
get username(): string {
return this.#username;
}
get email(): string {
return this.#email;
}
get lastLoginAt(): Date | null {
return this.#lastLoginAt;
}
get isLocked(): boolean {
return this.#isLocked;
}
// 带有验证的受控变异
updateEmail(newEmail: string): void {
if (!this.isValidEmail(newEmail)) {
throw new Error("无效的邮箱格式");
}
this.#email = newEmail;
}
// 封装的业务逻辑
attemptLogin(password: string): boolean {
if (this.#isLocked) {
throw new Error("账户已锁定");
}
if (this.verifyPassword(password)) {
this.#lastLoginAt = new Date();
this.#failedLoginAttempts = 0;
return true;
}
this.#failedLoginAttempts++;
if (this.#failedLoginAttempts >= 3) {
this.#isLocked = true;
}
return false;
}
// 私有辅助方法
private isValidEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
private verifyPassword(password: string): boolean {
// 哈希比较逻辑
return true; // 简化
}
unlock(): void {
this.#isLocked = false;
this.#failedLoginAttempts = 0;
}
}
C# 封装
// 带有属性和后备字段的强封装
public class Product
{
private readonly Guid _id;
private string _name;
private decimal _price;
private int _stockQuantity;
private readonly List<PriceHistory> _priceHistory;
public Product(string name, decimal price, int initialStock)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("产品名称必填", nameof(name));
if (price <= 0)
throw new ArgumentException("价格必须为正", nameof(price));
if (initialStock < 0)
throw new ArgumentException("库存不能为负", nameof(initialStock));
_id = Guid.NewGuid();
_name = name;
_price = price;
_stockQuantity = initialStock;
_priceHistory = new List<PriceHistory>
{
new PriceHistory(price, DateTime.UtcNow)
};
}
// 只读属性
public Guid Id => _id;
// 带有验证的属性
public string Name
{
get => _name;
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("产品名称必填");
_name = value;
}
}
// 带有副作用的属性
public decimal Price
{
get => _price;
set
{
if (value <= 0)
throw new ArgumentException("价格必须为正");
if (value != _price)
{
_price = value;
_priceHistory.Add(new PriceHistory(value, DateTime.UtcNow));
}
}
}
public int StockQuantity => _stockQuantity;
// 集合的防御性副本
public IReadOnlyList<PriceHistory> PriceHistory => _priceHistory.AsReadOnly();
// 封装的业务逻辑
public bool TryReserveStock(int quantity)
{
if (quantity <= 0)
throw new ArgumentException("数量必须为正");
if (_stockQuantity >= quantity)
{
_stockQuantity -= quantity;
return true;
}
return false;
}
public void RestockItems(int quantity)
{
if (quantity <= 0)
throw new ArgumentException("数量必须为正");
_stockQuantity += quantity;
}
public decimal GetAveragePrice()
{
return _priceHistory.Average(h => h.Price);
}
}
public record PriceHistory(decimal Price, DateTime ChangedAt);
数据隐藏模式
Java 中的信息隐藏
// 带有包私有实现的模块模式
public class OrderProcessor {
private final OrderValidator validator;
private final InventoryService inventory;
private final PaymentGateway payment;
public OrderProcessor(
OrderValidator validator,
InventoryService inventory,
PaymentGateway payment
) {
this.validator = validator;
this.inventory = inventory;
this.payment = payment;
}
// 公共接口 - 客户端需要知道的
public OrderResult processOrder(Order order) {
try {
validateOrder(order);
reserveInventory(order);
processPayment(order);
return OrderResult.success(order.getId());
} catch (ValidationException e) {
return OrderResult.validationError(e.getMessage());
} catch (InventoryException e) {
return OrderResult.inventoryError(e.getMessage());
} catch (PaymentException e) {
releaseInventory(order);
return OrderResult.paymentError(e.getMessage());
}
}
// 私有实现 - 对客户端隐藏
private void validateOrder(Order order) {
if (!validator.isValid(order)) {
throw new ValidationException("订单验证失败");
}
}
private void reserveInventory(Order order) {
for (OrderItem item : order.getItems()) {
if (!inventory.reserve(item.getProductId(), item.getQuantity())) {
throw new InventoryException("库存不足");
}
}
}
private void processPayment(Order order) {
PaymentRequest request = createPaymentRequest(order);
PaymentResponse response = payment.charge(request);
if (!response.isSuccessful()) {
throw new PaymentException("支付处理失败");
}
}
private void releaseInventory(Order order) {
for (OrderItem item : order.getItems()) {
inventory.release(item.getProductId(), item.getQuantity());
}
}
private PaymentRequest createPaymentRequest(Order order) {
return PaymentRequest.builder()
.orderId(order.getId())
.amount(order.getTotalAmount())
.customerId(order.getCustomerId())
.build();
}
}
TypeScript 中的闭包基础封装
// 带有闭包用于私有状态的工厂函数
function createCounter(initialValue = 0) {
// 私有状态 - 外部不可访问
let count = initialValue;
const listeners: Array<(value: number) => void> = [];
// 私有函数
function notifyListeners(): void {
listeners.forEach(listener => listener(count));
}
// 公共接口
return {
// 只读访问
getValue(): number {
return count;
},
// 受控变异
increment(): void {
count++;
notifyListeners();
},
decrement(): void {
count--;
notifyListeners();
},
reset(): void {
count = initialValue;
notifyListeners();
},
// 观察者模式
subscribe(listener: (value: number) => void): () => void {
listeners.push(listener);
// 返回取消订阅函数
return () => {
const index = listeners.indexOf(listener);
if (index > -1) {
listeners.splice(index, 1);
}
};
}
};
}
// 用法
const counter = createCounter(10);
const unsubscribe = counter.subscribe(value => console.log(`计数: ${value}`));
counter.increment(); // 日志: 计数: 11
counter.increment(); // 日志: 计数: 12
unsubscribe();
counter.increment(); // 无日志
Python 中的模块模式
# 带有私有实现细节的模块
from typing import Dict, List, Optional
from dataclasses import dataclass
from datetime import datetime
@dataclass
class CacheEntry:
"""内部表示 - 不导出。"""
value: any
expires_at: datetime
access_count: int = 0
class Cache:
"""公共缓存接口。"""
def __init__(self, max_size: int = 100):
self.__entries: Dict[str, CacheEntry] = {}
self.__max_size = max_size
self.__hits = 0
self.__misses = 0
def get(self, key: str) -> Optional[any]:
"""从缓存获取值。"""
entry = self.__entries.get(key)
if entry is None:
self.__misses += 1
return None
if self.__is_expired(entry):
self.__remove(key)
self.__misses += 1
return None
self.__hits += 1
entry.access_count += 1
return entry.value
def set(self, key: str, value: any, ttl_seconds: int = 3600) -> None:
"""设置缓存值并带TTL。"""
if len(self.__entries) >= self.__max_size:
self.__evict_least_used()
expires_at = datetime.now() + timedelta(seconds=ttl_seconds)
self.__entries[key] = CacheEntry(value, expires_at)
def delete(self, key: str) -> bool:
"""从缓存移除条目。"""
return self.__remove(key)
def clear(self) -> None:
"""清除所有缓存条目。"""
self.__entries.clear()
self.__hits = 0
self.__misses = 0
def get_stats(self) -> Dict[str, int]:
"""获取缓存统计。"""
return {
'size': len(self.__entries),
'hits': self.__hits,
'misses': self.__misses,
'hit_rate': self.__calculate_hit_rate()
}
# 私有方法 - 实现细节
def __is_expired(self, entry: CacheEntry) -> bool:
return datetime.now() > entry.expires_at
def __remove(self, key: str) -> bool:
if key in self.__entries:
del self.__entries[key]
return True
return False
def __evict_least_used(self) -> None:
if not self.__entries:
return
least_used = min(
self.__entries.items(),
key=lambda item: item[1].access_count
)
self.__remove(least_used[0])
def __calculate_hit_rate(self) -> float:
total = self.__hits + self.__misses
return self.__hits / total if total > 0 else 0.0
访问控制级别
Java 访问修饰符
// 演示所有访问级别
public class AccessControlExample {
// 私有 - 仅在此类内
private String secretKey;
// 包私有(默认) - 在同一包内
String packageData;
// 受保护 - 在包内和子类中
protected String inheritableData;
// 公共 - 到处
public String publicData;
// 用于工厂模式的私有构造函数
private AccessControlExample(String key) {
this.secretKey = key;
}
// 公共工厂方法
public static AccessControlExample create(String key) {
return new AccessControlExample(key);
}
// 私有辅助方法
private boolean validateKey(String key) {
return key != null && key.length() >= 10;
}
// 用于子类的受保护方法
protected void performSecureOperation() {
if (validateKey(secretKey)) {
// 操作逻辑
}
}
// 公共接口方法
public String getPublicInfo() {
return "公共信息";
}
}
// 用于内部实现的嵌套类
class InternalHelper {
// 包私有 - 仅在包内使用
static void helperMethod() {
// 实现
}
}
C# 访问级别
// 全面的访问控制
public class PaymentProcessor
{
// 私有字段 - 仅在类内
private readonly IPaymentGateway _gateway;
// 受保护字段 - 类和派生类
protected readonly ILogger _logger;
// 内部 - 在同一程序集内
internal readonly string AssemblyId;
// 受保护内部 - 在程序集内或派生类中
protected internal readonly DateTime CreatedAt;
// 私有受保护 - 在类内和同一程序集中的派生类
private protected readonly string ProcessorId;
// 公共构造函数
public PaymentProcessor(IPaymentGateway gateway, ILogger logger)
{
_gateway = gateway ?? throw new ArgumentNullException(nameof(gateway));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
AssemblyId = Guid.NewGuid().ToString();
CreatedAt = DateTime.UtcNow;
ProcessorId = GenerateProcessorId();
}
// 公共方法 - 外部接口
public async Task<PaymentResult> ProcessPaymentAsync(PaymentRequest request)
{
ValidateRequest(request);
return await ExecutePaymentAsync(request);
}
// 受保护方法 - 用于派生类
protected virtual void ValidateRequest(PaymentRequest request)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
if (request.Amount <= 0)
throw new ArgumentException("金额必须为正");
}
// 私有方法 - 内部实现
private async Task<PaymentResult> ExecutePaymentAsync(PaymentRequest request)
{
_logger.LogInformation($"处理支付: {request.Id}");
try
{
var response = await _gateway.ChargeAsync(request);
return ConvertToResult(response);
}
catch (Exception ex)
{
_logger.LogError(ex, "支付处理失败");
return PaymentResult.Failed(ex.Message);
}
}
// 内部方法 - 由程序集使用
internal void ResetGateway()
{
// 重置逻辑
}
// 私有辅助
private static string GenerateProcessorId()
{
return $"PROC-{Guid.NewGuid():N}";
}
// 私有转换
private PaymentResult ConvertToResult(GatewayResponse response)
{
return response.Success
? PaymentResult.Succeeded(response.TransactionId)
: PaymentResult.Failed(response.ErrorMessage);
}
}
不变性与封装
Java 中的不可变对象
// 不可变类 - 通过不可变性封装
public final class Money {
private final BigDecimal amount;
private final Currency currency;
private Money(BigDecimal amount, Currency currency) {
this.amount = amount;
this.currency = currency;
}
public static Money of(BigDecimal amount, Currency currency) {
Objects.requireNonNull(amount, "金额必填");
Objects.requireNonNull(currency, "货币必填");
return new Money(amount, currency);
}
public static Money zero(Currency currency) {
return new Money(BigDecimal.ZERO, currency);
}
// 所有getter返回副本或不可变值
public BigDecimal getAmount() {
return amount;
}
public Currency getCurrency() {
return currency;
}
// 操作返回新实例
public Money add(Money other) {
if (!currency.equals(other.currency)) {
throw new IllegalArgumentException("货币不匹配");
}
return new Money(amount.add(other.amount), currency);
}
public Money subtract(Money other) {
if (!currency.equals(other.currency)) {
throw new IllegalArgumentException("货币不匹配");
}
return new Money(amount.subtract(other.amount), currency);
}
public Money multiply(BigDecimal factor) {
return new Money(amount.multiply(factor), currency);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Money)) return false;
Money other = (Money) obj;
return amount.equals(other.amount) && currency.equals(other.currency);
}
@Override
public int hashCode() {
return Objects.hash(amount, currency);
}
@Override
public String toString() {
return String.format("%s %s", currency.getSymbol(), amount);
}
}
何时使用此技能
在以下情况应用封装原则:
- 设计具有内部状态的类和模块
- 创建具有业务规则的域对象
- 构建API和公共接口
- 保护对象不变量
- 隐藏实现细节
- 防止无效状态转换
- 管理复杂的内部结构
- 实现数据验证
- 创建可变对象的防御性副本
- 控制对敏感数据的访问
- 实现访问控制策略
- 构建框架和库
- 将过程式代码重构为面向对象
- 设计不可变值对象
- 创建线程安全的类
最佳实践
- 默认将字段设为私有,通过方法暴露
- 对访问级别使用最小权限原则
- 在公共方法中验证所有输入
- 返回可变内部对象的防御性副本
- 尽可能使类不可变
- 对不变的字段使用final/readonly
- 封装集合,切勿直接暴露
- 保持实现细节私有
- 使用属性/getter进行受控访问
- 在setter/mutator中实现验证
- 将相关数据和行为分组
- 通过简单接口隐藏复杂性
- 对实现类使用包私有/内部
- 避免为每个字段创建getter/setter对
- 通过隐藏可能变化的内容来设计以应对变化
常见陷阱
- 为每个字段创建getter/setter(JavaBeans反模式)
- 直接暴露可变内部集合
- 为“方便”而将字段设为公共
- 返回对可变内部对象的引用
- 使用受保护字段而不是受保护方法
- 过度使用继承以访问受保护成员
- 在构造函数中忽略验证
- 允许对象以无效状态创建
- 在getter/setter中混合业务逻辑
- 使用静态可变状态
- 忘记制作防御性副本
- 通过异常暴露实现细节
- 不考虑可变状态的线程安全
- 使用友元类破坏封装
- 使用反射访问私有成员
资源
- Joshua Bloch的《Effective Java》(封装章节)
- Robert Martin的《Clean Code》(对象和数据结构)
- 《设计模式:可复用面向对象软件的元素》
- Python数据模型:https://docs.python.org/3/reference/datamodel.html
- C#属性:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties
- Java访问控制:https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html
- TypeScript私有字段:https://www.typescriptlang.org/docs/handbook/2/classes.html#private