castle windsor - Avoiding Service Locator Antipattern with legacy app not designed for IOC -
i have read service locators in ioc anti-pattern.
last year introduced ioc (ninject specifically) our application @ work. app legacy, it's big , it's fragmented. there lots of ways class, or chain of classes can created. created web framework (which custom), created nhibernate. lots scattered around in weird places.
how handle different scenarios , not come thats not @ least servicelocatorish , not end different kernels in different places (scopes singleton, httprequest , thread important).
edit i'll add little more detail led our current sl pattern.
we don't want multiple kernels. want 1 (and indeed because of sl have 1 static one). here our setup:
1) have ninject modules in 7-8 different projects/assemblies. when our app (a webapp) starts modules gathered via assembly scanning , loaded kernel , placed in service locator. rather expensive.
2) have custom ui framework that's construction happy. think of around 120 tabbed forms each construct 1-10 tab pages part of lifecycle. sl strategically used in 5-6 places cover of either pure resolution or doing post instantiation injection of properties only.
3) under ui not covered top level calls , if classes want use ioc need come own strategy. there various different little frameworks each own little worlds.
so ideal way of doing have read injecting kernel whenever need access ioc...well that's fine , good; keep use of sl minimum.
but getting kernel from? don't want construct , register new 1 everywhere. seems have out of static context or factory can ensure kernel i'm using holding on same scoped objects else using , avoid expense of registering of modules. @ point, whatever thing seems lot service locator right?
keep in mind app huge , tightly coupled. don't have luxury of spending few months in 1 go of refactoring it. sl seemed way work ioc in had time.
so ideal way of doing have read injecting kernel whenever need access ioc...well that's fine , good; keep use of sl minimum.
no, injecting kernel business classes not best way go. better way create factory e.g. ifoofactory { ifoo create(); } or func<ifoo> , let 1 create new instance.
the implementation of interface goes composite root, gets instance of kernel , resolve using kernel. keeps classes free of specific container , can reuse them in project using different container. in case of func can use following module: does ninject support func (auto generated factory)? ninject 2.4 have native support this.
as far refactoring goes, hardly possible tell what's best way go without knowing source code of application. can give approch can work.
i suppose want refactor whole application proper di in long term. did once quite large project (30-40 man-years) following:
start @ composite root(s) , work down object tree , change 1 class after other use proper di. once reached leafs start refactor services not depend on other services , work leafs using same approach. afterwards, continue services depend on services have been refactored , repeat until services refactored. these steps can done 1 after other code continously gets improved while new features can still added @ same time. in mean time servicelocation acceptable, long focus right possible.
pseudo code example:
foo{ servicelocator.get<service1>(), new bar() } bar{ servicelocator.get<iservice1>(), servicelocator.get<iservice2>(), new baz() } baz{ servicelocator.get<iservice3>() } service1 { servicelocator.get<service3>() } service2 { servicelocator.get<service3>() } service3 { new someclass()} step 1 - change root (foo)
foo{ ctor(iservice1, ibar) } bar{ servicelocator.get<iservice1>(), servicelocator.get<iservice2>(), new baz() } baz{ servicelocator.get<iservice3>() } service1 { servicelocator.get<iservice2>() } service2 { servicelocator.get<iservice3>() } service3 { new someclass()} bind<ibar>().to<bar>(); bind<iservice1>().tomethod(ctx => servicelocator.get<iservice1>()); step 2 - change dependencies of root
foo{ ctor(iservice1, ibar) } bar{ ctor(iservice1, iservice2, ibaz) } baz{ servicelocator.get<iservice3>() } service1 { servicelocator.get<service2>() } service2 { servicelocator.get<service3>() } service3 { new someclass()} bind<ibar>().to<bar>(); bind<ibaz>().to<baz>(); bind<iservice1>().tomethod(ctx => servicelocator.get<iservice1>()); bind<iservice2>().tomethod(ctx => servicelocator.get<iservice2>()); step 3 - change dependencies , continue until @ leafs
foo{ ctor(iservice1, ibar) } bar{ ctor(iservice1, iservice2, ibaz) } baz{ ctor(iservice3) } service1 { servicelocator.get<service2>() } service2 { servicelocator.get<service3>() } service3 { new someclass() } bind<ibar>().to<bar>(); bind<ibaz>().to<baz>(); bind<iservice1>().tomethod(ctx => servicelocator.get<iservice1>()); bind<iservice2>().tomethod(ctx => servicelocator.get<iservice2>()); bind<iservice3>().tomethod(ctx => servicelocator.get<iservice3>()); step 4 - refactor services not depend on other ones
foo{ ctor(iservice1, ibar) } bar{ ctor(iservice1, iservice2, ibaz) } baz{ ctor(iservice3) } service1 { servicelocator.get<service2>() } service2 { servicelocator.get<service3>() } service3 { ctor(isomeclass) } bind<ibar>().to<bar>(); bind<ibaz>().to<baz>(); bind<isomeclass>().to<someclass>(); bind<iservice1>().tomethod(ctx => servicelocator.get<iservice1>()); bind<iservice2>().tomethod(ctx => servicelocator.get<iservice2>()); bind<iservice3>().to<service3>().insingletonscope(); step 5 - next refactor depend on services have refactored services dependency.
foo{ ctor(iservice1, ibar) } bar{ ctor(iservice1, iservice2, ibaz) } baz{ ctor(iservice3) } service1 { servicelocator.get<service2>() } service2 { ctor(iservice3) } service3 { ctor(isomeclass) } bind<ibar>().to<bar>(); bind<ibaz>().to<baz>(); bind<isomeclass>().to<someclass>(); bind<iservice1>().tomethod(ctx => servicelocator.get<iservice1>()); bind<iservice2>().to<service2>().insingletonscope(); bind<iservice3>().to<service3>().insingletonscope(); step 6 - repeat until every service refactored.
foo{ ctor(iservice1, ibar) } bar{ ctor(iservice1, iservice2, ibaz) } baz{ ctor(iservice3) } service1 { ctor(iservice2) } service2 { ctor(iservice3) } service3 { ctor(isomeclass) } bind<ibar>().to<bar>(); bind<ibaz>().to<baz>(); bind<isomeclass>().to<someclass>(); bind<iservice1>().to<service1>().insingletonscope(); bind<iservice2>().to<service2>().insingletonscope(); bind<iservice3>().to<service3>().insingletonscope(); probably want switch convention based container configuration refactoring. in case i'd add attribute refactored classes mark them , remove again after refactoring done. in conventions can use attribute filter refactored classes.
Comments
Post a Comment