23

This is a design decision that appears to come up quite a lot: how to pass context through a method that doesn't need it to a method that does. Is there a right answer or does it depend on the context.

Sample code that requires a solution

// needs the dependency
function baz(session) {
  session('baz');
}

// doesn't care about the dependency
function bar() {
  baz();
}

// needs the dependency
function foo(session) {
   session('foo')
   bar();
}

// creates the dependency
function start() {
  let session = new Session();
  foo(session);
}

Possible solutions

  • threadlocal
  • global
  • context object
  • pass the dependency through
  • curry baz and pass it into bar with the dependency set as the first arg
  • dependency injection

Examples of where is comes up

HTTP request processing

Context objects in the form of request attributes are often used: see expressjs, Java Servlets or .net's owin.

Logging

For Java logging folk often use globals / singletons. See the typical log4j / commons logging / java logging patterns.

Transactions

Thread locals are often used to keep a transaction or session associated with a chain of method calls to avoid needing to pass them as parameters to all the methods that don't need them.

Jamie McCrindle
  • 331
  • 2
  • 6

3 Answers3

11

The only fair answer is that it depends on your programming paradigm's idioms. If you're using OO, it's almost certainly incorrect to pass a dependency from method to method to method. It's a code smell in OO. In fact, that's one of the problems OO solves -- an object fixes a context. So in OO, one correct (there are always other ways) approach is to deliver the dependency via contructor or property. A commenter mentions "Dependency Injection" and that's perfectly legit, but it's not strictly necessary. Just provide the dependency so it's available as a member to foo and baz.

You mention currying, so I'll assume functional programming isn't out of the question. In that case, a philosophical equivalent of object context is closure. Any approach that, once again, fixes the dependency so it's available to dependents works just fine. Currying is one such approach (and it makes you sound smart). Just remember there are other ways to close over a dependency. Some of them are elegant and some of them awful.

Don't forget about Aspect-oriented programming. It seems to have fallen out of favor in the last few years, but its primary goal is solving exactly the problem you describe. In fact, the classic Aspect example is logging. In AOP, the dependency is added automatically after other code is written. AOP folks call this "weaving". Common aspects are woven into the code at the appropriate places. This makes your code easier to think about and is pretty darn cool, but it also adds a new testing burden. You'll need a way to determine your final artifacts are sound. AOP has answers for that as well, so don't feel intimidated.

Scant Roger
  • 9,038
  • 2
  • 29
  • 47
  • Claiming that passing parameters around within OO methods is a code smell is a highly controversial statement. I'd argue the complete opposite: encouraging mixing state and functionality within a class is one of the biggest mistakes made by the OO paradigm and avoiding it by injecting dependencies directly into methods, rather than via a constructor is a sign of a well designed piece of code, whether OO or not. – David Arno Nov 27 '15 at 17:21
  • 3
    @DavidArno I'd suggest using a different paradigm vs. concluding object statefulness is "one of the biggest mistakes make by the OO paradigm" and then circumventing the paradigm. I have nothing against almost any approach but generally dislike code where the author is fighting their tool. Private state is a distinguishing feature of OO. If you eschew that feature, you lose some of OO's power. – Scant Roger Nov 27 '15 at 22:21
  • 1
    @DavidArno A class that is all state and no functionality has no mechanism for enforcing invariant relationships in the state. Such a class is not at all OO. – Kevin Krumwiede Nov 28 '15 at 04:29
  • @KevinKrumwiede, to an extent, you have applied reducto ad absudium to my comment, but your point is still well made. Invariance on state is an important part of "moving on from OO" . So avoiding mixing functionality and state has to allow enough functionality in a state object as needed to achieve invariance (encapsulated fields set by the constructor and accessed via getters). – David Arno Nov 30 '15 at 09:17
  • @ScantRoger, I agree another paradigm can be adopted, namely the functional paradigm. Interestingly, most modern "OO" languages have a growing list of functional features and so it's possible to stick with those languages and adopt the function paradigm, without "fighting the tool". – David Arno Nov 30 '15 at 09:19
  • @DavidArno The functional features in modern OO languages are a separate paradigm -- a perfectly legitimate paradigm, but still a separate paradigm. That's why all three approaches above may be possible within a single language. What you seem to be arguing is that pure OO is now defunct given functional features. I don't believe that's correct. Both paradigms have their strengths and weaknesses. Sounds like you've been bitten by the functional bug and that's great. Just don't let it cloud your judgement about what's possible with other tools. – Scant Roger Nov 30 '15 at 13:51
  • 1
    ... Claiming that functional patterns are preferred to OO patterns is highly controversial ;) When I say "fighting the tool", I mean losing the richness or what's possible because of an over-zealous viewpoint. I don't know your background, but if Haskell/F#/whatever got you excited about pure functional programming, maybe try SmallTalk to get you excited about pure OO programming. To be clear, I'm not claiming that OO is better than functional. Not at all. I'm just highlighting that each paradigm tackles the OP's problem in different, yet effective, ways. – Scant Roger Nov 30 '15 at 14:03
  • 1
    If we are talking alternative paradigms, isn't this one area monads can be used? The compuational context can be precisely captured by eg a Reader or State monad, so we can reason about the dependency as well. – CLF Dec 03 '15 at 21:09
  • +1 for saying you have one correct way instead of the correct way – winkbrace Dec 04 '15 at 09:18
