"Notice that every object simply knows about the objects it directly interacts with. There is no passing of objects reference just to get them into the right location where they are needed."
But that's not true. The caller of LoginPage(UserRepository repo) has no legitimate interest in the fact that LoginPage needs a UserRepository, but you're insisting on it doing so. I'll agree that it's not as bad as having everyone in the call stack above UserRepository() know about the Database, but it's still sub-optimal, especially if LoginPage() has a bazillion callers.
Um... how does LoginPage's caller have any knowledge of how LoginPage is constructed. The only caller of the constructor is the container. No application object should ever invoke new LoginPage(someUserRepo).
Sorry, the only way the above isn't true is if LoginPage is constructed many times - but if the "bazillions of callers" are performing construction, then you've missed the point. LoginPage should be provided to its callers, not constructed by its callers.
Tapestry is really nice, this way. As a web-framework, it provides all its page objects from its IoC container, so what I'm saying above is literally true for all pages. But even if you weren't using a web-framework that provided LoginPage from a container, you would still want to separate construction/wiring from invocation of functionality. If the code that constructs LoginPage knows about UserRepository, that's just fine, since it's an essential dependency. the point is the average calling class does NOT need to know about its internals, and wouldn't.
I agree with most of points but Singleton Pattern is very useful on many of situation and with Spring framework managing singleton for you its even more useful. but yes you do have point to thought about.
Singleton is terrible for these reasons (aside from hidden dependency which the author covered):
1) Singleton lifespan is not directly controlled. It is created on the first call to .Instance. This can create very strange situations where you simply call "MySingleton::instance();" without actually using the instance returned because the side effects of the singleton starting up are required (database connection, network connection, whatever).
2) Singleton objects cannot be inherited from because inside their own static method they directly bake in a new object of the base class. This makes it impossible to mock up a testing db connection, or even to set up either develop or production versions of these objects without trending towards monolithic base classes, or additional dependencies.
I've seen things like "MySingleton::Instance().Initialize(MySingletonDependencies)", seriously going down this path is actually terrifying, what happens if someone forgets that Initialize? What happens if you write this CORRECTLY, but later on someone else calls .instance before your call breaking the application. How does this thread? You can already detect the smell of rotting corpses.
3) What happens when you want more than one logger to handle debug cases? What happens when you need a second database connection? What happens when you need to spin up multiple instances of your application logic to run a montecarlo simulation? Oh yeah, fuck you, that's what happens.
Least importantly of all:
4) Your code has hidden dependencies baked in all over the place, classic global variable problem.
What do I suggest? Well, a ServiceLocator can help... It still has issues, it is still not ideal, but at least it supports inheritance:
"Notice that every object simply knows about the objects it directly interacts with. There is no passing of objects reference just to get them into the right location where they are needed."
ReplyDeleteBut that's not true. The caller of LoginPage(UserRepository repo) has no legitimate interest in the fact that LoginPage needs a UserRepository, but you're insisting on it doing so. I'll agree that it's not as bad as having everyone in the call stack above UserRepository() know about the Database, but it's still sub-optimal, especially if LoginPage() has a bazillion callers.
Um... how does LoginPage's caller have any knowledge of how LoginPage is constructed. The only caller of the constructor is the container. No application object should ever invoke new LoginPage(someUserRepo).
ReplyDeleteSorry, the only way the above isn't true is if LoginPage is constructed many times - but if the "bazillions of callers" are performing construction, then you've missed the point. LoginPage should be provided to its callers, not constructed by its callers.
Tapestry is really nice, this way. As a web-framework, it provides all its page objects from its IoC container, so what I'm saying above is literally true for all pages. But even if you weren't using a web-framework that provided LoginPage from a container, you would still want to separate construction/wiring from invocation of functionality. If the code that constructs LoginPage knows about UserRepository, that's just fine, since it's an essential dependency. the point is the average calling class does NOT need to know about its internals, and wouldn't.
I agree with most of points but Singleton Pattern is very useful on many of situation and with Spring framework managing singleton for you its even more useful. but yes you do have point to thought about.
ReplyDeleteSingleton is terrible for these reasons (aside from hidden dependency which the author covered):
ReplyDelete1) Singleton lifespan is not directly controlled. It is created on the first call to .Instance. This can create very strange situations where you simply call "MySingleton::instance();" without actually using the instance returned because the side effects of the singleton starting up are required (database connection, network connection, whatever).
2) Singleton objects cannot be inherited from because inside their own static method they directly bake in a new object of the base class. This makes it impossible to mock up a testing db connection, or even to set up either develop or production versions of these objects without trending towards monolithic base classes, or additional dependencies.
I've seen things like "MySingleton::Instance().Initialize(MySingletonDependencies)", seriously going down this path is actually terrifying, what happens if someone forgets that Initialize? What happens if you write this CORRECTLY, but later on someone else calls .instance before your call breaking the application. How does this thread? You can already detect the smell of rotting corpses.
3) What happens when you want more than one logger to handle debug cases? What happens when you need a second database connection? What happens when you need to spin up multiple instances of your application logic to run a montecarlo simulation? Oh yeah, fuck you, that's what happens.
Least importantly of all:
4) Your code has hidden dependencies baked in all over the place, classic global variable problem.
What do I suggest? Well, a ServiceLocator can help... It still has issues, it is still not ideal, but at least it supports inheritance:
http://stackoverflow.com/questions/4074154/when-should-the-singleton-pattern-not-be-used-besides-the-obvious/4074287#4074287