Dependency Injection vs Service Location

前端 未结 11 1364
梦如初夏
梦如初夏 2020-11-28 03:59

I am currently weighing up the advantages and disadvantages between DI and SL. However, I have found myself in the following catch 22 which implies that I should just use SL

相关标签:
11条回答
  • 2020-11-28 04:54

    I know that people are really saying DI is the only good IOC pattern but I don't get this. I will try to sell SL a bit. I will use the new MVC Core framework to show you what I mean. First DI engines are really complex. What people really mean when they say DI, is use some framework like Unity, Ninject, Autofac... that do all the heavy lifting for you, where SL can be as simple as making a factory class. For a small fast project this is an easy way to do IOC without learning a whole framework for proper DI, they might not be that difficult to learn but still. Now to the problem that DI can become. I will use a quote from MVC Core docs. "ASP.NET Core is designed from the ground up to support and leverage dependency injection." Most people say that about DI "99% of your code base should have no knowledge of your IoC container." So why would they need to design from ground up if only 1% of code should be aware of it, didn't old MVC support DI? Well this is the big problem of DI it depends on DI. Making everything work "AS IT SHOULD BE DONE" takes a lot of work. If you look at the new Action Injection is this not depending on DI if you use [FromServices] attribute. Now DI people will say NO you are suppose to go with Factories not this stuff, but as you can see not even people making MVC did it right. The problem of DI is visible in Filters as well look at what you need to do to get DI in a filter

    public class SampleActionFilterAttribute : TypeFilterAttribute
    {
        public SampleActionFilterAttribute():base(typeof(SampleActionFilterImpl))
        {
        }
    
        private class SampleActionFilterImpl : IActionFilter
        {
            private readonly ILogger _logger;
            public SampleActionFilterImpl(ILoggerFactory loggerFactory)
            {
                _logger = loggerFactory.CreateLogger<SampleActionFilterAttribute>();
            }
    
            public void OnActionExecuting(ActionExecutingContext context)
            {
                _logger.LogInformation("Business action starting...");
                // perform some business logic work
    
            }
    
            public void OnActionExecuted(ActionExecutedContext context)
            {
                // perform some business logic work
                _logger.LogInformation("Business action completed.");
            }
        }
    }
    

    Where if you used SL you could have done this with var _logger = Locator.Get();. And then we come to the Views. With all there good will regarding DI they had to use SL for the views. the new syntax @inject StatisticsService StatsService is the same as var StatsService = Locator.Get<StatisticsService>();. The most advertised part of DI is unit testing. But what people and up doing is just testing there mock services with no purpose or having to wire up there DI engine to do real tests. And I know that you can do anything badly but people end up making a SL locator even if they don't know what it is. Where not a lot of people make DI without ever reading on it first. My biggest problem with DI is that the user of the class must be aware of the inner workings of the class in other to use it.
    SL can be used in a good way and has some advantages most of all its simplicity.

    0 讨论(0)
  • 2020-11-28 04:55

    My opinion is that it depends. Sometimes one is better and sometimes another. But I'd say that generaly I prefer DI. There are few reasons for that.

    1. When dependency is injected somehow into component it can be treated as part of its interface. Thus its easier for component's user to supply this dependecies, cause they are visible. In case of injected SL or Static SL that dependencies are hidden and usage of component is a bit harder.

    2. Injected dependecies are better for unit testing cause you can simply mock them. In case of SL you have to setup Locator + mock dependencies again. So it is more work.

    0 讨论(0)
  • 2020-11-28 04:56

    I have used the Google Guice DI framework in Java, and discovered that it does much more than make testing easier. For example, I needed a separate log per application (not class), with the further requirement that all my common library code use the logger in the current call context. Injecting the logger made this possible. Admittedly, all the library code needed to be changed: the logger was injected in the constructors. At first, I resisted this approach because of all the coding changes required; eventually I realized that the changes had many benefits:

    • The code became simpler
    • The code became much more robust
    • The dependencies of a class became obvious
    • If there were many dependencies, it was a clear indication that a class needed refactoring
    • Static singletons were eliminated
    • The need for session or context objects disappeared
    • Multi-threading became much easier, because the DI container could be built to contain just one thread, thus eliminating inadvertent cross-contamination

    Needless to say, I am now a big fan of DI, and use it for all but the most trivial applications.

    0 讨论(0)
  • 2020-11-28 04:56

    This is regarding the 'Service Locator is an Anti-Pattern' by Mark Seeman. I might be wrong here. But I just thought I should share my thoughts too.

    public class OrderProcessor : IOrderProcessor
    {
        public void Process(Order order)
        {
            var validator = Locator.Resolve<IOrderValidator>();
            if (validator.Validate(order))
            {
                var shipper = Locator.Resolve<IOrderShipper>();
                shipper.Ship(order);
            }
        }
    }
    

    The Process() method for OrderProcessor does not actually follow the 'Inversion of Control' principle. It also breaks the Single Responsibility principle at the method level. Why should a method be concerned with instantiating the

    objects(via new or any S.L. class) it needs to accomplish anything.

    Instead of having the Process() method create the objects the constructor can actually have the parameters for the respective objects(read dependencies) as shown below. Then HOW can a Service Locator be any different from a IOC

    container. AND it will aid in Unit Testing as well.

    public class OrderProcessor : IOrderProcessor
    {
        public OrderProcessor(IOrderValidator validator, IOrderShipper shipper)
        {
            this.validator = validator; 
            this.shipper = shipper;
        }
    
        public void Process(Order order)
        {
    
            if (this.validator.Validate(order))
            {
                shipper.Ship(order);
            }
        }
    }
    
    
    //Caller
    public static void main() //this can be a unit test code too.
    {
    var validator = Locator.Resolve<IOrderValidator>(); // similar to a IOC container 
    var shipper = Locator.Resolve<IOrderShipper>();
    
    var orderProcessor = new OrderProcessor(validator, shipper);
    orderProcessor.Process(order);
    
    }
    
    0 讨论(0)
  • 2020-11-28 05:02

    Sometimes logging can be implemented using AOP, so that it doesn't mix with business logic.

    Otherwise, options are :

    • use an optional dependency (such as setter property), and for unit test you don't inject any logger. IOC container will takes care of setting it automatically for you if you run in production.
    • When you have a dependency that almost every object of your app is using ("logger" object being the most commmon example), it's one of the few cases where the singleton anti-pattern becomes a good practice. Some people call these "good singletons" an Ambient Context: http://aabs.wordpress.com/2007/12/31/the-ambient-context-design-pattern-in-net/

    Of course this context has to be configurable, so that you can use stub/mock for unit testing. Another suggested use of AmbientContext, is to put the current Date/Time provider there , so that you can stub it during unit test, and accelerates time if you want.

    0 讨论(0)
提交回复
热议问题