引言

设计模式是软件开发中经过验证的、可复用的解决方案,用于解决特定上下文中反复出现的问题。在.NET生态系统中,许多框架都采用了各种设计模式来提高代码的可维护性、可扩展性和可测试性。本文将详细探讨ASP.NET Core、Entity Framework和WPF这三个主流框架中常见的设计模式应用,帮助开发者更好地理解和应用这些模式。

ASP.NET Core中的设计模式

ASP.NET Core是一个跨平台、高性能的开源框架,用于构建现代化的云端应用。其架构中融入了多种设计模式,使开发人员能够构建灵活且可维护的应用程序。

1. 依赖注入(Dependency Injection)模式

依赖注入是ASP.NET Core的核心特性,框架本身就内置了DI容器。

// 在Startup.cs或Program.cs中注册服务
public void ConfigureServices(IServiceCollection services)
{
    // 注册服务的生命周期
    services.AddTransient<ITransientService, TransientService>(); // 每次请求创建新实例
    services.AddScoped<IScopedService, ScopedService>();         // 每个HTTP请求内共享一个实例
    services.AddSingleton<ISingletonService, SingletonService>(); // 应用程序生命周期内共享一个实例
}

// 在控制器中通过构造函数注入
public class HomeController : Controller
{
    private readonly ITransientService _transientService;
    
    // 依赖通过构造函数注入
    public HomeController(ITransientService transientService)
    {
        _transientService = transientService; // 控制器不需要知道具体实现
    }
}

优势:

  • 降低组件间的耦合度
  • 提高代码的可测试性
  • 简化配置管理
  • 提高代码的可维护性和可扩展性

2. 中介者(Mediator)模式

ASP.NET Core中常见的MediatR库实现了中介者模式,用于处理请求/响应和事件通知。

// 定义请求和处理程序
public class GetCustomerQuery : IRequest<CustomerDto>
{
    public int CustomerId { get; set; }
}

public class GetCustomerQueryHandler : IRequestHandler<GetCustomerQuery, CustomerDto>
{
    private readonly ICustomerRepository _repository;

    public GetCustomerQueryHandler(ICustomerRepository repository)
    {
        _repository = repository;
    }

    public async Task<CustomerDto> Handle(GetCustomerQuery request, CancellationToken cancellationToken)
    {
        // 处理查询逻辑
        var customer = await _repository.GetByIdAsync(request.CustomerId);
        return new CustomerDto { Id = customer.Id, Name = customer.Name };
    }
}

// 在控制器中使用
public class CustomerController : Controller
{
    private readonly IMediator _mediator;

    public CustomerController(IMediator mediator)
    {
        _mediator = mediator;
    }

    public async Task<IActionResult> GetCustomer(int id)
    {
        // 发送请求到中介者,由中介者找到合适的处理程序
        var customer = await _mediator.Send(new GetCustomerQuery { CustomerId = id });
        return View(customer);
    }
}

优势:

  • 解耦请求的发送者和接收者
  • 简化复杂的业务逻辑
  • 支持横切关注点(如日志记录、缓存等)

3. 管道(Pipeline)模式

ASP.NET Core中间件是典型的管道模式应用,请求通过一系列组件顺序处理。

public void Configure(IApplicationBuilder app)
{
    // 管道中的每个中间件按顺序处理请求
    app.UseExceptionHandler("/Error");
    app.UseStaticFiles();
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();
    
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

优势:

  • 可以灵活地添加或移除处理步骤
  • 每个中间件只关注特定职责
  • 提高代码的可维护性和可测试性

4. 选项(Options)模式

ASP.NET Core使用选项模式来配置服务行为。

// 定义选项类
public class EmailSettings
{
    public string SmtpServer { get; set; }
    public int Port { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
}

// 注册选项
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));
}

// 在服务中使用选项
public class EmailService
{
    private readonly EmailSettings _settings;
    
    public EmailService(IOptions<EmailSettings> options)
    {
        _settings = options.Value;
    }
    
    public void SendEmail(string to, string subject, string body)
    {
        // 使用_settings中的配置
    }
}

优势:

  • 集中管理配置
  • 强类型配置访问
  • 支持运行时更新配置

