I noticed that in the GOOS you avoided at all any form of IOC using
always mandatory parameters in the constructor.
As I like IoC (although I don't like most of the IoC frameworks out
there) I'm wondering if it's only for book purposes or do you have bad
feelings about IOC as pattern?
cheers
Uberto
I tend to be cautious about IoC containers since some of them seem to lead teams in the wrong direction, and I can't see the point of trying to write code in XML. This is especially true for an application as small as the auction sniper.
Which is not to say that they can never be the right choice.
S.
Steve Freeman
Winner of the Agile Alliance Gordon Pask award 2006
Book: http://www.growing-object-oriented-software.com
+44 (0) 797 179 4105
M3P Limited. http://www.m3p.co.uk
Registered office. 2 Church Street, Burnham, Bucks, SL1 7HZ.
Company registered in England & Wales. Number 03689627
Ah--that is a refreshing viewpoint, contrasted with the "you must use
an IOC framework" that is pervasive these days.
- Stephen
Oh dear. I'm afraid you've hit my "pedantic about correct use of
technical terminology" nerve!
I'm old enough to remember what IOC really means. And in my day* it
didn't mean "knowing what constructors are for" or "avoiding the new
keyword will magically make code easier to maintain as long as you
never need to understand its dynamic dependencies". Inversion of
Control means callback-driven input, in contrast to having a main loop
that polls the input devices. Think of the difference between DOS and
Windows programming (or curses vs. X11 for Unix programmers). In DOS
you had to write a main loop that queried the position of the mouse
and state of the keyboard. Windows turned that around: it manages the
input devices and calls your program's event handler procedure when
the user performs some input. So Windows inverted the program''s
control flow compared to DOS: it called into the application when
input occurred rather than the application calling out to query device
state .
So, GOOS *only* uses the Inversion of Control pattern: the Smack
library delivers XMPP messages to the application through a callback
and Swing delivers user input events through callbacks.
But I'm being facetious. I believe you're using IOC in its recent
usage as a synonym for Dependency Injection. GOOS does use dependency
injection througout, but we don't call it that because I find the term
"dependency injection" is a really bad name for the pattern.
There are two aspects to Dependency Injection. Firstly, that an
object's interface should define the services that the object
*requires* as well as those it provides. In Java, that's what
constructor parameters are for. Secondly, the code that satisfies the
requirements of an object by giving it a reference to the services of
is collaborators is external to both the object and its collaborators
(for this reason, the pattern also used to be called "third-party
binding" -- some third party is responsible for connecting clients to
services).
The name "Dependency Injection" only refers to the second aspect. And
worse, makes it sound like dependencies are "injected" through the
object's encapsulation boundary rather than explicitly defined as part
of the object's API. And so we get "dependency injection" APIs, like
JavaEE 5, which use reflection to poke dependencies into an object's
private fields, bypassing the constructor, adding a great deal of
complexity to client code and not bringing the benefits that the
Dependency Injection pattern should deliver.
In GOOS we were careful to distinguish between the various practices
that are all lumped together under the term "Dependency Injection".
We define our objects to be context independent (p 54). We clearly
distinguish between an object's dependencies (which must be passed to
the constructor to ensure that the object can never be instantiated in
an invalid state) and its other collaborators (notification listeners,
adjustments) at the object's API (p 52). And we separate the code
that composes objects from the objects being composed (p 64-65).
We don't use a DI framework. Personally, I find them more of a
hindrance than a help in most situations because they don't
distinguish between "internals" & "peers" (p 50) and so often violate
the "composite simpler than the sum of its parts" principle (p 53). I
find that I need to clearly understand, and therefore express in code,
the dependencies between the objects in the system. Hiding that
information away in auto-wiring frameworks or obfuscating it in XML
files that, furthermore, cannot be easily refactored just gets in my
way when I later need to understand the code and modify it. GOOS
instead advocates clearly expressing the dependencies between objects
in the code that composes them, so that the system structure can
easily be refactored, and aggressively refactoring that compositional
code to remove duplication and express intent, and thereby raising the
abstraction level at which we can program (again, p 64-65), so we need
less and less code to write more and more functionality as the system
grows.
The one situation I have found a DI framework useful is when
dynamically loading plug-ins into a running program. But it's not
often I write a program that has to do that, and try to keep that
technical gubbins clearly separated from the code in the application
domain model and elsewhere in the system.
--Nat
* Windows 3.0 programming, if you must know. And be very thankful if
you've never had to do that!
Please copy / paste that in a blog post for posterity, Nat. To great to leave dangling around in a mailing list.
cheers,
Matt
What do you mean by "scope" in this case, compared to Java's lexical
scoping?
Never done DOS graphic programming, but in games we still use the same
pattern! :)
> So, GOOS *only* uses the Inversion of Control pattern: the Smack
> library delivers XMPP messages to the application through a callback
> and Swing delivers user input events through callbacks.
:)
> There are two aspects to Dependency Injection. Firstly, that an
> object's interface should define the services that the object
> *requires* as well as those it provides. In Java, that's what
> constructor parameters are for.
mmmh I agree on the first part but not on the second.
Constructor parameters have lots of limitation in Java, and you need
constructors without parameters for any object that could be
persisted.
Ideally I'd prefer a way to declare required services. Annotations are
a acceptable compromise.
But this is only my feelings.
> (for this reason, the pattern also used to be called "third-party
> binding" -- some third party is responsible for connecting clients to
> services).
First time I heard this name: much better IMHO than IOC or DI.
> The name "Dependency Injection" only refers to the second aspect. And
> worse, makes it sound like dependencies are "injected" through the
> object's encapsulation boundary rather than explicitly defined as part
> of the object's API. And so we get "dependency injection" APIs, like
> JavaEE 5, which use reflection to poke dependencies into an object's
> private fields, bypassing the constructor, adding a great deal of
> complexity to client code and not bringing the benefits that the
> Dependency Injection pattern should deliver.
agreed 100%
> In GOOS we were careful to distinguish between the various practices
> that are all lumped together under the term "Dependency Injection".
> We define our objects to be context independent (p 54). We clearly
> distinguish between an object's dependencies (which must be passed to
> the constructor to ensure that the object can never be instantiated in
> an invalid state) and its other collaborators (notification listeners,
> adjustments) at the object's API (p 52). And we separate the code
> that composes objects from the objects being composed (p 64-65).
I got it now.
In the book examples I agree that any form of IOC framework would be
an overkill, still in big application we found them useful.
Could be that our design is not as good as yours (well for sure it
isn't) but in one of our application we have:
CommandSaveInvoice (from CommandPattern) which requires:
InvoiceManager (for messages and validation)
InvoiceDao (from Dao pattern)
ClientDao
ProductDao
every Dao also require a reference to PersistenceManager and some
drivers for DbVersion and Encryption
Now, we can put Builders for all those classes but having Guice that
declaratively does the work for us it reduce our classes and simplify
the code (we can specify when use a simple new and when a specific
factory for the object).
The other use is in tests is very simple to say to guicemodule to use
mocked dao instead of real ones and be able to test the Commands, same
as we used constructor parameters.
To be honest lately I'm feeling like Guice is following the same path
of other frameworks and becoming more and more heavy, but it's still
very light.
what we really need it could be probably done with just a dozen of classes.
> way when I later need to understand the code and modify it. GOOS
> instead advocates clearly expressing the dependencies between objects
> in the code that composes them, so that the system structure can
> easily be refactored, and aggressively refactoring that compositional
> code to remove duplication and express intent, and thereby raising the
> abstraction level at which we can program (again, p 64-65), so we need
> less and less code to write more and more functionality as the system
> grows.
maybe we are not good enough for this... I fear lots of classes with
very tight integration and very little refactoring, because it's hard
and because we are always in a hurry...
The team has still problems to use TDD in the real application. Guice
could be a not so bad helper to declare required services (instead of
monster classes).
cheers
Uberto
I'm not sure I agree that it's right to annotate classes with things like scope. To me that's a configuration detail not an implementation detail. If you tag a class in Google Guice with a @Singleton annotation that feels wrong to me. It's up to the user how many instances they want to create.
Spring JavaConfig (i.e. configuration in Java, not XML) is better than Guice, in this respect at least
I'm not sure I agree that it's right to annotate classes with things like scope.
To me that's a configuration detail not an implementation detail. If you tag a class in Google Guice with a @Singleton annotation that feels wrong to me. It's up to the user how many instances they want to create.
Spring JavaConfig (i.e. configuration in Java, not XML) is better than Guice, in this respect at least, and is my preferred IoC framework. It also gives you an easy path to switch from Spring XML as you can use both config styles simultaneously.
Could you replace all those 'Dao's and 'Manager's with domain terms. For example, is an 'InvoiceDao' a 'Ledger'? It's amazing what a difference it makes to the code.
> maybe we are not good enough for this... I fear lots of classes with
> very tight integration and very little refactoring, because it's hard
> and because we are always in a hurry...
if it doesn't have to work well, you can go as fast as you like :)
> The team has still problems to use TDD in the real application. Guice
> could be a not so bad helper to declare required services (instead of
> monster classes).
You shouldn't be seeing monster classes. On the whole, you should get more locality as you discover intermediate objects that cluster up the creation of domain objects.
S.
I prefer to just instantiate objects and pass the dependencies to
their constructors. Whether it's a servlet or a domain object, I find
that makes code easier to understand and change.
I can see that a DI framework might help work around the fact that the
Servlet API makes it much harder to manage dependencies than it should
be. But I prefer to work around that by just avoiding those aspects
of the Servlet API, either by using a better HTTP server library, like
SimpleWeb, or by giving my servlets constructors and running them in
an embedded servlet engine like Jetty -- using JavaEE as a collection
of useful libraries rather than a surrogate operating system.
I've been surprised how much code I could get rid of just by giving
servlets constructors and passing them the objects they needed. Even
in a small web-app with a couple of servlets that use a few shared
objects, using constructors cut out several hundred lines of pointless
XML configuration and code that did nothing but put things into
various contexts in one part of the code and get things back out in
another part of the code. Totally pointless because Java already does
a pretty good job of defining and managing scopes itself.
--Nat
What limitations?
> you need
> constructors without parameters for any object that could be
> persisted.
Not necessarily. If a library forces that, it's not a very good
persistence library.
For example, you don't need to define a no-arg constructor to
serialise objects with Java serialisation or XStream. In Hibernate you
can define a Tupleizer to instantiate an object either by calling the
class' constructor or by using Objenesis to bypass the constructor
entirely. Or, if you don't want to do that, you can make the no-arg
constructor private so that it is only used by the persistence library
and that design wart doesn't pollute the rest of your domain model.
--Nat
>> maybe we are not good enough for this... I fear lots of classes with
>> very tight integration and very little refactoring, because it's hard
>> and because we are always in a hurry...
>
> if it doesn't have to work well, you can go as fast as you like :)
well... all in all it's working pretty well. :)
It's not that we don't have time to refactor, the problem is that is
not easy what you're asking for.
We cannot change our team, they are also good programmers, just used
to work with "brain half switched off and googling for technical
solutions". On the other side they are pretty good on domain related
problems.
From my experience this is far from atypical and it takes time to
change this attitude.
This is a bit OT anyway. :)
cheers
Uberto
Theoretically changing dao impl we can switch from bigtable to file system to dbms.
Not sure I got the point across. I know what a DAO and Manager is supposed to represent, but they're generic terms from the domain of implementation. I've seen code based improve radically by insisting on real domain terminology and banning all such terms--except at the edges. Using the right words throws up any inconsistencies in the structure of the code.
> It's not that we don't have time to refactor, the problem is that is
> not easy what you're asking for.
> We cannot change our team, they are also good programmers, just used
> to work with "brain half switched off and googling for technical
> solutions". On the other side they are pretty good on domain related
> problems.
> From my experience this is far from atypical and it takes time to
> change this attitude.
> This is a bit OT anyway. :)
This is all too true. And I don't work that way any more because I've seen the trouble it causes down the road.
mmmh ok. But anyway this is not the point.
Any hald decent IOC framework can pass dependences on constructor or
later on fields.
What I haven't very clear is how to pass dependences of dependences etc....
For example if for the SaveInvoice command I need InvoiceLedger and
for InvoiceLedger I need PersistenceManager (singleton) and
EmailSender and for the latter I need the ConfigurationService
(singleton) which kind of code I have to write to process that
command?
public SaveInvoice commandSaveInvoiceFactory(){
EmailSender emailSender = new
EmailSender(ConfigurationService.getInstance());
invoiceLedger = new
InvoiceLedger(PersistenceManager.getInstance(), emailSender);
return new SaveInvoice(InvoiceLedger);
}
does it make sense or am I talking rubbish?
cheers
Uberto
cheers
Uberto
Request scoped objects should be passed around as parameters.
I think you're misusing the term "singleton".
I've been solving this lately with a non-singleton Registry that is an
explicit interface for application-scoped beans.
A typical use case I use this for is something like:
* a parent view has 4 tabs
* tab1 wants application-scope bean A
* tab2 wants application-scope bean A and B
* tab3 ... etc.
So, when instantiating each tab, if the parent view has to know all the
beans its children care about, the parent gets constructor bloat of
useless-to-it beans that it just passes on.
Instead, I have an interface:
interface AppRegistry {
BeanA getBeanA();
BeanB getBeanB();
}
That functions a lot like a spring app context, but with an explicit
interface, and no magical runtime-lookup "get(SomeBean.class)" method.
ParentView's cstr is then:
ParentView(AppRegistry r, ...)
And the tab cstrs are:
TabA(AppRegistry r, nonAppScopeBeanC c) {
a = r.getBeanA();
}
TabB(AppRegistry r) {
a = r.getBeanA();
b = r.getBeanB();
}
It all works out rather nicely--the biggest downside is that it is no
longer clear what TabA/TabB dependent on, at least more granularly than
"some number of app-scoped beans".
In practice I haven't found this to be a big deal but would appreciate
any feedback on a better way to go about it.
- Stephen
:-)
I'd considered that, as I'd noticed some of the "MVP" GWT stuff uses
inner interfaces, though in a slightly different way.
But, yeah, something like:
public class TabA {
public interface Deps {
getBeanA();
getBeanB();
}
public TabA(TabA.Deps deps) {
...
}
}
I'd forgotten about that though--thanks for mentioning it!
Out of curiosity, would you have the TabA.Deps interface only
include application scoped dependencies, or potentially other
scopes as well?
(I haven't really done much formalization around any scope
other than the application one.)
- Stephen
>
>> You could solve the 'only downside' bit by making the AppRegistry
>> interface extend a bunch of interfaces (one per Tab) each of which
>> declared just what the Tab needed.
>
> :-)
>
> I'd considered that, as I'd noticed some of the "MVP" GWT stuff uses
> inner interfaces, though in a slightly different way.
>
I probably wouldn't do it as an inner interface, since I mostly dislike them and prefer creating new packages instead.
> I'd forgotten about that though--thanks for mentioning it!
>
> Out of curiosity, would you have the TabA.Deps interface only
> include application scoped dependencies, or potentially other
> scopes as well?
>
> (I haven't really done much formalization around any scope
> other than the application one.)
Me neither. I don't know, but I suspect I'd separate them.
>
> - Stephen
>
S.
On 27 Aug 2010, at 22:52, Stephen Haberman wrote:
>
>> What I haven't very clear is how to pass dependences of dependences
>> etc....
>
> I've been solving this lately with a non-singleton Registry that is an
> explicit interface for application-scoped beans.
>
> A typical use case I use this for is something like:
>
> * a parent view has 4 tabs
> * tab1 wants application-scope bean A
> * tab2 wants application-scope bean A and B
> * tab3 ... etc.
Steve Freeman
True, the view does not know what's inside the builder, but somebody has to,
and wiring together the resulting builder code by hand is, IMO, what pushes
people to auto-wiring DI frameworks.
For example, using guice-terminology of suppliers instead of builders:
class TabA {
public TabA(appBeanA, requestBeanB) {
...
}
}
class TabASupplier {
pirvate appBeanA;
public TabASupplier(appBeanA) {
this.appBeanA = appBeanA;
}
public TabA create(requestBeanB) {
return new TabA(appBeanA, requestBeanB);
}
}
Now, the parent view:
class ParentA {
private TabASupplier tabASupplier;
public ParentA(tabASupplier) {
this.tabASupplier = tabASupplier;
}
void businessLogic() {
tabASupplier.create(requestBeanB);
}
}
class ParentASupplier {
private TabASupplier tabASupplier;
public ParentASupplier(tabASupplier) {
this.tabASupplier = tabASupplier;
}
public ParentA create() {
return new ParentA(tabASupplier);
}
}
So whoever instantiates the ParentASupplier is going to have to know about the
TabASupplier...which is going to have to know about any suppliers that TabA
wants, recursing all the way down the control flow.
This leads to an explosion of supplier wiring code in the application startup
sequence where the top-level entry point is handed a single FooSupplier, that
seems simple to use (just call `create`), but it required references to every
other supplier in the app to put that one FooSupplier instance together.
Which is why people think they need guice to do all of that wiring for them.
I think anyway--let me know if I missed something.
(...re-reading this, these are really factories--I wonder why guice choose
to introduce a new term, if not to just avoid the conceptual legacy baggage
of "factories == bad".)
- Stephen
Thinking about it more, I agree, I would separate them too.
While I have not had explicitly named AppRegistry, SessionRegistry,
RequestRegistry interfaces, I have occasionally have RequestContext
interfaces, which s/Registry/Context/, they are really the same thing.
I stick with the Registry terminology mostly as a tribute to Fowler's
PoEAA [1], but also to be contrary.
(Admittedly, I started (ab)using registries as statics and relatively
recently changed my style [2] to passing them around explicitly.)
- Stephen
[1]: http://martinfowler.com/eaaCatalog/registry.html
[2]: http://www.draconianoverlord.com/2010/01/15/changing-my-style.html
(Huh--I had completely forgotten how of much of this AppRegistry style I
had written up in this post.)
Which shows that the code is violating the "Composite is Simpler than
the Sum of its Parts" principle.
I'd interpret that as a "design pressure" pushing me to do something
to improve the structure of the system to better hide information and
factor out more appropriate higher-level abstractions.
> Which is why people think they need guice to do all of that wiring for them.
Which is just sweeping the problems under the rug. And rug that ends
up with an ever-increasing bulge in the middle where all the dirt is
accumulating.
--Nat
If I understood correctly:
Let's forget a moment about guice, let's call it "binder with a
registry module" BRM in short.
Let's forget about annotations: let's use BRM to pass the correct
instantiated services to constructors.
The remaining problem is that this could be a incentive to abuse of
anonymous registered services instead of carefully choosing the one
best suited for the domain.
Like in the "managers/dao" naming.
Did I understand it correctly?
cheers
Uberto
Somewhat of a tangent, but is any auto-wiring DI really type safe?
You declare your dependencies via @Inject-annotated cstrs, then your
bindings of how these dependencies are satisfied somewhere else, and
then at runtime magic happens and the framework calls your cstrs via
reflection.
That your cstr dependencies are all correctly met by your bindings is
something that is only vetted at runtime.
Per the Weld documentation [1]:
At system initialization time, the container must validate that
exactly one bean exists which satisfies each injection point.
To me, using the term "type safe" == "the compiler will tell me if I
screw up", not "the framework will tell me at runtime".
- Stephen
[1]: http://docs.jboss.org/weld/reference/1.1.0.Beta1/en-US/html_single/
How would the compiler know how many candidates for injection you will
have on your classpath at runtime? The compiler only has a static view
of the world. In general it also doesn't know if a reference is null
when you access an object. "type safe" == "the compiler will tell me
if I screw up" is rather simplistic in that regard.
- Erich
If I follow correctly, you're saying if I inject Bar:
public Foo(Bar bar) { ... }
The instance `bar` that is injected will satisfy the static type of
`Bar`. Sure, but hasn't that always been the case with DI?
My impression was that "type safe DI" was a new term coined by the
Guice guys. Dan's comment that JSR-299 is type safe whereas Spring
is not piqued my interest and garnered my reply.
> How would the compiler know how many candidates for injection you will
> have on your classpath at runtime? The compiler only has a static view
> of the world.
Exactly.
That's why calling auto-wiring DI "type safe" makes no sense to me. The
runtime wiring is not at all type-safe. Which is fine, I understand why
it's not. But given that runtime wiring is the very purpose of auto DI
frameworks, why do people keep calling them "type safe"?
> "type safe" == "the compiler will tell me if I screw up" is rather
> simplistic in that regard.
Very true--I admit this was an overstatement. I should have keep the
statement simply as the compiler will catch `new Foo(wrongArgHere)` but
auto DI will not catch `new Foo(argNotBound)`.
- Stephen
> But given that runtime wiring is the very purpose of auto DI
> frameworks, why do people keep calling them "type safe"?
I have been reading about why Guice would claim type safety, and it
seems to be mostly because previously Spring was so horribly un-type
safe (string identifiers, XML, etc.).
Guice did a lot to make isolated user code type safe, and isolated
binding code type safe--it's just the runtime resolution/constructor
calls that are not type safe.
Dunno. I guess I see where they are coming from. But it still bothers
me.
- Stephen
That makes sense. Guice also offers a type-safe way to qualify
multiple objects that would fit the static type. All you need to do is
define the necessary annotations. I have to admit that I'm not sure
it's the right way to go. Looking at a few CDI examples in JEE6 made
me scratch my head.
> Guice did a lot to make isolated user code type safe, and isolated
> binding code type safe--it's just the runtime resolution/constructor
> calls that are not type safe.
It seems you want type-safety to imply that you don't have to wait
until runtime to find out that something is wrong. My impression is
that you're asking for more than type-safety is meant to guarantee.
> Dunno. I guess I see where they are coming from. But it still bothers
> me.
>
> - Stephen
>
- Erich
it's just the runtime resolution/constructor calls that are not type safe
Hello there,
> I've been surprised how much code I could get rid of just by giving servlets constructors and passing them the objects they needed.
I believe Paul Hammant was working on Jetty supporting servlets with constructors back in 2006, the exact way you mentioned... seemed great,its just sad it took forever for it to become popular - and it still did not.
> using constructors cut out several hundred lines of pointless XML configuration
Comparing anything with XML configuration is cheating :) I remember when Joe Walnes said that configuration should be first programmatic, and then, well, then anything else that *might* help. The key feature of a software would be to be *easily* configurable through its API. IMHO configuring the system is the same as "configuring our component composition".
> I prefer to just instantiate objects and pass the dependencies to their constructors. Whether it's a servlet or a domain object, I find that makes code easier to understand and change.
I believe that's how most of XStream internals work. Dependencies are instantiated "by hand" and passed to constructors
Nat, you mentioned the DI framework takes out the visibility that you have with "by hand" wiring dependencies, which I agree. The thought that "now I dont need to know who depends on who" because the frameworks makes that decision is not true because we need to know how the framework takes those decisions. And the negative side is that its hidden from us. I agree frameworks lack on that visibility. Wiring by hand still gives me IoC, but I know what is happenning because its clear code written in wiring parts of my software. ++ on that.
Those frameworks (ignore xml or member variable setting based ones) could expose the dependency tree somehow. It seems like this feature would help bringing back visibility on how things are connected, but not allowing me to easily play with composing the behavior.
About the scopes, I agree the question should not be in those cases about them. To create a new scope simply create a new object and compose your system as required. But the question is about lifespan and lifecycle.... handling it by hand, how would it look like? Let me try an example, I will go step by step.
Basic server startup:
class MyApp
pool = new MyConnectionPool()
new Server(pool).start()
My services are stateless, request lifespan, so:
class MyApp
pool = new MyConnectionPool();
services = new Services(pool);
new Server(services).start()
The next step is to add my URI bindings, and whichever one needs to access a connection, the lifecycle of a connection will be handled by the services class, as with any other "request" lifespan component.
class MyApp
pool = new MyConnectionPool();
services = new Services(pool);
services.add("/first", First.class);
services.add("/second", Second.class);
services.add("/third", Third.class);
new Server(services).start()
And now handling something that lives more than one request, i.e. a logged in user:
class MyApp
pool = new MyConnectionPool(); // app lifespan
users = new Users(pool); // between one request lifespan and the app lifespan
services = new Services(pool, users);
services.add("/first", First.class);
services.add("/second", Second.class);
services.add("/third", Third.class);
new Server(services).start()
Or should I pass the users to Server (as its in a larger picture?).
BTW I was trying to run away from "pattern based" names, like "Services". What should I rename it to? Or better, refactor those classes into?
Regards
Guilherme Silveira
The next step is to add my URI bindings, and whichever one needs to access a connection, the lifecycle of a connection will be handled by the services class, as with any other "request" lifespan component.
class MyApp
pool = new MyConnectionPool();
services = new Services(pool);
services.add("/first", First.class);
services.add("/second", Second.class);
services.add("/third", Third.class);
new Server(services).start()
And now handling something that lives more than one request, i.e. a logged in user:
class MyApp
pool = new MyConnectionPool(); // app lifespan
users = new Users(pool); // between one request lifespan and the app lifespan
In PHP frameworks we have exactly the same problem: Controllers are
our version of Servlets, which have a constructor not available for
collaborators. I know you do not like frameworks, but in PHP they
provide the functionalities of a Servlet Container, which would be
missing without them.
Creating a single Controller is not viable because it would have to
handle routing and bootstrap by itself (so much for using a
framework). I will think about it, however.
I've seen different solutions:
1. Instantiating a Factory that creates the necessary collaborators
and keep the controller's code so short that it is tested only
end-to-end.
2. Defining setters which are discovered in some framework hook, and
called with the right collaborators.
3. Use a framework like Symfony2, which provides support for injecting
a Container object into each Controller.
You described 1, and maybe 3 which generalizes 1. I feel 2 would be
the cleanest solution, but 1 or 3 have a better ROI and the
side-effects that your application is not tied to the framework for
more than the code in the Controllers. The framework may be subject to
change, unlike the Servlet api.
--
Giorgio Sironi
Piccolo Principe & Web Engineer
http://giorgiosironi.blogspot.com
http://twitter.com/giorgiosironi
> I suppose the intent is that the services object instantiates a First,Yep... then:
> Second etc objects, is that right? But now you can't pass collaborators to
> these new objects. This is how a servlet engine works, and it's the reason
> why servlets must have a zero-args constructor. I don't like this!
> > services.add("/first", new First(d1,d2));
> > services.add("/second", new Second(d2));
> > services.add("/third", new Third(d1));
> > new Server(services).start()
The problem now is that if the lifecycle I wanted to have for fields
within First were request based, now they are App base. An example is
the database connection, which would not allow me to share the
connection between first and second in the same request without using
ThreadLocal?
> If we are talking about a web application, I really don't like this design.Anything you store in session? Imagine that's the thing you want to
> I don't want User objects to live beyond a single request. If I need to
> know stuff about the logged-in user I will load that from session storage at
> every request.
share between two components.
Not a problem... trying to find out how the code would look like and,
> Disclaimer: this is how *I* do things. YMMV.
in the end, if it would really be easy to maintain those lifecycles
and scopes. It seems like the dev can still do nasty things with or
without the frameworks, doing bad things seems as easy as it was when
using them, does not?
Regards
Guilherme
Programmers can write bad code with or without a framework. I don't
think that using a DI framework might lead you to poorer design
because it kind of "hides" dependencies from you. In fact, most of bad
design I've seen weren't using a DI framework (so the dependencies
were very clear to those programmers).
Besides the fact that if a programmer wants to write bad code, he
will, I want to discuss the following: I believe that some
dependencies need to be more explicit than others. Suppose that a
class depends on some class that access the database (a DAO). These
kind of dependencies usually never change in runtime. I know that my
class depends upon some Dao interface, but I don't care if a OracleDao
or a MySqlDao is injected.
Now, suppose some class that depends on an algorithm that may be
composed by many other small behaviors. An example that comes to mind
is a price of a service which may be calculated by its tax plus profit
that the company needs plus basic price, etc. All of them implement
PriceAlgorithm interface and I compose the behavior just like that:
priceCalculator = new Tax(new Profit(new Basic(...));
Now suppose some other class that issues invoices and depends on this
algorithm to calculate the final amount plus a DAO:
public class InvoiceGenerator {
public InvoiceGenerator(ServiceDAO services, PriceAlgorithm algorithm) { ... }
public void issue() {
for(Service service : services.all()) { /* use algorithm to
calculate price and issue an invoice */ }
}
}
When creating this InvoiceGenerator, I don't care which implementation
of ServiceDAO was injected, but I do care about the price algorithm
implementation as it would change the expected behavior (if I add or
remove one behavior in that decorator, for example). Did I make my
point and showed a differences between these two dependencies? Does
that make sense?
How do you deal with them? A solution would be, instead of injecting
PriceAlgorithm directly, injecting a PriceAlgorithmBuilder which is
responsible to build this algorithm. Any other ideas?
Regards,
Mauricio