# 1.5 OOP规范 --- ## 1. 类的职责划分 ### 1.1 单一职责原则 ```csharp // ❌ 不建议:一个类承担多个职责 public class User { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } // 数据访问职责 public void SaveToDatabase() { } // 邮件发送职责 public void SendWelcomeEmail() { } // 日志记录职责 public void LogUserAction() { } // 数据验证职责 public bool ValidateEmail() { } } // ✅ 建议:职责分离 public class User { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } } public class UserRepository { public async Task SaveAsync(User user) { // 数据访问逻辑 } } public class UserEmailService { public async Task SendWelcomeEmailAsync(User user) { // 邮件发送逻辑 } } public class UserValidator { public bool IsValidEmail(string email) { // 验证逻辑 } } ``` ### 1.2 类的大小控制 ```csharp // - 方法数量:建议不超过 20 个 // - 代码行数:建议不超过 300 行 // - 依赖数量:建议不超过 5 个 public class OrderService { // 依赖注入(建议最多5个依赖) private readonly IOrderRepository _orderRepository; private readonly IProductService _productService; private readonly IEmailService _emailService; private readonly ILogger _logger; public OrderService( IOrderRepository orderRepository, IProductService productService, IEmailService emailService, ILogger logger) { _orderRepository = orderRepository; _productService = productService; _emailService = emailService; _logger = logger; } // 核心业务方法 public async Task CreateOrderAsync(CreateOrderRequest request) { // 方法体不超过30行 } } // ❌ 如果类过大,考虑拆分 // 例如:将订单创建、订单查询、订单取消拆分为不同的服务类 public class OrderCreationService { } public class OrderQueryService { } public class OrderCancellationService ``` ## 2. 封装原则 ### 2.1 字段私有化 ```csharp // ❌ 不建议:公开字段 public class BankAccount { public decimal Balance; // 可被外部直接修改 } // ✅ 建议:私有字段 + 公共属性 public class BankAccount { private decimal _balance; public decimal Balance { get => _balance; private set => _balance = value; // 只能内部修改 } // 通过方法控制状态变化 public void Deposit(decimal amount) { if (amount <= 0) { throw new ArgumentException("存款金额必须大于0"); } _balance += amount; } public void Withdraw(decimal amount) { if (amount <= 0) { throw new ArgumentException("取款金额必须大于0"); } if (amount > _balance) { throw new InvalidOperationException("余额不足"); } _balance -= amount; } } ``` ### 2.2 属性设计 ```csharp public class Product { // ✅ 自动属性(简单场景) public int Id { get; set; } public string Name { get; set; } // ✅ 只读属性(外部只能读取) public DateTime CreateTime { get; private set; } // ✅ 计算属性(不存储值) public decimal TotalPrice => Price * Quantity; // ✅ 带验证的属性 private decimal _price; public decimal Price { get => _price; set { if (value < 0) { throw new ArgumentException("价格不能为负数"); } _price = value; } } // ✅ 延迟初始化属性 private List _reviews; public List Reviews => _reviews ??= new List(); // ✅ init 访问器(只能在构造时设置) public string Code { get; init; } // ✅ required 修饰符(C# 11+) public required string Category { get; init; } } // 使用示例 var product = new Product { Code = "P001", Category = "电子产品", // 必须设置 Price = 999.99m }; // product.Code = "P002"; // 编译错误,init 属性不可修改 ``` ### 2.3 对象不变性 ```csharp // ✅ 不可变对象(线程安全) public class Money { public decimal Amount { get; } public string Currency { get; } public Money(decimal amount, string currency) { if (amount < 0) { throw new ArgumentException("金额不能为负数"); } Amount = amount; Currency = currency ?? throw new ArgumentNullException(nameof(currency)); } // 操作返回新对象,而不修改当前对象 public Money Add(Money other) { if (Currency != other.Currency) { throw new InvalidOperationException("货币类型不同"); } return new Money(Amount + other.Amount, Currency); } public Money Multiply(decimal factor) { return new Money(Amount * factor, Currency); } } // ✅ 使用 record 创建不可变对象 public record Address(string Street, string City, string ZipCode); // 使用 var address = new Address("中山路100号", "上海", "200000"); // address.Street = "新地址"; // 编译错误 // 创建副本并修改 var newAddress = address with { Street = "南京路200号" }; ``` ## 3. 继承使用原则 ### 3.1 何时使用继承 ```csharp // ✅ 正确使用继承 public abstract class Animal { public string Name { get; set; } public abstract void MakeSound(); public virtual void Eat() { Console.WriteLine($"{Name} is eating"); } } public class Dog : Animal { public override void MakeSound() { Console.WriteLine("Woof!"); } } public class Cat : Animal { public override void MakeSound() { Console.WriteLine("Meow!"); } public override void Eat() { base.Eat(); // 调用基类实现 Console.WriteLine("Cat loves fish"); } } // ❌ 错误使用继承:仅为复用代码 public class Stack : List // 不应该继承 List { // Stack 不是一个 List } // ✅ 正确:使用组合 public class Stack { private readonly List _items = new(); public void Push(int item) => _items.Add(item); public int Pop() { var item = _items[^1]; _items.RemoveAt(_items.Count - 1); return item; } } ``` ### 3.2 继承层次控制 ```csharp // ✅ 继承深度建议不超过3层 // Level 1 public abstract class Entity { public int Id { get; set; } } // Level 2 public abstract class AuditableEntity : Entity { public DateTime CreateTime { get; set; } public DateTime? UpdateTime { get; set; } } // Level 3 public class Product : AuditableEntity { public string Name { get; set; } public decimal Price { get; set; } } // ❌ 避免更深的继承层次 // Level 4 - 过深 public class SpecialProduct : Product { } // 考虑重新设计 ``` ### 3.3 虚方法使用规范 ```csharp public abstract class PaymentProcessor { // ✅ 模板方法模式 public async Task ProcessPaymentAsync(PaymentRequest request) { // 1. 验证(子类可重写) ValidateRequest(request); // 2. 预处理(子类可重写) await PreProcessAsync(request); // 3. 核心处理(子类必须实现) var result = await ExecutePaymentAsync(request); // 4. 后处理(子类可重写) await PostProcessAsync(result); return result; } // 虚方法(可选重写) protected virtual void ValidateRequest(PaymentRequest request) { if (request.Amount <= 0) { throw new ArgumentException("支付金额必须大于0"); } } protected virtual Task PreProcessAsync(PaymentRequest request) { return Task.CompletedTask; } // 抽象方法(必须实现) protected abstract Task ExecutePaymentAsync(PaymentRequest request); protected virtual Task PostProcessAsync(PaymentResult result) { return Task.CompletedTask; } } // 具体实现 public class AlipayProcessor : PaymentProcessor { protected override async Task ExecutePaymentAsync(PaymentRequest request) { // 支付宝支付逻辑 return new PaymentResult { Success = true }; } protected override async Task PostProcessAsync(PaymentResult result) { // 发送支付通知 await base.PostProcessAsync(result); } } ``` ## 4. 组合优于继承 ### 4.1 使用组合的场景 ```csharp // ❌ 错误:使用继承实现不同功能组合 public class FlyingSwimmingAnimal : Animal { } // 会飞会游泳的动物? // ✅ 正确:使用组合和接口 public interface IFlyable { void Fly(); } public interface ISwimmable { void Swim(); } public class Duck : Animal, IFlyable, ISwimmable { private readonly FlyingAbility _flyingAbility = new(); private readonly SwimmingAbility _swimmingAbility = new(); public void Fly() => _flyingAbility.Fly(); public void Swim() => _swimmingAbility.Swim(); public override void MakeSound() { Console.WriteLine("Quack!"); } } public class FlyingAbility { public void Fly() { Console.WriteLine("Flying in the sky"); } } public class SwimmingAbility { public void Swim() { Console.WriteLine("Swimming in the water"); } } ``` ### 4.2 策略模式代替继承 ```csharp // ✅ 使用策略模式提供不同行为 public interface IShippingStrategy { decimal CalculateCost(decimal weight, decimal distance); } public class StandardShipping : IShippingStrategy { public decimal CalculateCost(decimal weight, decimal distance) { return weight * 0.5m + distance * 0.1m; } } public class ExpressShipping : IShippingStrategy { public decimal CalculateCost(decimal weight, decimal distance) { return weight * 1.0m + distance * 0.2m + 20m; } } public class Order { private IShippingStrategy _shippingStrategy; public void SetShippingStrategy(IShippingStrategy strategy) { _shippingStrategy = strategy ?? throw new ArgumentNullException(nameof(strategy)); } public decimal CalculateShippingCost(decimal weight, decimal distance) { if (_shippingStrategy == null) { throw new InvalidOperationException("运输策略未设置"); } return _shippingStrategy.CalculateCost(weight, distance); } } ``` ## 5. 接口设计原则 ### 5.1 接口命名与定义 ```csharp // ✅ 接口命名以 I 开头 public interface ICustomerRepository { Task GetByIdAsync(int id); Task> GetAllAsync(); Task AddAsync(Customer customer); Task UpdateAsync(Customer customer); Task DeleteAsync(int id); } // ✅ 能力接口(形容词) public interface IDisposable { void Dispose(); } public interface IComparable { int CompareTo(T other); } // ✅ 服务接口(名词) public interface IEmailService { Task SendAsync(string to, string subject, string body); } public interface ILogger { void LogInformation(string message); void LogError(Exception ex, string message); } ``` ### 5.2 接口隔离 ```csharp // ❌ 不建议:臃肿的接口 public interface IRepository { // CRUD 操作 void Add(); void Update(); void Delete(); void Get(); // 批量操作 void BulkAdd(); void BulkUpdate(); void BulkDelete(); // 搜索功能 void Search(); void AdvancedSearch(); // 导出功能 void ExportToExcel(); void ExportToPdf(); } // ✅ 建议:接口隔离 public interface IReadRepository { Task GetByIdAsync(int id); Task> GetAllAsync(); } public interface IWriteRepository { Task AddAsync(T entity); Task UpdateAsync(T entity); Task DeleteAsync(int id); } public interface IBulkRepository { Task BulkAddAsync(IEnumerable entities); Task BulkUpdateAsync(IEnumerable entities); } public interface ISearchableRepository { Task> SearchAsync(SearchCriteria criteria); } // 组合使用 public interface ICustomerRepository : IReadRepository, IWriteRepository, ISearchableRepository { // 特定于 Customer 的方法 Task GetByEmailAsync(string email); } ``` ### 5.3 显式接口实现 ```csharp public interface IAnimal { void Move(); } public interface IRobot { void Move(); } // ✅ 显式接口实现解决方法冲突 public class RobotDog : IAnimal, IRobot { // 隐式实现(默认) public void Move() { Console.WriteLine("RobotDog moving"); } // 显式实现 IAnimal void IAnimal.Move() { Console.WriteLine("Animal walking"); } // 显式实现 IRobot void IRobot.Move() { Console.WriteLine("Robot moving mechanically"); } } // 使用 var robotDog = new RobotDog(); robotDog.Move(); // "RobotDog moving" ((IAnimal)robotDog).Move(); // "Animal walking" ((IRobot)robotDog).Move(); // "Robot moving mechanically" ``` ## 6. 抽象类使用场景 ### 6.1 抽象类 vs 接口 ```csharp // ✅ 使用抽象类:有共同实现逻辑 public abstract class Document { // 公共属性 public string Title { get; set; } public DateTime CreateTime { get; set; } // 公共方法实现 public void Save() { ValidateBeforeSave(); PerformSave(); LogSave(); } // 模板方法 protected virtual void ValidateBeforeSave() { if (string.IsNullOrWhiteSpace(Title)) { throw new ValidationException("标题不能为空"); } } // 抽象方法(强制子类实现) protected abstract void PerformSave(); // 虚方法(可选重写) protected virtual void LogSave() { Console.WriteLine($"Document '{Title}' saved at {DateTime.Now}"); } } public class PdfDocument : Document { protected override void PerformSave() { // PDF 特定的保存逻辑 Console.WriteLine("Saving as PDF"); } } public class WordDocument : Document { protected override void PerformSave() { // Word 特定的保存逻辑 Console.WriteLine("Saving as Word document"); } protected override void ValidateBeforeSave() { base.ValidateBeforeSave(); // 额外的验证逻辑 } } // ✅ 使用接口:定义契约,无共同实现 public interface INotificationSender { Task SendAsync(string recipient, string message); } public class EmailSender : INotificationSender { public async Task SendAsync(string recipient, string message) { // 邮件发送实现 } } public class SmsSender : INotificationSender { public async Task SendAsync(string recipient, string message) { // 短信发送实现 } } ``` ### 6.2 抽象类设计要点 ```csharp public abstract class BaseEntity { // ✅ 抽象类可以有构造函数 protected BaseEntity() { CreateTime = DateTime.UtcNow; Id = Guid.NewGuid(); } // ✅ 可以有字段 private readonly List _domainEvents = new(); // 公共属性 public Guid Id { get; private set; } public DateTime CreateTime { get; private set; } public DateTime? UpdateTime { get; protected set; } // ✅ 受保护的方法供子类使用 protected void AddDomainEvent(DomainEvent domainEvent) { _domainEvents.Add(domainEvent); } protected void MarkAsUpdated() { UpdateTime = DateTime.UtcNow; } // 抽象方法 public abstract void Validate(); } public class Customer : BaseEntity { public string Name { get; set; } public string Email { get; set; } public override void Validate() { if (string.IsNullOrWhiteSpace(Name)) { throw new ValidationException("客户名称不能为空"); } if (string.IsNullOrWhiteSpace(Email)) { throw new ValidationException("邮箱不能为空"); } } public void UpdateEmail(string newEmail) { Email = newEmail; MarkAsUpdated(); // 使用基类方法 AddDomainEvent(new CustomerEmailChangedEvent(Id, newEmail)); } } ``` ## 7. 密封类使用 ### 7.1 何时使用 `sealed` ```csharp // ✅ 防止被继承(性能优化、设计意图) public sealed class ConfigurationManager { private readonly Dictionary _settings = new(); public string GetSetting(string key) { return _settings.TryGetValue(key, out var value) ? value : null; } } // ❌ 无法继承 // public class CustomConfigManager : ConfigurationManager { } // 编译错误 // ✅ 工具类密封 public sealed class StringHelper { private StringHelper() { } // 私有构造防止实例化 public static string Truncate(string value, int maxLength) { if (string.IsNullOrEmpty(value) || value.Length <= maxLength) { return value; } return value.Substring(0, maxLength) + "..."; } } // ✅ 密封重写方法(防止进一步重写) public class BaseProcessor { public virtual void Process() { Console.WriteLine("Base processing"); } } public class DerivedProcessor : BaseProcessor { // 密封此方法,子类不能再重写 public sealed override void Process() { Console.WriteLine("Derived processing"); base.Process(); } } public class FurtherDerived : DerivedProcessor { // ❌ 编译错误:不能重写密封方法 // public override void Process() { } } ``` ## 8. 访问修饰符使用规范 ### 8.1 访问级别选择 ```csharp public class Product { // ✅ private - 只在类内部使用 private decimal _costPrice; private List _priceHistory = new(); // ✅ private protected - 仅当前类和派生类访问(同一程序集) private protected void RecordPriceChange(decimal oldPrice, decimal newPrice) { _priceHistory.Add(new PriceHistory(oldPrice, newPrice)); } // ✅ protected - 当前类和派生类可访问 protected decimal CostPrice => _costPrice; // ✅ internal - 同一程序集内可访问 internal void SetCostPrice(decimal cost) { _costPrice = cost; } // ✅ protected internal - 同一程序集或派生类 protected internal decimal CalculateMargin() { return Price - _costPrice; } // ✅ public - 所有地方可访问 public decimal Price { get; set; } public string Name { get; set; } public decimal GetProfitMargin() { return (Price - _costPrice) / Price * 100; } } // 访问修饰符决策树: // 1. 只在当前类使用? -> private // 2. 需要子类访问? -> protected // 3. 同一程序集内使用? -> internal // 4. 需要外部调用? -> public // 5. 同程序集或子类? -> protected internal // 6. 仅同程序集的子类? -> private protected ``` ### 8.2 最小权限原则 ```csharp // ✅ 建议:默认使用最小访问权限 public class OrderProcessor { // 私有字段 private readonly IOrderRepository _repository; private readonly ILogger _logger; // 公共构造函数 public OrderProcessor(IOrderRepository repository, ILogger logger) { _repository = repository; _logger = logger; } // 公共方法(对外API) public async Task ProcessOrderAsync(int orderId) { var order = await GetOrderAsync(orderId); ValidateOrder(order); await SaveOrderAsync(order); return order; } // 私有方法(内部实现细节) private async Task GetOrderAsync(int orderId) { return await _repository.GetByIdAsync(orderId); } private void ValidateOrder(Order order) { if (order == null) { throw new ArgumentNullException(nameof(order)); } } private async Task SaveOrderAsync(Order order) { await _repository.UpdateAsync(order); _logger.LogInformation("Order {OrderId} saved", order.Id); } } // ❌ 不建议:不必要的公开 public class BadOrderProcessor { // 不应该公开 public IOrderRepository Repository { get; set; } // 不应该公开内部实现 public void ValidateOrder(Order order) { } public async Task SaveOrderAsync(Order order) { } } ```