шаблон поиска службы против фабрики агрегатов служб при наследовании - PullRequest
0 голосов
/ 28 октября 2019

Скажем, у меня есть базовый класс, которому нужна куча сервисов. И у этого базового класса есть несколько производных классов.

class BaseClass
{
    BaseClass(IServiceA srvA, IServiceB srvB) { ... }
}

Чтобы наследовать от этого базового класса с помощью DI, мне нужно повторить параметры конструктора в производном классе:

class DerivedClassA
{
    DerivedClassA(IServiceA srvA, IServiceB srvB) : base(srvA, srvB) {}
}

Когда позжебазовый класс нуждается в новой зависимости, мне нужно будет изменить базовый класс, а также производный класс. Представьте себе кошмар, когда есть несколько производных классов.

Я провел некоторое исследование, и, кажется, есть решение этой проблемы, которое представляет собой фабрику агрегатов услуг (или похожий термин):

interface IServiceFactory
{
    IServiceA getSrvA();
    IServiceB getSrvB();
}

class ServiceFactory : IServiceFactory
{
     public ServiceFactory(IServiceA srvA, IServiceB srvB) { ... }

     public IServiceA IServiceFactory.getSrvA() => this._srvA;

     public IServiceB IServiceFactory.getSrvB() => this._srvB;
}

class BaseClass
{
    BaseClass(IServiceFactory factory)
    {
        this._srvA = factory.getSrvA();
        this._srvB = factory.getSrvB();
    }
}

class DerivedClassA : BaseClass
{
    DerivedClassA(IServiceFactory factory) : base(factory) { ... }
}

Таким образом, мне нужно только изменять ServiceFactory каждый раз, когда мне нужно добавить дополнительные классы в классы, не меняя их.

Но тогда есть IServiceProvider, который является контейнером DI по умолчанию в.net core. Так что я могу сделать:

class BaseClass
{
    public BaseClass(IServiceProvider provider)
    {
        this._srvA = provider.GetRequiredService<IServiceA>();
        this._srvB = provider.GetRequiredService<IServiceB>();
    }
}

class DerivedClassA : BaseClass
{
    public DerivedClassA(IServiceProvider provider) : base(provider) {}
}

Он выглядит так же, как фабрика агрегатов сервисов, но люди говорят, что это прямой шаблон для прямого использования IServiceProvider. Так что я в замешательстве. Какой вариант выбрать?

1 Ответ

2 голосов
/ 28 октября 2019

Скажем, у меня есть базовый класс, которому нужно несколько сервисов. И у этого базового класса есть некоторые производные классы.

Перестал читать здесь, и вы должны иметь тожеЭто четкий показатель того, что ваш базовый класс делает слишком много. Это нарушает SRP (принцип единой ответственности) из SOLID .

Внедрение в конструктор делает ошибки проектирования (такие как нарушения SOLID) очень очевидными, и вы должны обратить на это внимание, поскольку оно говорит вам, когда у вашего класса слишком много обязанностей (класс пытается сделать слишком много).

Прежде всего, вам нужно подумать, зачем вашему базовому классу нужны эти зависимости, и изменить это. Ваш вопрос - способ абстрагироваться, поэтому не могу сказать, почему вашему классу может понадобиться так много зависимостей.

Если вашему классу действительно нужны все эти зависимости, рассмотрите возможность использования фасада. Фасад - это еще один класс, который скрывает сложность и зависимости за простым в использовании интерфейсом, тем самым уменьшая количество зависимостей. См. Рефакторинг для агрегированных сервисов Марка Симанна .

Фасад - это больше, чем просто класс, который предлагает легкий доступ к другим зависимостям (т. Е. Класс, имеющий только 5 свойств, которые представляют другие сервисы). Фасад будет скрывать зависимости, если это возможно, и предлагать методы для легкого доступа к функциональности, стоящей за ним, то есть метод .GetDrives может перечислять аппаратные устройства и сканировать его на наличие разделов и прослушивать каждый правильно отформатированный раздел, чтобы его не нужно было повторятькод вызова.

Представьте, что у вас есть такие услуги, как IMemoryService, ICpuService, IDiskService, INetworkService, и внедрите их во все ваши базовые классы. Это много зависимостей.

Вместо этого у вас может быть интерфейс IComputerServices, который дает вам доступ ко всем этим службам, скрытым с помощью простых в использовании API / методов / свойств (т. Е. Метод IComputerServices.CopyFileTo, который абстрагирует всеОперации копирования выполняются одним простым в использовании методом и внедряются в ваши службы. Теперь вы можете скопировать файл, например computerServices.CopyFileTo(sourceFile, destinationFile).

Если появляется новая зависимость, например IDisplayService, вы просто добавляете ееуслуга фасада, и она будет доступна во всех ваших классах, которые также вводят IComputerServices.

...