Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enforcing dependencies in IoC via a constructor?

I'm trying to come to terms with using IoC/Dependency Injection while at the same time programming to contracts rather than specific classes. The dilemma I'm having is the tension between:

  1. Do program to interfaces for IoC: I started out with IoC relying heavily on interfaces. Judging by Spring's sample projects, interfaces are the way to go when programing to a contract with IoC.

  2. ( ... although abstract classes generally preferred: the main drawback of interfaces is that they are much less flexible than classes when it comes to allowing for evolution of APIs )

  3. Do make class dependencies explicit via constructor My gut feeling is that it's good programming practice to pass dependencies in to a class's constructor. Indeed, this is dependency injection.

  4. ... except you can't enforce constructor signature in interfaces/abstract clases: Neither interfaces or nor abstract classes allow for defining a constructor signature ( easily / elegantly ). See also Framework Design Guidelines section 4.4: DO NOT define public or protected internal constructors in abstract types. ... Constructors should be public only if users will need to create instances of the type.

This question is related to a previous stackoverflow question: Interface defining a constructor signature?

But my question is:

Since you can't define a constructor in a C# interface/abstract class, as the question above asks, on a practical level:

How do you reconcile this with the sensible practice of passing dependencies in via a constructor?

Edit: Thank you for the answers. I'm hoping for some insight on what I should do in this case. Just not use contructor args? Use some sort of Init() method that does take the dependencies? Edit2: Thanks for the great answers, very helpful.

like image 661
Jeffrey Knight Avatar asked Apr 16 '09 15:04

Jeffrey Knight


2 Answers

I always think this is easier to explain with a (made up) example...

Imagine you have an ICustomerRepository interface, an IShoppingCartRepository interface and an ICheckout interface. You have concrete implementations of those interfaces - CustomerRepository, ShoppingCartRepository, and CheckoutService.

Your CheckoutService concrete class has a constructor that takes an ICustomerRepository and an IShoppingCartRepository - e.g.

public CheckoutService(ICustomerRepository customerRepository, IShoppingCartRepository shoppingCartRepository)
{
  // Set fields for use in some methods later...
  _customerRepository = customerRepository;
  _shoppingCartRepository = shoppingCartRepository;
}

Then, when you want an ICheckoutService implementation to do some work with, you tell your IoC container which concrete class it should use for each interface type and ask it to build you an ICheckoutService. Your IoC container will go and build your classes for you, injecting the correct concrete classes into the constructor of your CheckoutService. It will also build dependencies all the way down the class heirarchy here, so if, for example your ShoppingCartRepository takes an IDatabaseSession interface in the constructor, your IoC container will inject that dependency too, as long as you have told it which concrete class to use for your IDatabaseService.

Here's some code you might use when configuring (for example) StructureMap as your IoC container (this code would typically be called during app startup):

public class AppRegistry : Registry
{
  public AppRegistry()
  {
    ForRequestedType<ICheckoutService>().TheDefaultIsConcreteType<CheckoutService>();
    ForRequestedType<ICustomerRepository>().TheDefaultIsConcreteType<CustomerRepository>();
    // etc...
  }
}

Then to get an instance of ICheckoutService built up and ready to go, with all the dependencies passed into the constructor for you, you would use something like:

var checkoutService = ObjectFactory.GetInstance<ICheckoutService>();

I hope that makes sense!

like image 146
Steve Willcock Avatar answered Oct 12 '22 23:10

Steve Willcock


Your IoC container must construct an object from a concrete type, even though what you're passing around is an interface. Your constructor is not a behavior or state contract, so it does not belong in an interface or as a public member of your abstract class.

A constructor is an implementation detail, so you do not need to separate its definition from the concrete class.

like image 39
Michael Meadows Avatar answered Oct 13 '22 01:10

Michael Meadows