次の方法で共有


Code As Dependency Configuration

In his article on Inversion of Control and Dependency Injection, Martin Fowler has a quite interesting section towards the end where he talks about how to configure loosely coupled systems. One of his points is that in some cases, it makes more sense to tie it all together using code than by using a configuration system.

When I introduce the concept of Code As Configuration for most people, they seem to instantly stop listening to what I'm saying. Configuration (the ability to change the behavior of the application without changing its code base) is good, so why would you ever want not to enable configuration? In .NET, this notion is particularly strong because System.Configuration makes it so easy to create a rich configuration object model backed by XML configuration files.

With Dependency Injection, the configurable entities are types (defined in various assemblies). Because of Dependency Injection, each library in your application can be compiled in isolation, since its volatile dependencies are being injected at run-time. A design pattern such as Constructor Injection is fairly simple and intuitive to use as long as you use it in isolation during development (e.g. while unit testing), but when it comes to tying everything together in an executable application, all these dependencies must be loaded into an object hierarchy such as this one:

In this example, each class is defined in a separate assembly. Note that the arrows indicate usage, not dependencies. For the application to run in the desired configuration, ClassC must consume an instance of ClassE, and so forth. However, ClassC only knows about Abstraction5, which is being injected into it via its constructor:

 public class ClassC : Abstraction3
 {
     private Abstraction5 a5_;
  
     public ClassC(Abstraction5 a5)
         : base()
     {
         this.a5_ = a5;
     }
  
     protected override string DoStuffCore()
     {
         StringBuilder builder = new StringBuilder();
         builder.AppendLine("I am really Class C. My dependency says:");
         builder.Append(this.a5_.DoStuff());
         return builder.ToString();
     }
 }

The other classes are implemented in a similar way; ClassB, for example, only communicates with Abstraction3 and Abstraction4 and doesn't have any knowledge of ClassD or ClassC (let alone that in this configuration, ClassC consumes ClassE).

The executable must somehow construct the correct object graph. A common approach is to use the app.config file to define this hierarchy (for an example, Spring.NET supports this option). To properly identify which type to instantiate when an abstraction is requested, an assembly-qualified type name is provided in the configuration file, and reflection is used to create instances of the configured types.

This very common approach comes with a rather painful drawback: Since the executable application has no dependencies on any of its servers, none of the containing assemblies are automatically copied to the output directory. Although the application compiles, it crashes when executed because Fusion is unable to locate the assemblies specified in the configuration file.

To work around this, you can either put all of your assemblies in the GAC or copy them to the relevant output directory, but both of these options are either repetitive or requires automation. In any case, this drawback can be a bit counter-productive during development.

Another approach is simply to code the desired configuration into the top-level executable. Whether this is a viable option depends on your reason for isolating your volatile dependencies in the first place. If your motivation is genuinely to support plug-ins or other extensibility models, configuration-driven Dependency Injection is certainly the right approach, and hard-coding a particular configuration is not an option. For other scenarios, however, this solves the problem of managing and deploying the correct assemblies.

In my example, the client will create the entire object hierarchy like this:

 static void Main(string[] args)
 {
     Abstraction1 a1 = Program.CreateAbstraction1();
     Abstraction2 a2 = Program.CreateAbstraction2();
  
     Console.WriteLine(a1.DoStuff());
     Console.WriteLine(a2.DoStuff());
  
     Console.ReadKey(true);
 }
  
 private static Abstraction1 CreateAbstraction1()
 {
     return new ClassA();
 }
  
 private static Abstraction2 CreateAbstraction2()
 {
     Abstraction5 a5 = new ClassE();
     Abstraction4 a4 = new ClassD();
     Abstraction3 a3 = new ClassC(a5);
     Abstraction2 a2 = new ClassB(a3, a4);
     return a2;
 }

Since all implementations use Constructor Injection, the client can construct the entire object tree by starting with the leaves and working its way to the root of the tree. Notice that although this creates an object hierarchy as illustrated above, the dependency hierarchy is flattened:

Apart from the top-level executable, each assembly only has a dependency on an assembly containing the abstractions (base classes and interfaces). Since such an assembly is considered a stable dependency, isolation has been achieved for all libraries. The only assembly with volatile dependencies is the top-level executable, but that's commonly where your UI resides, so it's not going to be testable in any case (and should be kept as minimal as possible).

Since the top-level executable has all relevant dependencies for the desired configuration, all required assemblies will automatically be part of the build.

Obviously, if you ever need to run the application in a different configuration, you will need to change the code, compile and redeploy, but that's not particularly different from the pure configuration scenario. Even if you use a configuration file, you will still need to compile and deploy your new or changed modules. In my opionion, it's the overhead of actually deploying even a single file that carries the greatest cost; whether you copy one or all of your files is merely a question of network or disk transfer time.

For applications where you control the entire code base and no extensibility support is required, the Code As Configuration technique can make development teams more productive without sacrificing significant functionality.

Comments

  • Anonymous
    May 30, 2007
    Given that you're top level assembly has all the required concrete dependencies in order to use code instead of configuration, you could still call into something like Spring.net. (I'm just using your preconditions for code to work, in order to rebutt your "doesn't copy to output directory" argument.) There's also runtime object creation that DI frameworks provide, that are incredibly useful in scenarios like the one described here: http://udidahan.weblogs.us/2007/04/30/generic-validation/ You still haven't solved the abstract factory issue with your code example. For instance, how would you do: spring.GetObjectsOfType(typeof(IValidator<Customer>)); To get all the classes which should validate your customer object. Other than that, I agree that code, with its intellisense support is probably more convenient than XML for wiring dependencies.

  • Anonymous
    May 30, 2007
    Hi Udi Thank you for your comments. The point of this post was not to claim that Spring.NET doesn't support this technique, but just to describe the benefits of Code As Configuration. The reason I mentioned Spring.NET was just because this is a relatively well-known framework that (also) supports the XML configuration option, with the advantages and disadvantages this entails. I totally agree that you could use Spring.NET or other DI frameworks to achieve the same effect as the one I was aiming for - I just didn't want to make knowledge of a particular framework a precondition for reading the post, which is why I used Constructor Injection in my example. In this post, however, I wasn't trying to solve the Abstract Factory issue at all :) In the example outlined, an Abstract Factory shouldn't be allowed at all; if you want a list of IValidator<Customer>, you should take an instace of IList<IValidator<Customer>> as a constructor parameter. That's just the approach taken in this sample, and I'm not saying that you shouldn't use Abstract Factory at all, but for code consistency, I think it's a good idea to stick with a single strategy - either Constructor Injection, or Abstract Factory, or something else. In fact, my favorite is Provider Injection. When used for DI, Abstract Factory is very similar to Service Locator. Although I've been very fond of Service Locator in the past, these days I prefer Constructor or Provider Injection, because these patterns lets the API communicate intent more clearly. Maybe I should write a separate blog post on this subject?

  • Anonymous
    May 31, 2007
    I know this is a little OT, but it does apply to constructor injection. My question is why not have interfaces that support construtor contracts? Perhaps there is some deep reasoning and the universe might explode or there are other problems with this,  but I have seen many instances where this would be useful.

  • Anonymous
    May 31, 2007
    Hi Mike D Thank you for asking - although the universe will not explode, our solar system might disintegrate if we do something like this :) Joking aside, there are at least two answers to that question: One having to do with practical constrains, and one having to do with design and architecture. Let's do the first one first: Interfaces don't support the modelling of constructors, and public constructors in abstract classes don't make any sense, so pure constructors are out of the question. Then what about factory methods? Well, there's actually the Abstract Factory design pattern, which is probably the closest you can come to specifying a contract for object creation, but that still leaves the question of how to create the abstract factory itself. Basically, there's no support for such things in the BCL. From a design perspective, it also makes a lot of sense to separate object creation from object behavior. Consider my example in this post. ClassC uses Abstraction5 which is supplied to it in its constructor. ClassB incidentally uses ClassC because it uses Abstraction3. Let's say that I wanted to change the hierarchy by replacing ClassC with a self-contained class (let's call it ClassCPrime). ClassCPrime doesn't consume Abstraction5, so it only exposes a default constructor. In other words, ClassC's constructor has a different signature than the constructor of ClassCPrime. This is what allows me to dramatically change the service hierarchy in the example. To extend this further, you may have some service implementations that are actually Singletons. Since we are not making any assumption on how objects are created, we could perfectly well pass a Singleton as a parameter for a class that uses Constructor Injection. This flexibility is very powerful, and that's why we don't have interfaces that specifies how implementations are created. This subject is rather complex and could actually benefit from a more thorough treatment, including code examples and illustrations, but I hope I managed to answer your question to at least some extent; otherwise, feel free to ask again.

  • Anonymous
    May 31, 2007
    Thanks for taking the time to explain, and actually I do get your point. Whatever the technical term for hitting the wall is, I hit the wall all the time.  I try and code my way out only to find myself in another corner. DI is GOOD, however we have no means of explicitly defining a contract for dependencies. We can define a contract for behavior. Everytime I find myself in this place, jumping through hoops, to do what seems to be a simple task, I can't help wonder are we missing the forest for the trees. Yes you answered my question well in the context of the example, I see the mess interface constructors could create. What if there were a way to specify that contract A requires contract B without the hoops? I really like your blog and would like to see more on decoupling layers.

  • Anonymous
    June 01, 2007
    The comment has been removed

  • Anonymous
    June 02, 2007
    Is there not a need for decoupling between the DC, DiscountCalculator, and the DAC/WS versions, where the discounts are stored is not it's concern. in other words each implementation of the DC would have it's own DI for which the order is unaware of. IOrder requires IDiscountCalc DACDiscountCalc requires IDataContext WSDiscountCalc requires IWebServiceContext DACDC implements IDiscountCalc WSDC implements IDiscountCalc IDiscountCalc dc = DiscountFactory.Create() { DAC || WS } IOrder o = OrderFactory.Create(dc) When I look at the DI techniques and can't help but think that the language is missing convention to handle this. One good example is generics, when introduced much of the code we write started looking a feeling right. This pretty much eliminated the need for the ( new keyword as used in overloading ) due to the lack of conariant return types. While talking about generics, I just had the need for a new() constratint that would look like this: <T>Create() : where T new(IDiscountCalc) This is a generic method that can create an object that must have a dependency on IDiscountCalc.  I had to resort do declaring an interface IDiscountCalcDependent. <T>Create() : where T : IDiscountCalcDependent Wouldn't it make more since if there were a language feature with compiler checks that allowed you to specify that A depends on B. I totally get DI constructor and setter, both need to be there, I just see the problem as the language should allow you to specify a dependency. Thanks for the discussion...

  • Anonymous
    June 02, 2007
    Hi Mike If I understand what you are saying, you are not talking about chaining dependencies together in contracts (that's what I thought at first), but rather that you wish to be able to explicitly state that a particular implementation (Order) needs an instance of a particular abstraction (DiscountCalculator)? If this is the case, I can see that it quickly becomes quite complex when you take the Abstract Factory/Service Locator route. On the other hand, such semantics are automatically expressed by Constructor Injection, which is one of the reasons I like this pattern better. Your question inspired me to write a post about this subject - I'm not sure it answers your question, but I thought it was a worthwhile subject none the less :) Please take a look and let me know if you think I understood your question :)

  • Anonymous
    November 28, 2007
    It's no secret that I prefer unit tests over integration tests. Whenever it's possible to replace an

  • Anonymous
    February 18, 2008
    Inversion of Control (IoC) is often achieved by Dependency Injection and programming to interfaces and

  • Anonymous
    June 27, 2008
    In my previous post , I explained how to implement a WCF service without referencing WCF. In simple cases,

  • Anonymous
    July 08, 2008
    In my previous post , I explained how to unit test a WCF service with callbacks. Since the scenario involves

  • Anonymous
    July 11, 2008
    In the last couple of posts, I've demonstrated how to isolate implementation from WCF contract definition