Log in

No account? Create an account
current entries friends' entries archives about me Previous Previous Next Next
Technical Life - cellophane — LiveJournal
the story of an invisible girl
Technical Life
read 16 comments | talk to me!
pstscrpt From: pstscrpt Date: April 1st, 2008 01:36 pm (UTC) (Link)
That's the same idea, though, isn't it? And you'd still need to design your system to use factories everywhere to supply the mock versions when appropriate. Unless maybe the constructors looked for a test flag, but then you're having the main class server as a mock...

I dunno. TDD is probably worth all that when you get the hang of it (don't get me wrong, I totally understand the benefits). It just seems like the accepted best practices are getting to be seriously at odds with the fundamental natures of the languages. Maybe when you declare an object reference it should have to be typed as an interface and not a class. Maybe New() should be a static method on the class that can see what test you're running (annotations?) and doesn't have to return the type you said. Maybe first-class functions could be a cleaner way of providing test functionality than programming to interfaces...
specialagentm From: specialagentm Date: April 1st, 2008 01:57 pm (UTC) (Link)
One definition of subtypes in OO [1] is that you should be able to freely substitute a subtype for any instance of its supertype, and the caller should not know the difference. Different behaviors may result, but the message-passing, flow of control, and correctness is identical.

All that factories or dependency injection does is pull that up one more level and let you make a decision to, application-wide, swap in and out different implementations without having to change every single call to new(). Some very dynamic languages like Lisp or Ruby let you do this directly (I can, at runtime, redefine class Foo to be some other piece of code entirely). Less smart languages like Java need a little help, but you can write a factory class or static factory method pretty quickly, and a global search and replace to change:

Foo bar = new Foo();

to this:

Foo bar = FooFactory.fetchMeAFoo();

is pretty easy too.

So, yes... upfront work is needed. But this doesn't just drive TDD, it drives a lot of other pieces of flexibility that are often useful. For us, use of factories means we can write core code that doesn't care where it's running at, and how it gets to the data it needs -- in some cases, it has local database access. In other cases, it has to fetch the data over a Web Service to someone else who has DB access. In the TDD case, it's using fake in-memory data.

I'm not trying to sell anything, just trying to show the full picture of why these practices are followed. I'll admit that the way you implement these concepts could be more elegant, but the dirty bits are usually all quite well hidden once you've got it all setup.

[1] http://en.wikipedia.org/wiki/Liskov_substitution_principle
read 16 comments | talk to me!