面向对象编程封装Skill oop-encapsulation

本技能专注于面向对象编程中的封装和信息隐藏原则,用于控制对对象内部状态和行为的访问,通过定义清晰的接口来提高代码的健壮性、可维护性和安全性。适用于Java、Python、TypeScript、C#等多种编程语言,涵盖访问控制、数据隐藏模式、不变性设计等。关键词:封装、信息隐藏、OOP、访问控制、软件设计、数据保护。

架构设计 0 次安装 0 次浏览 更新于 3/25/2026

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);
    }
}

何时使用此技能

在以下情况应用封装原则:

  1. 设计具有内部状态的类和模块
  2. 创建具有业务规则的域对象
  3. 构建API和公共接口
  4. 保护对象不变量
  5. 隐藏实现细节
  6. 防止无效状态转换
  7. 管理复杂的内部结构
  8. 实现数据验证
  9. 创建可变对象的防御性副本
  10. 控制对敏感数据的访问
  11. 实现访问控制策略
  12. 构建框架和库
  13. 将过程式代码重构为面向对象
  14. 设计不可变值对象
  15. 创建线程安全的类

最佳实践

  1. 默认将字段设为私有,通过方法暴露
  2. 对访问级别使用最小权限原则
  3. 在公共方法中验证所有输入
  4. 返回可变内部对象的防御性副本
  5. 尽可能使类不可变
  6. 对不变的字段使用final/readonly
  7. 封装集合,切勿直接暴露
  8. 保持实现细节私有
  9. 使用属性/getter进行受控访问
  10. 在setter/mutator中实现验证
  11. 将相关数据和行为分组
  12. 通过简单接口隐藏复杂性
  13. 对实现类使用包私有/内部
  14. 避免为每个字段创建getter/setter对
  15. 通过隐藏可能变化的内容来设计以应对变化

常见陷阱

  1. 为每个字段创建getter/setter(JavaBeans反模式)
  2. 直接暴露可变内部集合
  3. 为“方便”而将字段设为公共
  4. 返回对可变内部对象的引用
  5. 使用受保护字段而不是受保护方法
  6. 过度使用继承以访问受保护成员
  7. 在构造函数中忽略验证
  8. 允许对象以无效状态创建
  9. 在getter/setter中混合业务逻辑
  10. 使用静态可变状态
  11. 忘记制作防御性副本
  12. 通过异常暴露实现细节
  13. 不考虑可变状态的线程安全
  14. 使用友元类破坏封装
  15. 使用反射访问私有成员

资源