关于依赖注入

发布于:2025-03-21 ⋅ 阅读:(25) ⋅ 点赞:(0)
一、依赖注入的核心原理
  1. 控制反转(Inversion of Control, IoC)

    • 核心思想:将对象的创建和依赖管理从代码内部转移到外部容器,控制权由容器接管。
    • 示例:在 WPF 中,MainViewModel不直接创建IDataService,而是通过容器注入。
    • 实现方式:通过构造函数、属性或方法注入依赖项,容器负责解析依赖关系并实例化对象。
  2. 依赖管理机制

    • 依赖图:容器根据对象间的依赖关系构建有向图,确保正确的创建顺序。
    • 生命周期管理:支持单例(Singleton)、瞬态(Transient)、作用域(Scoped)等模式。
      • 单例:全局唯一实例(如日志服务)。
      • 瞬态:每次请求创建新实例(如临时数据处理服务)。
      • 作用域:在特定上下文(如 HTTP 请求)内共享实例。
  3. 松耦合设计

    • 通过接口定义服务,实现类与调用者解耦。例如,MockDataService实现IDataService,当需要切换数据源时,只需替换实现类而不影响调用方。
二、依赖注入的核心原则
  1. 依赖倒置原则(Dependency Inversion Principle, DIP)

    • 高层模块不依赖底层模块,两者均依赖抽象。
    • 示例MainViewModel依赖IDataService接口,而非具体的MockDataService类。
    • 作用:提高代码灵活性,支持运行时动态替换服务实现。
  2. 单一职责原则(Single Responsibility Principle, SRP)

    • 每个类仅负责单一功能,依赖由外部注入。
    • 示例ConsoleLoggerService专注日志记录,MockDataService专注数据获取,通过构造函数注入协作。
  3. 构造函数注入优先

    • 通过构造函数明确声明依赖,确保对象初始化时依赖已准备好。
    • 反模式:避免使用属性注入(Setter Injection)或字段注入(Field Injection),因为可能导致未初始化的依赖。
  4. 显式依赖原则

    • 依赖关系必须显式声明,而非隐式查找(如通过全局服务定位器)。
    • 优势:提高代码可读性,便于静态分析和测试。

案例一:

MainWindow.xaml

<Window x:Class="UserApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="User App" Height="350" Width="525">
    <Grid>
        <StackPanel Orientation="Vertical" Margin="20">
            <TextBox Text="{Binding NewName, UpdateSourceTrigger=PropertyChanged}" PlaceholderText="Enter user name" />
            <TextBox Text="{Binding NewAge, UpdateSourceTrigger=PropertyChanged}" PlaceholderText="Enter user age" />
            <Button Content="Add User" Command="{Binding AddUserCommand}" />
            <ListBox ItemsSource="{Binding Users}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding Name}" Margin="5" />
                            <TextBlock Text=" - " Margin="5" />
                            <TextBlock Text="{Binding Age}" Margin="5" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </StackPanel>
    </Grid>
</Window>    

MainWindow.xaml.cs
 

using Microsoft.Extensions.DependencyInjection;
using System.Windows;

namespace UserApp
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            var serviceCollection = new ServiceCollection();
            serviceCollection.AddSingleton<IUserService, UserService>();
            serviceCollection.AddSingleton<UserViewModel>();
            var serviceProvider = serviceCollection.BuildServiceProvider();

            DataContext = serviceProvider.GetRequiredService<UserViewModel>();
        }
    }
}    

UserService.cs

using System.Collections.Generic;

// 用户服务接口
public interface IUserService
{
    List<User> GetUsers();
    void AddUser(User user);
}

// 用户类
public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// 用户服务实现类
public class UserService : IUserService
{
    private readonly List<User> _users = new List<User>();

    public List<User> GetUsers()
    {
        return _users;
    }

    public void AddUser(User user)
    {
        _users.Add(user);
    }
}    

UserViewModel.cs

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;

// 用户视图模型
public class UserViewModel : INotifyPropertyChanged
{
    private readonly IUserService _userService;
    public ObservableCollection<User> Users { get; set; }
    private string _newName;
    private int _newAge;

    public string NewName
    {
        get { return _newName; }
        set
        {
            _newName = value;
            OnPropertyChanged();
        }
    }

    public int NewAge
    {
        get { return _newAge; }
        set
        {
            _newAge = value;
            OnPropertyChanged();
        }
    }

    public UserViewModel(IUserService userService)
    {
        _userService = userService;
        Users = new ObservableCollection<User>(_userService.GetUsers());
    }