Entity Framework Core中的设计模式

Entity Framework Core是.NET的现代化ORM框架,它也采用了多种设计模式来简化数据访问层的开发。

1. 仓储(Repository)与工作单元(Unit of Work)模式

仓储模式封装数据访问逻辑,工作单元模式管理事务一致性。

// 仓储接口
public interface ICustomerRepository
{
    Task<Customer> GetByIdAsync(int id);
    Task<IEnumerable<Customer>> GetAllAsync();
    Task AddAsync(Customer customer);
    Task UpdateAsync(Customer customer);
    Task DeleteAsync(int id);
}

// 工作单元接口
public interface IUnitOfWork
{
    ICustomerRepository Customers { get; }
    IOrderRepository Orders { get; }
    Task<int> SaveChangesAsync();
}

// EF Core实现
public class CustomerRepository : ICustomerRepository
{
    private readonly AppDbContext _context;
    
    public CustomerRepository(AppDbContext context)
    {
        _context = context;
    }
    
    public async Task<Customer> GetByIdAsync(int id)
    {
        return await _context.Customers.FindAsync(id);
    }
    
    // 实现其他方法...
}

public class UnitOfWork : IUnitOfWork
{
    private readonly AppDbContext _context;
    private ICustomerRepository _customerRepository;
    private IOrderRepository _orderRepository;
    
    public UnitOfWork(AppDbContext context)
    {
        _context = context;
    }
    
    public ICustomerRepository Customers => 
        _customerRepository ??= new CustomerRepository(_context);
    
    public IOrderRepository Orders => 
        _orderRepository ??= new OrderRepository(_context);
    
    public async Task<int> SaveChangesAsync()
    {
        return await _context.SaveChangesAsync();
    }
}

优势:

  • 隔离数据访问逻辑,提高可测试性
  • 确保事务一致性
  • 提供统一的数据访问API

2. 规约(Specification)模式

规约模式用于封装查询条件,使它们可重用和可组合。

// 基础规约接口
public interface ISpecification<T>
{
    Expression<Func<T, bool>> Criteria { get; }
    List<Expression<Func<T, object>>> Includes { get; }
    List<string> IncludeStrings { get; }
    Expression<Func<T, object>> OrderBy { get; }
    Expression<Func<T, object>> OrderByDescending { get; }
}

// 基础规约实现
public abstract class BaseSpecification<T> : ISpecification<T>
{
    public Expression<Func<T, bool>> Criteria { get; private set; }
    public List<Expression<Func<T, object>>> Includes { get; } = new List<Expression<Func<T, object>>>();
    public List<string> IncludeStrings { get; } = new List<string>();
    public Expression<Func<T, object>> OrderBy { get; private set; }
    public Expression<Func<T, object>> OrderByDescending { get; private set; }
    
    protected BaseSpecification(Expression<Func<T, bool>> criteria)
    {
        Criteria = criteria;
    }
    
    protected void AddInclude(Expression<Func<T, object>> includeExpression)
    {
        Includes.Add(includeExpression);
    }
    
    protected void AddInclude(string includeString)
    {
        IncludeStrings.Add(includeString);
    }
    
    protected void ApplyOrderBy(Expression<Func<T, object>> orderByExpression)
    {
        OrderBy = orderByExpression;
    }
    
    protected void ApplyOrderByDescending(Expression<Func<T, object>> orderByDescendingExpression)
    {
        OrderByDescending = orderByDescendingExpression;
    }
}

// 具体规约实现
public class CustomerWithOrdersSpecification : BaseSpecification<Customer>
{
    public CustomerWithOrdersSpecification(int customerId) 
        : base(c => c.Id == customerId)
    {
        AddInclude(c => c.Orders);
    }
}

// 在仓储中使用规约
public interface IRepository<T> where T : class
{
    Task<T> GetByIdAsync(int id);
    Task<IReadOnlyList<T>> ListAllAsync();
    Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec);
    Task<T> FirstOrDefaultAsync(ISpecification<T> spec);
}

public class Repository<T> : IRepository<T> where T : class
{
    protected readonly DbContext _dbContext;

    public Repository(DbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec)
    {
        return await ApplySpecification(spec).ToListAsync();
    }

