Pass Around Ginormous Context Objects

October 27th, 2008 · 11 Comments ·

This question was submitted by one of the readers, and it shows a common questions people ask when they are coming up to speed when trying to write testable code….

About “Pass[ing] around ginormous context objects” (http://misko.hevery.com/2008/07/24/how-to-write-3v1l-untestable-code/) – this situation comes up for me when [I] trying to get configuration settings into my application. I want objects deeper in the system to have some configurable values which come from outside the system, and I don’t want everyone along the way to know about what those configurable values are. How do you deal with this case? I’m thinking about a collection of console utilities which take settings like number of retries for different actions, timeouts to allow, logging levels, and other things. Some settings will be of interest to many components and others only to one, and it seems like if I decompose them then whomever handles the construction will have to know a lot about what settings classes know about and don’t know about.

// I would hate the class constructing the
// FileCopier to know it is interested in
// all of these!
FileCopier copier = new FileCopier(logger, 
           copierSettings, generalTimeouts);

// I end up doing this:
FileCopier copier = new FileCopier(configuration);
// and inside..
FileCopier(Configuration configuration) {
  X = configuration.GetSetting(“X”, DefaultValue);
  …
}

Lets look into each of these in turn:

  1. “I would hate the class constructing the FileCopier to know it is interested in all of these!” Why is this a problem? as discussed in ‘Breaking the Law of Demeter is Like Looking for a Needle in the Haystack‘ When you ask for things explicitly it makes it easy to pass in test-doubles for these dependencies and hence it makes the code testable. Not only is the code testable it is easy to understand. If I am new to the code-base I look at the constructor and I see that logger, copierSettings and timeouts are things which I may be interested in. On the other hand passing in a Context object it is hard for me to know which parts from the Context object the class-under-test is interested in. It also makes testing harder. Now I have to create Mock Context and override all of the getLogger(), getCoppierSettings() and so on for these methods to return the real test-doubles of interest. But How do I know what needs to be mocked out? I need to read the code, I can’t just look at the constructor and know. But there is more: Suppose I want to reuse FileCopier class in a different project. In that case I need to reuse all of the compile-time dependant classes as well. In this case it is the Context. But context is a kitchen sink and it knows about everything in your application which means I have to resuse those objects as well. So the code with Context objects is not reusable/re-compilable.
  2. The place where our mental model diverged is in this sentence: “I don’t want everyone along the way to know about what those configurable values are.” The fear is that in order to call the constructor the caller will have to know about the parameters, and its caller will have to know and so on. We talked about this in ‘Dependency Injection Myth: Reference Passing‘. However, we are making one more mistake. We are mixing object creation (and look up) with application logic, see: ‘How to Think About the “new” Operator with Respect to Unit Testing‘. The basic mistake people make at this point is that they assume that the object construction graph is same as object instantiation graph. This assumption is mostly true only if you sprinkle new-operators with your application logic. If the new operator is removed from application logic than your application has two phases. Phase (1) wire the objects together in order to instantiate the application. In Phase (1) all we do is call the new operators and pass in the right dependencies. We do not call any methods! In phase (2) we execute the application, here we stay away from the new-operators and call methods so that the objects can collaborate. Once the application is constructed we no longer need to pass the configuration information around. We simply execute methods on FileCopier.
  3. Loging is a Singleton and we have discussed Singletons here, here and here. The root problem is that Singleton (the design pattern) is the global state and global state sucks, from testability, maintainability, and few other -bilities. So should we pass in the Logger through the constructor. Well that depends… If you want to assert that under some conditions a specific log message is produced than you have no choice but to pass it in. On the other hand, if you just want logging, and don’t want to test that it works correctly, than you can do Logger.getLogger(Class.class). This does not make Singletons OK, but it is benign-kind-of-evil. The reason is that with Logger the information only flows one way: Into the Logger. Your application does not read data from the Logger and more importantly does not behave any differently depending on if the Logger is turned-on or turned-off (It behaves different only in the sense that things don’t get printed, but not in the sense that your application now computes a different answer.) Most Singletons do not fall into this rare category where we push data only in, and hence most Singletons are a nightmare to test.

NOTE: when I say Singleton I mean the design anti-pattern with a global instance field as in private static Singleton instance = new Singleton(); This is what makes the Singleton a global. To be contrasted with singleton (lower case ‘s’) as in a single instance of something without a global instance field.

Tags: Uncategorized

11 responses so far ↓

  • Gerard // Oct 28, 2008 at 5:40 am

    Perhaps you could quickly write what the FileCopier factory might look like in this case, and how you might use it.

  • Artem B. // Nov 12, 2008 at 11:25 pm

    This is the point where I don’t get it.

    Assume that you have a WebServer class that controls a web server instance. It has a constructer like

    WebServer (Configuration config) {
    this.port = config.getPort();
    }

    This breaks the law of Demeter and I want to refator it. But hey, I can’t move the port to the constructor. This way I will be mixing the service-objects with value objects. And my application factory will have to know how to get the port configuration out of a config file which means doing work in the factory.

    Another way to refactor it is to move the configuration options to the start() method like this.

    // This is a test code, not a production code
    WebServer server = new WebServer();
    server.start(8080);

    But this obligates the callers to know what port they need to start the server on, which again breaks the law of Demeter, because they shouldn’t need any configuration options they do not directly use.

    Please, advice me on a way to refactor the code with Dependency Injection and the Law of Demeter in mind.

  • misko // Nov 13, 2008 at 11:06 am

    Hi Artem,

    Excellent question. Have a look at: http://misko.hevery.com/2008/09/30/to-new-or-not-to-new/ The key question is can a Dependency Injection Framework such as GUICE know what do you mean when you ask for @Port int portNumber. And the answer is yes, there is not ambiguity. (You need the annotation since there are lot of ints but only one @Port int)

    So the prfered way is to ask for the port number in the constructor. If you think about it it makes perfect sense. In order to instantiate a WebServer you need to know the port number. So ask for it in constructor.

    I think the trouble you can ge into is when you ask for things which Dependency Injection Frameworks can not provide. Such as a Song. Well there are houndreds of Songs in the system, which one do you want? And annotation does not help since the Song list is dynamic.

    – Misko

  • Artem B // Nov 13, 2008 at 11:46 am

    Misko,

    I have read the post you recommended and it says:

    “However, Injectable can never ask for a non-Injectable (Newable) in its constructor.”

    As far as I understand, the port is NOT an injectable, but the WebServer is. The problem I have is exactly the problem you mention here: “when you ask for things which Dependency Injection Frameworks can not provide”. What do I do then?

    thanx, Artem.

  • misko // Nov 13, 2008 at 12:04 pm

    Port number is injectable. You can very well make a binding for it and ask for it in constructor. I think I should clarify this more in future posts. To put it another way if you ask for a port in constructor there is never an ambiguity about what you are looking for. There is only one port.

    to contrast this with something like CreditCard. That is not really injectable since in a system you can have a lot of credit cards and the number of them is dynamic at run-time.

    – Misko

  • Enrico M. // Jan 31, 2009 at 11:28 am

    Hi Misko,first of all I have to say thank you. With your blog posts about DI you completely changed my view on object creation / instantiation (which also implies I’m new to the topic).It’s been a while since your last response to that post, but I hope you are still reading this. The thing is, even after thoroughly considering all your posts about DI, I still cannot see a solution to Artem’s question.The question it comes down to for me is: how could you support a user-configured port for the web-server. Let’s say we have a configuration file for configuring the port for the web server. Sticking to the rule that in the creation-phase only object creation should be done, there’s no way to read the configuration to determine the port. Even a DI framework could never provide this information, as it cannot be bound to an injectable without reading the configuration file before.So how to solve this? The solution I came up with is introducing another step in my main method, i.e. reading the configuration before creation phase and requiring the configuration object in the constructor of the application factory. This is somewhat bad as I now have a “new Properties()” in my main method. However, imho, this does not reduce testability, as each object still declares it’s dependencies. So, besides the factory now calls … = new WebServer(config.get(“port”));you can still instantiate the WebServer for a test like this:… = new WebServer([some int]);I’m still not contented with this version. How would you solve this issue, Misko? Please note I’m refraining from using a DI framework because the middleware I’m developing is not too complex and other developers need to understand the code at first sight. Thank you!Enrico

  • misko // Jan 31, 2009 at 11:39 am

    @ Artem,

    You are very quick! You are right

    WebServer (Configuration config) {
    this.port = config.getPort();
    }

    This breaks the law of demeter and I would never do this. However this works just fine.

    WebServer (int Port) {
    this.port = port;
    }

    But as you correctly pointed out now we are mixing service and value objects. The reason this is a problem is that DI frameworks will not know how to inject ‘int’ as there are a lot of ints to choose from. But if you are doing manual DI this is not a problem. So to make DI frameworks happy we need one more step.

    WebServer (@named(“httpPort”) int Port) {
    this.port = port;
    }

    By adding the annotation to the parameter you can now write this in your GUICE module

    bind(int.class).annatatedWith(name(“httpPort”).to(8080);

    and everyone is happy.

  • Enrico M. // Jan 31, 2009 at 11:54 am

    Wow, that was quick. I’m not sure if you replied to my post because you addressed it to Artem, but I assume so ;)

    I understand that you could do a binding like “bind(int.class).annotatedWith(name(”httpPort”).to(8080);
    and with manual DI you could simply hard-code the port number. But the question that remains is:

    When you have a configuration file for specifying the port – where in
    your application do you read the configuration file (needs to be done
    before binding).

    Could you please clarify this. Thank you!!!

  • misko // Jan 31, 2009 at 12:05 pm

    Hi Enrico,

    Check out this example:
    It is only half finished, but it has the idea. I do the flag parsing in the GUICE module and than i bind the configuration to the primitives in the module itself.
    if you are doing manual DI than I would parse the flags into some configuration object and than pass the configuration object to the Factory. The factory would than extract the data from the configuration object and injected into the constructor which needed it.
    – Misko
  • Enrico M. // Jan 31, 2009 at 12:09 pm

    You just made my day! This is exactly the info I needed. Thank you for your fast and helpful responses.Enrico

  • We need an API - Page 9 // Mar 30, 2012 at 10:35 am

    [...] a bit? – Stack Overflow that being said, be wary that you don't abuse the context pattern as well. Pass Around Ginormous Context Objects note that sometimes its easier and more streamlined to code as if you're working with global [...]

Leave a Comment