How to avoid Service Locator Anti-Pattern?

前端 未结 3 1377
自闭症患者
自闭症患者 2020-12-17 17:01

I\'m trying to remove a Service Locator from an abstract base class, but I\'m not sure what to replace it with. Here is a psuedo-example of what I\'ve got:

p         


        
3条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-17 17:32

    I agree with @chrisichris and @Mark Seemann.

    Ditch the kernel from the controller. I'd switch your resolver composition a little bit so that your controller can remove the dependency on the IoC container and allow the resolver to be the only item that worries about the IoC container.

    Then I would let the resolver get passed into the constructor of the controller. This will allow your controller to be far more testable.

    For example:

    public interface IMyServiceResolver
    {
        List Resolve(Type[] types);
    }
    
    public class NinjectMyServiceResolver : IMyServiceResolver
    {
        private IKernal container = null;
    
        public NinjectMyServiceResolver(IKernal container)
        {
            this.container = container;
        }
    
        public List Resolve(Type[] types)
        {
            List services = new List();
    
            foreach(var type in types)
            {
                IMyServiceInterface instance = container.Get(type);
                services.Add(instance);
            }
    
            return services;
        }
    }
    
    public abstract class MyController : Controller
    {
        private IMyServiceResolver resolver = null;
    
        public MyController(IMyServiceResolver resolver) 
        { 
            this.resolver = resolver;
        }
    
        protected void DoActions(Type[] types)
        {
            var services = resolver.Resolve(types);
    
            foreach(var service in services)
            {
                service.DoAction();
            }
        }
    }
    

    Now your controller isn't coupled to a specific IoC container. Also your controller is much more testable since you can mock the resolvers and not require an IoC container at all for your tests.

    Alternatively, if you don't get to control when a controller is instantiated, you can modify it slightly:

    public abstract class MyController : Controller
    {
        private static IMyServiceResolver resolver = null;
    
        public static InitializeResolver(IMyServiceResolver resolver)
        {
            MyController.resolver = resolver;
        }
    
        public MyController() 
        { 
            // Now we support a default constructor
            // since maybe someone else is instantiating this type
            // that we don't control.
        }
    
        protected void DoActions(Type[] types)
        {
            var services = resolver.Resolve(types);
    
            foreach(var service in services)
            {
                service.DoAction();
            }
        }
    }
    

    You would then call this at your application start up to initialize the resolver:

    MyController.InitializeResolver(new NinjectMyServiceResolver(kernal));
    

    We did this to handle elements created in XAML who require dependencies resolved but we wanted to remove Service Locator like requests.

    Please excuse any syntactical errors :)

    I'm writing a blog post series on the topic of refactoring an MVVM application with Service Locator calls in the view models you might find interesting. Part 2 is coming soon :)

    http://kellabyte.com/2011/07/24/refactoring-to-improve-maintainability-and-blendability-using-ioc-part-1-view-models/

提交回复
热议问题