On Wed, Sep 2, 2009 at 4:30 PM, David
Pollak<feeder.of.the.be...@gmail.com> wrote:
>
>
> On Wed, Sep 2, 2009 at 1:27 PM, Kris Nuttycombe <kris.nuttyco...@gmail.com>
> wrote:
>>
>> I think that the following really misses the point of dependency
>> injection:
>>
>> On Wed, Sep 2, 2009 at 11:39 AM, David
>> Pollak<feeder.of.the.be...@gmail.com> wrote:
>> >
>> > Let's say we're running in test mode, in Boot.scala:
>> > if (Props.testMode) {
>> >   MyAppRules.paymentGateway = () => MockPaymentGateway
>> > }
>>
>> In order to test in isolation, production code should never have to
>> have any idea that mock classes might exist. In most cases, they don't
>> - the mock is a dynamic proxy that has expectations configured on it
>> *in the test case*.
>
> At some point, the concrete implementation has to be specified, DI or no.
> At some point there needs to be a definition (in a config file, in an
> annotation, in Boot, in the current session, on the current call stack) of
> the concrete class.  Having a factory function that can be changed means
> that you can define how an instance is created, that's all.

My point is that Boot is part of the production codebase, and as such
it should be entirely ignorant of the test harness.

>> Dependency injection can be used to do configuration at any level of
>> granularity, not just at the "global config" level that is Boot.scala.
>
> And my example above allowed for configuration at any level (well... the
> example didn't include 'current request' but that's a change from SessionVar
> to RequestVar)

But can you test a snippet in the absence of references to RequestVar,
SessionVar, S, and the rest of Lift if the snippet makes calls to such
objects? I don't want to have to set up the state of a Req having been
processed through a RewriteRequest and so on to create an environment
for my snippet to run in.

>> This is the whole reason the enterprise world has rejected singletons,
>> because any code that uses such a singleton cannot be tested in
>> isolation without messing with a class that may be largely irrelevant
>> to the functionality being tested.
>
> I guess this is where our philosophies diverge.  I believe in integration
> tests and unit tests of things that deal with untyped data (Strings).  Most
> other forms of testing tend in my experience to be pointless: they take lots
> of time to write and run and yield very few delta defects.

I guess I'm just not that good; I miss boundary cases in my algorithms
on a not-too infrequent basis, particularly when there's a large state
space that the configurations of my persistent data can occupy. I find
unit tests to be extremely helpful, particularly with how often the
requirements I'm trying to satisfy grow and force me to refactor.

What's strange to me is that in my experience, unit tests are quick to
write and run, while integration tests are the ones that are a
nightmare to set up for.

> Additionally, the S pattern looks like a global, but is in fact a front end
> to thread-specific state.  Scala's DynamicVar (S sits on top of a DynamicVar
> style pattern) gives you a lot of latitude to have a concrete symbol for
> something with a dynamic meaning for that symbol.

I understand this, but to me thread-local state is little better than
global state, because when you come down to it RequestVar and
SessionVar instances behave like globals within the context of the
request or the session, respectively. If I have multiple snippets on a
page that both happen to mutate the state of a RequestVar without
checking it, code that's ignorant of the order of snippet calls cannot
reliably  make any assumptions about said state. This has caused me
bugs that took serious time to track down and in some cases still
aren't fully resolved.

>> If the only dependencies that an
>> object has are provided through constructor parameters, any and all
>> external state that the object depends upon can be trivially mocked
>> simply by passing in different parameters.
>
> I don't understand the difference between having a parameter magically
> passed because on an annotation and making a method call to get a parameter
> that satisfies an interface other than the call is explicit and the
> annotation based mechanism is something that happens by magic where I regard
> magic to be bad.

I guess I feel like dependency injection is a declarative approach,
which I prefer to the imperative method call. Ultimately, the
significant question is what is allowed to configure how that method
call responds; if there are several layers of framework (Boot, S,
RequestVar, etc.) between the configurer and the configuree I don't
have confidence that the state I'm trying to establish wont get mucked
up along the way. DI is hardly magic; it's just a matter of having a
piece of code that will calculate a dependency graph for you then find
the correct objects to plug in from a flat scope to establish the
state of the object you request.

>> With respect to Tim's comment, with Guice you usually don't use a
>> configuration file; your configuration is in code. In a test case, you
>> create an Injector using a set of modules that have the "rules" for
>> object creation (specifications for what type or instance of object is
>> to be injected in any given position) and then you use this Injector
>> as your factory. In a production system, the process is exactly the
>> same - but you create the Injector with a different set of modules.
>
> So, you've got code that makes a determination about how to be a factory for
> a given interface under a specific circumstance, which sounds a lot like
> what I'm proposing.

The difference is that in your example, it eventually all devolves to
the MyAppRules object, and if you want to make a change to the gateway
that is used you have to change MyAppRules itself rather than supply a
different instance of a class conforming to the MyAppRules interface.
There are other issues - take the example of choosing the payment
gateway based upon locale. What if there are other factors you want to
choose based upon - say the part of the application that the gateway
is being called from? With the DI solution, that part of the
application simply instantiates its own injector with the correct
configuration; with your solution, MyAppRules then becomes coupled to
some notion about who its caller is.

>> In reading this thread, I can't help but to wonder... how extensively
>> have those of you who are purporting traits and partial functions to
>> be a replacement for DI actually used a modern dependency injection
>> framework?
>
> I'm all for a discussion and even disagreement that's based on code and code
> examples.  I'm not keen on insulting other people because their views and
> experience differs from yours.

I'm surprised you found my question insulting, and if you took it as a
slight I apologize, but I was actually honestly wanting to know, not
trying to score some sort of point. I know that my understanding of
the utility of DI was seriously flawed until I had used Guice in a
large project and discovered how simple it made testing for me. I
realize now based upon what you said above that you don't really find
unit testing to be of value, so I can see how these issues are not as
important to you.

Kris

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Lift" group.
To post to this group, send email to liftweb@googlegroups.com
To unsubscribe from this group, send email to 
liftweb+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/liftweb?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to