开发者

Am I properly understanding DI/IoC? [closed]

开发者 https://www.devze.com 2023-02-02 09:43 出处:网络
Closed. This question needs to be more focused. It is not currently accepting answers. Want to improve this question? Update the question so it focuses on one problem only by editing this
Closed. This question needs to be more focused. It is not currently accepting answers.

Want to improve this question? Update the question so it focuses on one problem only by editing this post.

Closed 5 years ago.

Improve this question

I am currently attempting to learn the benefits of using an IoC container and familiarize myself with DI. I've started to use StructureMap since it seems rather simplistic and yet powerful.

I want to verify that my understanding of these concepts are correct. Let's assume the following rudimentary classes in an application (details ommited for brevity):

public class OrderService : IOrderService
{
    private IOrderRepository _repository;
    private ITaxCalculator _taxCalculator;
    private IShippingCalculator _shippingCalculator;

    public OrderService(IOrderRepository repository, 
        ITaxCalculator taxCalculator, 
        IShippingCalculator shippingCalculator)
    {
        this._repository = repository;
        this._shippingCalculator = shippingCalculator;
        this._taxCalculator = taxCalculator;
    }

    public IOrder Find(int id开发者_开发知识库) { return this._repository.Find(id); }
}

public class OrderRepository : IOrderRepository
{
    public IOrder Find(int id) { // ... }
}

public class StandardShippingCalculator : IShippingCalculator
{
    // ...
}

public class StandardTaxCalculator : ITaxCalculator
{
    private ITaxSpecification _specification;

    public StandardTaxCalculator(ITaxSpecification specification)
    {
        this._specification = specification;
    }
}

First and foremost, the Dependency Inversion Principle states that since the OrderService is a "high-level" module it houldn't depend on the implementation details of anything lower level, it should just have references to those classes and be able to ask them to do their thing without having to know what it's doing, and the consuming code should be responsible for creating and handing these preconfigured modules to it. Is that right? Therefore, DI keeps these classes loosely coupled so that they don't have to know the exact way a method on that dependency is being called, simply that it will be called and do whatever it needs to do - the OrderService doesn't care if the Repository is querying XML, or using NHibernate or EF or even raw DataSets; it just knows that it can call into the Repository, tell it to find a Order with an ID of 42, and the repository will know what to do.

It's also my understanding that an IoC container, StructureMap in this case, provides a benefit by not forcing us to make sure we're creating all these dependencies by hand and passing them in. For example, the Main method of a trivial app using the above code might have:

static void Main(string[] args)
{
    IOrderService service = new OrderService(
        new OrderRepository(), new StandardShippingService(), 
        new StandardTaxService(new StandardTaxSpecification()));

    IOrder order = service.Find(42);

    // Do something with order...
}

which is revoltingly ugly with all the news to set it up; even if I created variables it's still ugly. The use of the IoC container lets me avoid all of this, and in the case of StructureMap it would become:

static void Main(string[] args)
{
    ObjectFactory.Initialize(x =>
    {
        x.For<IOrderRepository>().Use<OrderRepository>();
        x.For<IOrderService>().Use<OrderService>();
        x.For<IOrder>().Use<Order>();
        x.For<IShippingCalculator>()
                        .Use<StandardShippingCalculator>();
        x.For<ITaxCalculator>().Use<StandardTaxCalculator>();
        x.For<ITaxSpecification>()
                        .Use<StandardTaxSpecification>();
    });

    IOrderService service =
                ObjectFactory.GetInstance<IOrderService>();
    IOrder order = service.Find(42);
    // do stuff with order...
}

Which is much cleaner, easier to maintain, and lets me just sub out the concrete class for, say, a Mock if I was writing a unit test. In short, the benefit is that it decouples everything even more to where I don't even have to care (in the calling code, that is) what a particular class depends on, I can just create one using the container and let it do it's thing and ensure the consuming code only knows what it needs to - for instance in a real app if the Controller is calling the service, it doesn't need to know about Repositories or Calculators or Specifications, all it needs to know is to use the OrderService to do something with an Order.

Is this understand correct? On that note, there is a few things I'm not sure of yet:

  1. If you decide to use an IoC container, is it meant to be used everywhere in the application, only where you have many inverted dependencies to resolve, or only in the consumer? For example, in the OrderRepository if I'm using a concrete implementation and newing up an Order; would this class be using StructureMap as well to get the Order? This might be a somewhat silly question but all of the DI/IoC examples I've seen only focus on using it in the consuming client (e.g. a webpage), and never touch on using it elsewhere. It seems like it's an all-or-nothing approach: If you're going to use an IoC container then it gets used everywhere; you are essentially replacing any call to new SomeObject(); with, in this case, ObjectFactory.GetInstance<ISomeObject>();

  2. Is it considered good or bad to have every class (where possible, of course) derive from an interface whether or not there's a need to use DI/IoC or something like mocking it? I've seen many code examples where every single class that's not a built-in one has an interface behind it, and while I can see the benefits and possible future-proofing of doing this and I suppose that following TDD or BDD might be a factor here as using those methodologies will usually tell you if you need an interface for a class, but I've seen and spoken to many people who, TDD or not, feel that you should never define an object's type as a concrete class; it should always be an interface or abstract class as the underlying type. That seems like it's a case of the "Needless Complexity" code smell, not to mention a violation of YAGNI.


Both your questions concern controversial topics, but I'll weigh in on my side of the debates.

If you decide to use an IoC container, is it meant to be used everywhere in the application, only where you have many inverted dependencies to resolve, or only in the consumer?

Your top-level application (the consumer) is the only component that should know about your dependency injection framework. You don't need to replace new throughout your codebase because each object should have all the dependency instances it needs to do its job (Miško Hevery's "Dependency Injection Myth: Reference Passing" was what finally drove this home for me).

Is it considered good or bad to have every class (where possible, of course) derive from an interface whether or not there's a need to use DI/IoC or something like mocking it?

The rest of this question shows that you already know the answer to this: only build interfaces to create more suitable abstractions (than the concrete class in question) or to provide some other value.


I'm currently working deeply with DI/IoC in C#/WinForm on VisualStudio2010/12. My choise fall on Castle Windsor, but also StructureMap but is not important what IoCC you use.

For a very detailed answer I suggest you to read "Dependency Injection in .NET" by Mark Seemann. That is a good book even if you do not develop in .NET.

Regarding your questions:

  1. A DI CONTAINER is a library that you can potentially use from wherever you would like—but that doesn’t mean that you should. Although you can spread out the use of the container so that it permeates a large percentage of your classes, you should instead concentrate it into a single area of your application.

    This place is called the COMPOSITION ROOT and you should only use a DI CONTAINER from within that place. The COMPOSITION ROOT of the application should be located in the application’s root so that it can properly compose the application. You shouldn’t attempt to compose classes in any of the modules because that approach limits your options. All classes in application modules should use CONSTRUCTOR INJECTION (or, in rare cases, one of the other patterns like Property Injection) and leave it up to the COMPOSITION ROOT to compose the application’s object graph. Any DI CONTAINER call should be limited to the COMPOSITION ROOT.

    The COMPOSITION ROOT can be spread out across a few classes. This is expected—the important thing is that all classes are contained in the same module, which preferebly is the application root.

  2. You are not required to use interfaces everywhere. You can also use concrete classes. Certainly interfaces provide a higher level of abstraction, but you have to consider if you need in your project. For example, it is good practice to use the interface in the business logic in your application.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号