WPF Desktop C#中因注入依赖项而导致的多个构造函数参数

bnl4lu3b  于 2023-01-02  发布在  C#
关注(0)|答案(1)|浏览(140)

我有一个问题,希望帮助我。我有一些服务类,例如,我显示其中一个的一部分:

public class CategoriesService : ICategoriesService
{           
    public List<Categories>? getAll()
    {
        return RYCContextService.getInstance().BBDD.categories?.ToList();
    }
    .
    .
    .
}

然后,我有这样的类:

public class TransactionsService : ITransactionsService
{
    private readonly ISplitsService splitsService;
    private readonly ICategoriesService categoriesService;
    private readonly IAccountsService accountsService;

    public TransactionsService(ISplitsService splitsService,ICategoriesService categoriesService,
        IAccountsService accountsService)
    {
        this.splitsService = splitsService;
        this.accountsService = accountsService;
        this.categoriesService = categoriesService;
    }

    public List<Transactions>? getAll()
    {
        return RYCContextService.getInstance().BBDD.transactions?.ToList();
    }
    .
    .
    .
}

它使用类别类和其他类,我在它们上使用注入的依赖项,并通过构造函数参数传递服务以解析这些依赖项。
此类服务在表单中使用,例如:

public partial class FrmTransaction : Window
{
    private Transactions? transaction;
    private readonly int? accountidDefault;
    private readonly IAccountsService accountsService;
    private readonly ICategoriesService categoriesService;
    private readonly IPersonsService personsService;
    private readonly ITagsService tagsService;
    private readonly ISplitsService splitsService;
    private readonly ITransactionsService transactionsService;
    private readonly ITransactionsStatusService transactionsStatusService;

    public FrmTransaction(IAccountsService accountsService, ICategoriesService categoriesService,
        IPersonsService personsService, ITagsService tagsService,ITransactionsService transactionsService,
        ITransactionsStatusService transactionsStatusService, ISplitsService splitsService)
    {
        this.accountsService = accountsService;
        this.categoriesService = categoriesService;
        this.personsService = personsService;
        this.tagsService = tagsService;
        this.transactionsService = transactionsService;
        this.transactionsStatusService = transactionsStatusService;
        this.splitsService = splitsService;
        InitializeComponent();
        
    }
    . . . . .
    . . . .
    . . . . 
}

此窗体是从主窗体调用的:

public partial class MainWindow : Window
{
    private readonly IAccountsService accountsService;
    private readonly IAccountsTypesService accountsTypesService;
    private readonly ICategoriesService categoriesService;
    private readonly ICategoriesTypesService categoriesTypesService;
    private readonly ITransactionsService transactionsService;
    private readonly ITransactionsRemindersService transactionsRemindersService;
    private readonly IExpirationsRemindersService expirationsRemindersService;
    private readonly ISplitsService splitsService;
    private readonly ISplitsRemindersService splitsRemindersService;
    private readonly IPeriodsRemindersService periodsRemindersService;
    private readonly IPersonsService personsService;
    private readonly ITagsService tagsService;
    private readonly ITransactionsStatusService transactionsStatusService;

    public MainWindow(IAccountsService accountsService, ITransactionsService transactionsService,
        ISplitsService splitsService, IExpirationsRemindersService expirationsRemindersService,
        ICategoriesService categoriesService,ICategoriesTypesService categoriesTypesService,
        IAccountsTypesService accountsTypesService,IPersonsService personsService,
        ITagsService tagsService, ITransactionsRemindersService transactionsRemindersService,
        ITransactionsStatusService transactionsStatusService, ISplitsRemindersService splitsRemindersService,
        IPeriodsRemindersService periodsRemindersService)
    {
        InitializeComponent();
        this.accountsService = accountsService;
        this.transactionsService = transactionsService; 
        this.splitsService= splitsService;  
        this.expirationsRemindersService = expirationsRemindersService;
        this.categoriesService = categoriesService;
        this.categoriesTypesService = categoriesTypesService;
        this.accountsTypesService = accountsTypesService;
        this.personsService = personsService;
        this.tagsService = tagsService;
        this.transactionsRemindersService = transactionsRemindersService;
        this.transactionsStatusService = transactionsStatusService;
        this.periodsRemindersService = periodsRemindersService;
        this.splitsRemindersService = splitsRemindersService;
    }