10

If bar is dependent on baz, which in turn requires dependency, then bar requires dependency too in order to correctly use baz. Therefore, the correct approaches would be to either pass the dependency through as a parameter to bar, or curry baz and pass that to bar.

The first approach is simpler to implement and read, but creates a coupling between bar and baz. The second approach removes that coupling, but could result in less clear code. Which approach is best will therefore likely depend on the complexity and behaviour of both functions. For example, if baz or dependency have side effects, ease-of-testing will likely be a large driver in which solution is chosen.

I'd suggest all the other options you propose are both "hacky" in nature and likely to lead to problems both with testing and with difficult to track down bugs.

David Arno
  • 38,972
  • 9
  • 88
  • 121
  • 1
    I almost agree completely. Dependency Injection may be another Non-"hacky"aproach. – Jonathan van de Veen Nov 27 '15 at 14:35
  • 1
    @JonathanvandeVeen, surely the very act of passing `dependency` around via parameters is dependency injection? – David Arno Nov 27 '15 at 16:47
  • 2
    @DavidArno Dependency injection frameworks don't get rid of these kinds of dependencies, they just move them. The magic is that they move them outside your classes, to a place where testing is Somebody Else's Problem. – Kevin Krumwiede Nov 28 '15 at 04:21
  • @JonathanvandeVeen I agree, dependency injection is a valid solution. In fact it's the one I'd most often pick. – Jamie McCrindle Nov 28 '15 at 16:08
1

Philosophically Speaking

I agree with David Arno's concern.

I'm reading the OP as looking for implementation solutions. However, the answer is change the design. "Patterns"? OO design is, one could say, all about context. It's a vast, blank sheet of paper pregnant with possibilities.

Dealing with existing code is a different, well, context.



I'm working on 'zactly the same problem right now. Well, I'm fixing the hundreds of lines of code copy-n-paste that was done just so a value can be injected.

Modularize the code

I threw away 600 lines of duplicate code then refactored so instead of "A calls B calls C calls D ..." I have "Call A, return, Call B, return, Call C ...". Now we only need to inject the value into one of those methods, let's say method E.

Add a default parameter to the constructor. Existing callers do not change - "optional" is the operative word here. If an argument is not passed the default value is used. Then only 1 line change to pass the variable into the refactored, modular structure; and a small change in method E to use it.


Closures

A Programmers thread - "Why would a program use a closure?"

Essentially, you are injecting values into a method which returns a method customized with the values. That customized method is subsequently executed.

This technique would allow you to modify an existing method without changing its signature.

radarbob
  • 5,808
  • 18
  • 31
  • This approach looks [oddly familiar](http://programmers.stackexchange.com/a/274821/22815)... –  Dec 01 '15 at 00:17
  • Roger on the temporal coupling (your link) issue, @Snowman. It is important that the required execution order is encapsulated. – radarbob Dec 01 '15 at 04:14