    public void AddNewUser()
    {
        if (!string.IsNullOrEmpty(NewName) && NewAge > 0)
        {
            var newUser = new User { Name = NewName, Age = NewAge };
            _userService.AddUser(newUser);
            Users.Add(newUser);
            NewName = string.Empty;
            NewAge = 0;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}    

案例二

Book.cs

namespace WpfMvvmDiExample.Models
{
    public class Book
    {
        public string Title { get; set; }
        public string Author { get; set; }
    }
}    

BookService.cs

using System.Collections.Generic;
using WpfMvvmDiExample.Models;

namespace WpfMvvmDiExample.Services
{
    public class BookService : IBookService
    {
        private readonly List<Book> _books = new List<Book>();

        public List<Book> GetBooks()
        {
            return _books;
        }

        public void AddBook(Book book)
        {
            _books.Add(book);
        }
    }
}    

IBookService.cs

using System.Collections.Generic;

namespace WpfMvvmDiExample.Services
{
    public interface IBookService
    {
        List<Book> GetBooks();
        void AddBook(Book book);
    }
}    

BookViewModel.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using WpfMvvmDiExample.Models;
using WpfMvvmDiExample.Services;

namespace WpfMvvmDiExample.ViewModels
{
    public class BookViewModel : INotifyPropertyChanged
    {
        private readonly IBookService _bookService;
        public ObservableCollection<Book> Books { get; set; }
        private string _newTitle;
        private string _newAuthor;

        public string NewTitle
        {
            get { return _newTitle; }
            set
            {
                _newTitle = value;
                OnPropertyChanged();
            }
        }

        public string NewAuthor
        {
            get { return _newAuthor; }
            set
            {
                _newAuthor = value;
                OnPropertyChanged();
            }
        }

        public BookViewModel(IBookService bookService)
        {
            _bookService = bookService;
            Books = new ObservableCollection<Book>(_bookService.GetBooks());
        }

        public void AddNewBook()
        {
            if (!string.IsNullOrEmpty(NewTitle) && !string.IsNullOrEmpty(NewAuthor))
            {
                var newBook = new Book { Title = NewTitle, Author = NewAuthor };
                _bookService.AddBook(newBook);
                Books.Add(newBook);
                NewTitle = string.Empty;
                NewAuthor = string.Empty;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}    

App.xaml.cs

using Microsoft.Extensions.DependencyInjection;
using System.Windows;
using WpfMvvmDiExample.Services;
using WpfMvvmDiExample.ViewModels;

namespace WpfMvvmDiExample
{
    public partial class App : Application
    {
        private readonly ServiceProvider _serviceProvider;

        public App()
        {
            var serviceCollection = new ServiceCollection();
            ConfigureServices(serviceCollection);
            _serviceProvider = serviceCollection.BuildServiceProvider();
        }

        private void ConfigureServices(ServiceCollection services)
        {
            services.AddSingleton<IBookService, BookService>();
            services.AddSingleton<BookViewModel>();
            services.AddTransient<MainWindow>();
        }

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            var mainWindow = _serviceProvider.GetRequiredService<MainWindow>();
            mainWindow.Show();
        }
    }
}    

MainWindow.xaml

<Window x:Class="WpfMvvmDiExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Book Management App" Height="350" Width="525">
    <Grid>
        <StackPanel Orientation="Vertical" Margin="20">
            <TextBox Text="{Binding NewTitle, UpdateSourceTrigger=PropertyChanged}" PlaceholderText="Enter book title" />
            <TextBox Text="{Binding NewAuthor, UpdateSourceTrigger=PropertyChanged}" PlaceholderText="Enter book author" />
            <Button Content="Add Book" Click="AddBookButton_Click" />
            <ListBox ItemsSource="{Binding Books}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding Title}" Margin="5" />
                            <TextBlock Text=" - " Margin="5" />
                            <TextBlock Text="{Binding Author}" Margin="5" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </StackPanel>
    </Grid>
</Window>    

MainWindow.xaml.cs

using Microsoft.Extensions.DependencyInjection;
using System.Windows;
using WpfMvvmDiExample.ViewModels;

namespace WpfMvvmDiExample
{
    public partial class MainWindow : Window
    {
        private readonly BookViewModel _viewModel;

        public MainWindow(BookViewModel viewModel)
        {
            InitializeComponent();
            _viewModel = viewModel;
            DataContext = _viewModel;
        }

        private void AddBookButton_Click(object sender, RoutedEventArgs e)
        {
            _viewModel.AddNewBook();
        }
    }
}