    private void gvTransactions_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        if (gvTransactions.CurrentItem != null)
        {
            FrmTransaction frm = new FrmTransaction((Transactions)gvTransactions.CurrentItem,accountsService,categoriesService,
                personsService,tagsService,transactionsService,transactionsStatusService,splitsService);
            frm.ShowDialog();
            loadAccounts();
            loadTransactions();
            refreshBalance();
        }
    }
    ....
    ....
    .....
}

我认为有一些东西是不正确的,因为代码对我来说是非常丑陋的,对于我从主体类调用的每个窗体,增量类变量和构造函数参数,它不用于主体窗体,只用于传递到下一个窗体。

k10s72fa

k10s72fa1#

您的代码存在一些设计问题,导致您目前的情况是感觉代码难以维护。根据您希望代码有多干净、应用程序有多大(以及它需要多大的可扩展性),您可以将设计问题归类为严重问题:

问题

您的构造函数可能有太多的参数。这表明您的类可能有太多的依赖项,这意味着它有太多的责任。您应该重新审视您的类设计。这个问题会使您的代码难以理解,即使是您自己。
溶液
应用"单一责任原则"(SOLID中的"S"),将大类拆分为许多较小的类。将功能从定义了太多依赖项和责任的类中移除。这也有助于更好地理解代码。

问题

你到处散布RYCContextService.getInstance。看起来你在这里实现了 * Singleton * 模式。你应该总是避免它。特别是在使用依赖注入(或任何形式的IoC)的环境中,你不需要 * Singleton
你通常使用依赖注入来使你的应用程序具有可扩展性。给static示例添加一个紧耦合会使目标落空。它还引入了几个严重的问题,比如降低依赖类的可测试性,从而降低你的应用程序的可测试性。
您还会造成潜在的内存泄漏(此static引用的
所有*引用和资源将在整个应用程序生存期内保持活动状态。原因:一个static引用被当作一个引用(又名对象)树的对象根。垃圾收集器不能收集static对象树)。
溶液
相反,使用IoC容器的生存期管理。例如,全局共享示例应该像其他依赖项一样注入。但是配置IoC容器以始终返回 * 相同 * 示例(共享示例)。对于大多数IoC容器,此生存期称为Singleton,如模式。

问题

你显式地创建示例。这种显式创建要求创建者知道所创建类型的所有依赖项。
在依赖注入上下文中,您不希望显式地创建示例。您的代码显示了缺点:
a)引入紧耦合(new MyType()
b)类声明了仅在内部构造其他类型时才需要的依赖项。那些创建的类型对于创建类的外部是不可见的(依赖项隐藏)
c)延展性损失:改变显式构造的类型或修改其依赖关系将需要修改创建类型。

    • 示例:**
class ClassA
{
  public ClassA(IClassC classCInstance)
  {
    this.ClassBDependency = classCInstance;
  }

  private void OnButtonClick(object sender, EventArgs e)
  {
    string userName = "Some User";
    IClassB dynamicInstance = new ClassB(userName, this.ClassBDependency);
  }
}

溶液
解决方案是通过将依赖定义为类依赖来使依赖可见,换句话说,让IoC容器为您示例化类型。
如果需要动态示例化依赖关系,则必须使用 * Abstract Factory * 模式:

    • 版本A**使用Func<T>委托作为工厂。Func<T>还允许传递动态参数,如用户输入数据:
class ClassA
{
  private Func<IClassB> ClassBFactory { get; }

  public ClassA (Func<string, IClassB> classBFactory)
  {
    this.ClassBFactory = classBFactory;
  }

  private void OnButtonClick(object sender, EventArgs e)
  {
    string userName = "Some User";
    IClassB dynamicInstance = this.ClassBFactory.Invoke(userName);
  }
}
    • 版本B**实现了 * 抽象工厂 * 模式:
interface IClassBFactory
{
  IClassB Create(string userName);
}

// The concrete type that the IoC container will inject later
class ClassBFactory : IClassBFactory
{
  private IClassC ClassBDependency { get; }

  // The factory alone knows the dependencies of the product.
  // A factory can also depend on other abstract factories.
  public ClassBFactory(IClassC classCInstance)
  {
    this.ClassBDependency = classCInstance;
  }

  public IClassB Create(string userName) 
    => new ClassB(this.ClassBDependency);
}

// The type that depends on ClassB and needs to create instances of it dynamically
class A
{
  private IClassBFactory ClassBFactory { get; }

  public ClassA(IClassBFactory classBFactory)
  {
    this.ClassBFactory = classBFactory;
  }

  private void OnButtonClick(object sender, EventArgs e)
  {
    string userName = "Some User";
    IClassB dynamicInstance = this.ClassBFactory.Create(userName);
  }
}

相关问题