    private IQueryable<T> ApplySpecification(ISpecification<T> spec)
    {
        var query = _dbContext.Set<T>().AsQueryable();
        
        if (spec.Criteria != null)
            query = query.Where(spec.Criteria);
            
        query = spec.Includes.Aggregate(query, (current, include) => current.Include(include));
        
        query = spec.IncludeStrings.Aggregate(query, (current, include) => current.Include(include));
        
        if (spec.OrderBy != null)
            query = query.OrderBy(spec.OrderBy);
        else if (spec.OrderByDescending != null)
            query = query.OrderByDescending(spec.OrderByDescending);
            
        return query;
    }
    
    // 实现其他方法...
}

优势:

  • 使查询条件可复用和组合
  • 提高代码可读性和可维护性
  • 支持复杂查询的构建

3. 领域驱动设计(DDD)模式

Entity Framework Core可以与DDD模式一起使用,构建富领域模型。

// 实体基类
public abstract class Entity
{
    public int Id { get; protected set; }
    
    protected Entity() { }
    
    public override bool Equals(object obj)
    {
        var entity = obj as Entity;
        if (entity == null) return false;
        
        return Id == entity.Id;
    }
    
    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}

// 值对象
public class Address : ValueObject
{
    public string Street { get; private set; }
    public string City { get; private set; }
    public string State { get; private set; }
    public string Country { get; private set; }
    public string ZipCode { get; private set; }
    
    private Address() { } // EF Core需要
    
    public Address(string street, string city, string state, string country, string zipCode)
    {
        Street = street;
        City = city;
        State = state;
        Country = country;
        ZipCode = zipCode;
    }
    
    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return Street;
        yield return City;
        yield return State;
        yield return Country;
        yield return ZipCode;
    }
}

// 聚合根
public class Order : Entity, IAggregateRoot
{
    private readonly List<OrderItem> _orderItems = new List<OrderItem>();
    
    public IReadOnlyCollection<OrderItem> OrderItems => _orderItems.AsReadOnly();
    public decimal TotalAmount => _orderItems.Sum(i => i.UnitPrice * i.Quantity);
    public OrderStatus Status { get; private set; }
    public CustomerId CustomerId { get; private set; }
    public Address ShippingAddress { get; private set; }
    
    private Order() { } // EF Core需要
    
    public Order(CustomerId customerId, Address shippingAddress)
    {
        CustomerId = customerId;
        ShippingAddress = shippingAddress;
        Status = OrderStatus.Created;
    }
    
    public void AddOrderItem(ProductId productId, decimal unitPrice, int quantity)
    {
        // 领域逻辑:检查产品是否已存在
        var existingItem = _orderItems.SingleOrDefault(i => i.ProductId == productId);
        if (existingItem != null)
        {
            // 更新现有项目数量
            existingItem.UpdateQuantity(existingItem.Quantity + quantity);
        }
        else
        {
            // 添加新项目
            _orderItems.Add(new OrderItem(productId, unitPrice, quantity));
        }
    }
    
    public void Confirm()
    {
        // 领域逻辑:确认订单
        if (Status != OrderStatus.Created)
            throw new OrderDomainException("只能确认处于'已创建'状态的订单");
            
        Status = OrderStatus.Confirmed;
    }
}

// DbContext配置
public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
    
    public DbSet<Order> Orders { get; set; }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 配置值对象为拥有的实体
        modelBuilder.Entity<Order>()
            .OwnsOne(o => o.ShippingAddress);
            
        // 配置一对多关系
        modelBuilder.Entity<Order>()
            .HasMany(o => o.OrderItems)
            .WithOne()
            .IsRequired();
            
        // 其他配置...
    }
}

优势:

  • 将业务规则封装在领域模型中
  • 使用强类型的值对象增强类型安全
  • 支持聚合边界以确保数据一致性

WPF中的设计模式

Windows Presentation Foundation (WPF)是用于创建桌面应用的UI框架,它也充分利用了各种设计模式。

1. MVVM(Model-View-ViewModel)模式

MVVM是WPF应用程序中最常用的架构模式,它将应用程序分为三层:Model(模型)、View(视图)和ViewModel(视图模型)。

