InversionOfControl в новом проекте MVC3 - PullRequest
4 голосов
/ 06 декабря 2011

Я создаю сайт электронной коммерции, используя C #, MVC3, Entity Framework 4, мой первый опыт MVC3 и Entity Framework, поэтому я хочу обеспечить надежную архитектуру. В частности, я подвергаю сомнению мое использование Интерфейсов и Инверсии Зависимостей, поскольку они относятся к уровням Services и Repository. Для краткости и ясности я сосредоточусь на одной области системы, системе Cart.

Вот пример обращения интерфейса, который PluralSite использует для объяснения типичной тенденции создания интерфейсов без учета надлежащих зависимостей.

Допустим, у вас есть интерфейс IKangaroo, от которого зависит класс "BoxingMatch". Когда вы добавляете больше «боксеров», таких как IMikeTyson и IJoeBoxer, у вас теперь есть три разных интерфейса, по одному для каждого боксера, о которых «BoxingMatch» должен знать. IKangaroo, IMikeTyson и IJoeBoxer имеют только одну конкретную реализацию, что означает, что вам даже не нужны эти интерфейсы (вы также можете сделать BoxingMatch зависимым напрямую от конкретных классов Kangaroo, MikeTyson и JoeBoxer). Кроме того, даже не имеет смысла, что будет несколько реализаций IKangaroo или IMikeTyson. Таким образом, интерфейсы не нужны и не приносят никакой пользы архитектуре.

Инверсия зависимостей в этом примере приведет к тому, что «BoxingMatch» определит интерфейс, который будут использовать классы, которые он собирается использовать (Kangaroo, MikeTyson и JoeBoxer). Таким образом, «BoxingMatch» будет зависеть от интерфейса IBoxer, а Kangaroo, MikeTyson и JoeBoxer будут реализовывать IBoxer. Это инверсия, и она имеет смысл.

Теперь моя ситуация ... В конструкторе CartController введены два параметра зависимости: ICartService и IProductService). CartService принимает один введенный аргумент конструктора (ICartRepository). CartController вызывает AddItem () для CartService, а CartService вызывает AddItem () для CartRepository.

ICartRepository имеет две реализации: CartRepository (в основном веб-проекте) и TestCartRepository (в проекте Tests). ICartService имеет только одну реализацию.

Мои вопросы: как моя архитектура складывается с уроком в приведенном выше примере? Я действительно не понимаю, как мой CartController может быть менее связан, чем он уже есть с CartService. Контроллер зависит только от CartService, а не ICartRepository. Так что не похоже, что я могу инвертировать управление здесь, если CartController определит, какой интерфейс CartService и ICartRepository будут использовать, поскольку CartService и CartRepository полностью разные слои. Я прав?

Вниз на один уровень и тот же вопрос. CartService зависит от CartRepository. Применим ли вышеупомянутый принцип инверсии здесь? Или я уже перевернул зависимость, потребовав внедрить параметр ICartRepository в конструкторе CartService?

Итак, мой вопрос на самом деле: я сделал это "правильно"?

Будем благодарны за любые мысли или советы.

Мой код, для справки:

CartController:

//constructor
public CartController(ICartService cartService, IProductService productService)
{
    _cartService = cartService;
    _productService = productService;
}

public RedirectToRouteResult AddItem(Cart cart, int productId)
{
    var product = _productService.GetProduct(productId);
    if (product != null)
    {
        _cartService.AddItem(cart, product, 1);
    }
    return RedirectToAction("Index");
}

CartService (реализация):

//constructor
public CartService(ICartRepository repository)
{
    _repository = repository;
}

public void AddItem(Cart cart, Product product, int quantity)
{
    //simplified for brevity
    var cartProduct = _repository.CartProducts().SingleOrDefault(cp => cp.CartId == cart.CartId && cp.ProductId == product.ProductId);
    _repository.AddCartItem(cartProduct);
}

Хранилище корзины (реализация):

public void AddCartItem(CartProduct cartProduct)
{
    _context.CartProducts.Add(cartProduct);
    _context.SaveChanges();
}

Ответы [ 2 ]

4 голосов
/ 06 декабря 2011

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

Используя контейнер IoC, вы можете управлять временем жизни объекта (например, заставляя объекты жить в течение жизни одного веб-запроса).

Контейнер IoC также обрабатывает рекурсивные зависимости.Если вы создаете IMikeTyson, а IMikeTyson зависит от IBoxingShorts, то вам не нужно создавать экземпляры боксерских шорт, они приходят бесплатно.

Все это игнорирует основную причину использования IoC, и это делает модультестирование проще.Используя интерфейсы, вы можете предоставить имитирующие IMikeTysons для своих модульных тестов BoxingMatch, чтобы вы могли кормить известные значения BoxingMatch и не нуждаться в сбросе базы данных после каждого теста.

И еще около 100 других преимуществчто программирование на основе IoC и интерфейса приносит на стол.

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

1 голос
/ 06 декабря 2011

Это, вероятно, не отвечает на все ваши вопросы, но если в какой-то момент вы намереваетесь разделить процесс между уровнями Controller и Service, например, с помощью WCF, то ваши I * Services станут контрактами на обслуживание WCF - это укрепитрешение о соединении слоев через интерфейсы, а не напрямую связанные с классами.

В этом случае можно добавить дополнительный фасад / косвенное указание, часто называемое агентом службы, в результате чего «точные» интерфейсы службы SOA скрыты отконтроллер.Это позволило бы заменить уровень обслуживания одним из аналогичных функций, но с использованием разных интерфейсов обслуживания.

Ваша инверсия выглядит хорошо - ваши уровни зависят от абстракции (интерфейсов) и скрывают реализацию, поэтому * Служба не будетне следует знать, что хранилище использует Entity Framework, NHibernate или DataSets, и при этом Контроллер не будет знать, что уровень Service использует хранилище, или напрямую обращается к базе данных (конструкторы внедрения не будут отображаться на интерфейсах, используемых клиентамислой, так что не беспокойтесь здесь).

...