tag:blogger.com,1999:blog-5654876.post7679079597441733955..comments2024-01-07T00:15:48.561+02:00Comments on Marcel Popescu: ServiceLocator anti-pattern?Marcelhttp://www.blogger.com/profile/13951354231483245521noreply@blogger.comBlogger7125tag:blogger.com,1999:blog-5654876.post-51814845347661892742011-01-06T14:41:58.840+02:002011-01-06T14:41:58.840+02:00I never got back to this article, unfortunately......I never got back to this article, unfortunately... but yea, I pretty much agree with Nick nowadays :) Thanks again Nick.Marcelhttps://www.blogger.com/profile/13951354231483245521noreply@blogger.comtag:blogger.com,1999:blog-5654876.post-57425833628931072092009-01-08T22:30:00.000+02:002009-01-08T22:30:00.000+02:00Thanks for stopping by again :) I guess my opinion...Thanks for stopping by again :) I guess my opinions are heavily colored by WebForms development; I only recently started to play with the MVC framework (I hate installing betas).<BR/><BR/>I guess I'll have to study the DI frameworks some more. They still smack of magic a bit much, and I still absolutely hate the idea of XML configuration, but I guess I'll have to bite the bullet sooner or later :)<BR/><BR/>Thanks for discussing this!Marcelhttps://www.blogger.com/profile/13951354231483245521noreply@blogger.comtag:blogger.com,1999:blog-5654876.post-76054461142603960972009-01-08T21:27:00.000+02:002009-01-08T21:27:00.000+02:00The case of Pages/UserControls is one situation wh...The case of Pages/UserControls is one situation where a global service locator is a practial necessity. Moving away from this is a good thing though - and not all frameworks have this requirement anymore (check out the controller factories in ASP.NET MVC as an example.)<BR/><BR/>Different implementations for different requestors are configured at the level of the requestor, so that would still require a change in the Bind statement - just not a change in the component. The different implementations need to be configured with different keys, and the bindings for the client components each need to use a different key.<BR/><BR/>If:<BR/><BR/>var ib = ServiceLocator.GetInstanceOf<IB>();<BR/><BR/>is in the constructor of A, and a similar call is in the constructor of B, I think a stack overflow will still occur in this example.<BR/><BR/>Even stack overflow for new instances is a problem however, because StackOverflowException is a nasty exception to recover from.<BR/><BR/>Cheers!<BR/><BR/>NickNickhttps://www.blogger.com/profile/02020072230899910249noreply@blogger.comtag:blogger.com,1999:blog-5654876.post-17816936302112449332009-01-08T01:41:00.000+02:002009-01-08T01:41:00.000+02:00Another clarification :) When I said the framework...Another clarification :) When I said the framework won't tolerate declaring dependencies explicitly I didn't mean the repository, of course, but stuff like Controllers and Pages. Since I need to create two constructors for those (a default one, using the service locator, and an explicit one), I went with the same pattern everywhere.Marcelhttps://www.blogger.com/profile/13951354231483245521noreply@blogger.comtag:blogger.com,1999:blog-5654876.post-18298080117431574482009-01-08T01:25:00.000+02:002009-01-08T01:25:00.000+02:00Made a mistake above in the bindings, please ignor...Made a mistake above in the bindings, please ignore... I need to bind IA to () => new A(), of course.Marcelhttps://www.blogger.com/profile/13951354231483245521noreply@blogger.comtag:blogger.com,1999:blog-5654876.post-42637209751163528002009-01-08T01:22:00.000+02:002009-01-08T01:22:00.000+02:00Wow, fast reply :) Thanks a lot for this, let me s...Wow, fast reply :) Thanks a lot for this, let me see if I can address your points in a way that makes sense.<BR/><BR/>1. Publicly declaring dependencies. Absolutely necessary, except that the framework won't usually tolerate this, at least in ASP.NET. So what I end up with is this:<BR/><BR/> public class ProductRepository : IProductRepository<BR/> {<BR/> private readonly IRepository<Product> repository;<BR/><BR/> public ProductRepository()<BR/> : this(ServiceLocator.GetInstanceOf<IRepository<Product>>())<BR/> {<BR/> }<BR/><BR/> public ProductRepository(IRepository<Product> repository)<BR/> {<BR/> this.repository = repository;<BR/> }<BR/><BR/>This way I get the best of both worlds: the caller knows the dependency and can use that constructor (from a test case for example), but the framework can use the default constructor and the ServiceLocator will still retrieve the correct instance.<BR/><BR/>2. Different implementations for different requestors: I haven't hit this problem, I must admit. I have no idea how I could solve a ServiceLocator.GetInstanceOf<string>() call. I am, however, unclear on how a DI container can solve it either.<BR/><BR/>It would seem to me that identifying by type would be easier... I can ask for a ILocalFoo or a IRemoteFoo, even if they're basically identical. Agreed, it does look like a hack, but again, how exactly do you tell the DI container "when X asks for resource R, give it a R1, when Y does the same thing, give it a R2"?<BR/><BR/>3. Circular dependencies: I don't see any reason for a stack overflow, unless A needs a <B>new</B> instance of B, and then in turn B needs a <B>new</B> instance of A and so on. If they are singletons, for example, you bind the actual types in the main() or global.asax, not when you're using them:<BR/><BR/>Bind<IB>.To(() => new A()).CacheBy(InstanceScope.Singleton);<BR/>Bind<IA>.To(() => new B()).CacheBy(InstanceScope.Singleton);<BR/><BR/>A needs an instance of IB:<BR/><BR/>var ib = ServiceLocator.GetInstanceOf<IB>();<BR/><BR/>B needing an instance of IA is similar. No stack overflow.Marcelhttps://www.blogger.com/profile/13951354231483245521noreply@blogger.comtag:blogger.com,1999:blog-5654876.post-33239727762130923152009-01-08T00:45:00.000+02:002009-01-08T00:45:00.000+02:00Hi Marcel,Thanks for your feedback on my article. ...Hi Marcel,<BR/><BR/>Thanks for your feedback on my article. I can see a need for clarifying the points that you're questioning above. They probably deserve more attention than this, but in quick summary:<BR/><BR/>Publicly declaring dependencies: when dependencies appear as constructor parameters or writeable properties, programmers reusing the class effectively have a specification of what they have to provide in order for that class to work. Without such, they'll have to read source code. This is one of the big motivators for this component-oriented style of programming, because it makes it easier to reconfigure/refactor large applications.<BR/><BR/>Different implementations for different requestors: If I start building an app that has only one database connection, then all the components ask for "connectionString". If I then include some components in from another app, that ask for "connectionString" but really mean an MSMQ server connection - with dependency injection I'm fine, with service locator I have to change code. A bigger issue when the services are identified by type (e.g. IFoo).<BR/><BR/>Concurrency, re-entrancy, etc.: take circular dependency detection as an example - if A calls the SL for B, and B calls the SL for A, there is a question of where to keep the state that describes this so that an error other than StackOverflowException can be thrown. The other items relate to similar issues - nothing's ever impossible, but it can get tough.<BR/><BR/>Hope this helps, thanks again for dropping by.<BR/><BR/>NickNickhttps://www.blogger.com/profile/02020072230899910249noreply@blogger.com