// Model - 表示数据和业务逻辑
public class Customer : INotifyPropertyChanged
{
    private string _name;
    public string Name
    {
        get => _name;
        set
        {
            if (_name != value)
            {
                _name = value;
                OnPropertyChanged(nameof(Name));
            }
        }
    }
    
    // INotifyPropertyChanged实现
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

// ViewModel - 适配Model供View使用,并提供命令
public class CustomerViewModel : INotifyPropertyChanged
{
    private Customer _customer;
    
    public CustomerViewModel(Customer customer)
    {
        _customer = customer;
        SaveCommand = new RelayCommand(Save, CanSave);
    }
    
    public string Name
    {
        get => _customer.Name;
        set
        {
            if (_customer.Name != value)
            {
                _customer.Name = value;
                OnPropertyChanged(nameof(Name));
            }
        }
    }
    
    public ICommand SaveCommand { get; }
    
    private bool CanSave()
    {
        return !string.IsNullOrWhiteSpace(Name);
    }
    
    private void Save()
    {
        // 保存客户数据
    }
    
    // INotifyPropertyChanged实现
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

// View - XAML界面
<Window x:Class="WpfApp.CustomerView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Customer" Height="300" Width="400">
    <Grid>
        <StackPanel>
            <TextBlock Text="Customer Name:" Margin="10"/>
            <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Margin="10"/>
            <Button Content="Save" Command="{Binding SaveCommand}" Margin="10"/>
        </StackPanel>
    </Grid>
</Window>

优势:

  • 实现关注点分离,提高代码的可维护性
  • 支持UI和业务逻辑的单元测试
  • 利用数据绑定简化UI更新逻辑

2. 命令(Command)模式

WPF支持命令模式,通过实现ICommand接口将用户操作与业务逻辑分离。

// 命令基类
public class RelayCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Predicate<object> _canExecute;
    
    public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }
    
    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute(parameter);
    }
    
    public void Execute(object parameter)
    {
        _execute(parameter);
    }
    
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
}

// 在ViewModel中使用命令
public class MainViewModel
{
    public ICommand OpenCommand { get; }
    public ICommand SaveCommand { get; }
    public ICommand CloseCommand { get; }
    
    public MainViewModel()
    {
        OpenCommand = new RelayCommand(Open);
        SaveCommand = new RelayCommand(Save, CanSave);
        CloseCommand = new RelayCommand(Close);
    }
    
    private void Open(object parameter)
    {
        // 打开文件逻辑
    }
    
    private void Save(object parameter)
    {
        // 保存文件逻辑
    }
    
    private bool CanSave(object parameter)
    {
        // 判断是否可以保存
        return true;
    }
    
    private void Close(object parameter)
    {
        // 关闭操作逻辑
    }
}

// 在XAML中使用命令
<Button Content="Open" Command="{Binding OpenCommand}" />
<Button Content="Save" Command="{Binding SaveCommand}" />
<Button Content="Close" Command="{Binding CloseCommand}" />

优势:

  • 解耦UI事件和业务逻辑
  • 提高代码的可测试性
  • 支持命令的启用/禁用状态管理

3. 观察者(Observer)模式

WPF使用INotifyPropertyChanged和INotifyCollectionChanged接口实现观察者模式,使UI能够响应数据变化。

// ViewModel实现INotifyPropertyChanged
public class ProductViewModel : INotifyPropertyChanged
{
    private decimal _price;
    private int _quantity;
    
    public decimal Price
    {
        get => _price;
        set
        {
            if (_price != value)
            {
                _price = value;
                OnPropertyChanged(nameof(Price));
                OnPropertyChanged(nameof(Total)); // 相关属性也需要通知
            }
        }
    }
    
    public int Quantity
    {
        get => _quantity;
        set
        {
            if (_quantity != value)
            {
                _quantity = value;
                OnPropertyChanged(nameof(Quantity));
                OnPropertyChanged(nameof(Total)); // 相关属性也需要通知
            }
        }
    }
    
    public decimal Total => Price * Quantity;
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

// 使用ObservableCollection实现集合变更通知
public class ProductsViewModel
{
    public ObservableCollection<ProductViewModel> Products { get; }
    
