Следует ли использовать AggregateService в качестве контейнера для хранения общих зависимостей? - PullRequest
4 голосов
/ 26 января 2012

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

public Interface ICommonControllerDependencies
{
    ICookieService CookieService{ get; }
    IAuthenticationService AuthenticationService{ get; }
}

public abstract class BaseController : Controller
{
    protected readonly ICommonControllerDependencies Dependencies;

    protected BaseController(ICommonControllerDependencies dependencies)
    {
        this.Dependencies = dependencies;
    }

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.Controller.ViewData[UserName] = Dependencies.AuthenticationService.UserName;

        base.OnActionExecuting(filterContext);            
    }
     protected ActionResult RedirectToDefaultPage()
    {
        var page = Dependencies.CookieService.GetDefaultPageCookie(Request, RouteData);
        //Do Something
    }  

}

public class BaseReportController : BaseController
{
    public BaseReportController(ICommonControllerDependencies dependencies)
        : base(dependencies)
   {
        _cookieService = dependencies.CookieService;
   }
}

OR

public class BaseReportController : BaseController
{
    public BaseReportController(
        ICookieService cookieService, 
        ICommonControllerDependencies dependencies)
        : base(dependencies)
    {
        _cookieService = cookieService;
    }
}

Ответы [ 3 ]

2 голосов
/ 26 января 2012

AggregateService предназначен для того, чтобы изолировать ваш подкласс от изменений в суперклассе и разрешить изменения в суперклассе, не нарушая подклассы. Если вы выберете вариант 1, вы подключите подкласс к своему суперклассу, тем самым потеряв предполагаемую изоляцию.

Не зная больше о вашем дизайне, я бы выбрал вариант 2. Я не думаю, что могу ожидать, что ServiceA всегда будет там. Принимая явную зависимость от IServiceA в подклассе, этот класс может жить счастливо, не зная об изменениях, происходящих во внутренних органах суперкласса.

1 голос
/ 27 января 2012

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

Другими словами, шаблон Aggregate Service - это кодовый запах *.

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

Однако с такими инфраструктурами пользовательского интерфейса, как ASP.NET MVC, это не всегда легко, поскольку большинство самих сред поддерживают наследование.

Попробуйте извлечь логику базового класса для разделения зависимостей, особенно когда этот код в базовом классе используется не всеми подтипами. Например, вы можете изменить метод RedirectToDefaultPage на следующий:

public class DefaultPageRedirector
{
    private readonly ICookieService cookieService;

    public DefaultPageRedirector(ICookieService cookieService)
    {
        this.cookieService = cookieService;
    }

    public ActionResult RedirectToDefaultPage(
        Controller controller)
    {
        var page = this.cookieService.GetDefaultPageCookie(
            controller.Request, controller.RouteData);

        //Do Something
    }
}

Таким образом, вы можете вводить DefaultPageRedirector только в Controller типы, которые действительно нуждаются в этом.

Для OnActionExecuting он отличается, так как он вызывается для каждого подтипа. Однако свойство ViewData["UserName"], вероятно, будет использоваться не всеми View в системе, и в этом случае вам следует рассмотреть возможность возврата UserName как части (статически типизированного) объекта ViewModel. Если он используется большинством представлений, вы можете рассмотреть возможность использования частичного представления, поскольку в ваших представлениях может быть повторяющийся код (принцип DRY действует не только для кода, но и для каждой части в система).

Это, вероятно, избавит от большей части кода в базовом классе, что, вероятно, также удалит большинство зависимостей в базе (если не все).

* Обратите внимание, что запах кода не означает, что всегда проблема. Процитируем Википедию: «Запах кода - это любой признак в исходном коде программы, который , возможно, указывает на более серьезную проблему».

0 голосов
/ 26 января 2012

Я думаю, что это зависит от того, зарегистрировали ли вы IserviceA в контейнере или нет, и от того, как вы ожидаете развития вашего дизайна.

Лично я бы выбрал первый вариант, если вы не ожидаете удаления serviceA из интерфейса ICommonControllerDependencies.Кажется, проще ввести один интерфейс.Если вы думаете, что можете удалить serviceA в какой-то момент, я бы рассмотрел второй вариант, поскольку он удаляет связь между службой и зависимостями контроллера в BaseReportController.Я думаю, что второй вариант более точно следует закону Деметры .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...