    public ProductsViewModel()
    {
        Products = new ObservableCollection<ProductViewModel>();
        
        // 添加产品
        Products.Add(new ProductViewModel { Price = 10.99m, Quantity = 1 });
        Products.Add(new ProductViewModel { Price = 25.49m, Quantity = 2 });
    }
    
    public void AddProduct(ProductViewModel product)
    {
        Products.Add(product); // 自动通知UI更新
    }
    
    public void RemoveProduct(ProductViewModel product)
    {
        Products.Remove(product); // 自动通知UI更新
    }
}

// XAML数据绑定
<ListBox ItemsSource="{Binding Products}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Price, StringFormat=C}" Width="100"/>
                <TextBlock Text="{Binding Quantity}" Width="50"/>
                <TextBlock Text="{Binding Total, StringFormat=C}" Width="100"/>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

优势:

  • 自动更新UI,响应数据变化
  • 简化视图与数据模型的协调
  • 提供清晰的变更通知机制

4. 装饰器(Decorator)模式

WPF中的附加属性可以被视为装饰器模式的一种实现,它允许为现有元素添加新功能。

// 附加属性定义
public static class ValidationBehavior
{
    public static readonly DependencyProperty IsRequiredProperty =
        DependencyProperty.RegisterAttached(
            "IsRequired",
            typeof(bool),
            typeof(ValidationBehavior),
            new PropertyMetadata(false, OnIsRequiredChanged));
    
    public static bool GetIsRequired(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsRequiredProperty);
    }
    
    public static void SetIsRequired(DependencyObject obj, bool value)
    {
        obj.SetValue(IsRequiredProperty, value);
    }
    
    private static void OnIsRequiredChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is TextBox textBox)
        {
            if ((bool)e.NewValue)
            {
                textBox.TextChanged += TextBox_TextChanged;
                // 初始验证
                ValidateTextBox(textBox);
            }
            else
            {
                textBox.TextChanged -= TextBox_TextChanged;
                // 移除验证样式
                textBox.ClearValue(TextBox.BorderBrushProperty);
                textBox.ToolTip = null;
            }
        }
    }
    
    private static void TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        ValidateTextBox(sender as TextBox);
    }
    
    private static void ValidateTextBox(TextBox textBox)
    {
        if (string.IsNullOrWhiteSpace(textBox.Text))
        {
            // 显示错误
            textBox.BorderBrush = Brushes.Red;
            textBox.ToolTip = "该字段为必填项";
        }
        else
        {
            // 清除错误
            textBox.ClearValue(TextBox.BorderBrushProperty);
            textBox.ToolTip = null;
        }
    }
}

// 在XAML中使用附加属性
<TextBox local:ValidationBehavior.IsRequired="True" />

优势:

  • 在不修改原有类的情况下扩展其功能
  • 增强代码的可复用性
  • 实现横切关注点分离

总结与实践建议

通过深入了解ASP.NET Core、Entity Framework Core和WPF中的常见设计模式应用,我们可以看到这些模式如何帮助开发者构建更加健壮、可维护和可扩展的应用程序。以下是应用这些设计模式时的一些实践建议:

  1. 不要过度设计:设计模式是工具,不是目标。根据项目的实际需求和复杂度选择适当的模式。

  2. 保持一致性:在一个项目中,尽量保持模式使用的一致性,避免混合使用多种风格的模式。

  3. 理解框架的内置模式:现代框架如ASP.NET Core已经内置了许多设计模式,理解它们可以避免"重新发明轮子"。

  4. 关注点分离:无论使用哪种模式,都应该确保关注点分离,使代码更易于维护和测试。

  5. 保持简单:选择最简单、最能满足需求的模式。复杂的模式可能导致更高的维护成本。

通过合理运用这些设计模式,我们可以构建更加健壮、灵活和可维护的.NET应用程序,从而提高开发效率和代码质量。

参考资料

  1. ASP.NET Core 官方文档
  2. Entity Framework Core 官方文档
  3. WPF 官方文档
  4. .NET 架构指南
  5. 设计模式:可复用面向对象软件的基础

在这里插入图片描述

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