Singleton pattern... the wrong way.


  • Winner of the 2016 Presidential Election

    @dkf said in Singleton pattern... the wrong way.:

    Yes… but your code shouldn't talk directly to the directory.

    Also, this. If you have a "Service container" object floating around in your application, which ever single class uses and asks for its actual dependencies, you're not doing DI correctly. You just created a God object and loads of global state. (And you should find another job immediately, because you suck at programming.)

    Instead, only the services each class actually depends on should be passed the constructor. The only code that talks to the object maintaining the global state should be setup code and factories.



  • @RaceProUK Yeh, It is kinda weird being able to just "do something" when in the past you couldn't. You have to de-program yourself a bit.



  • @asdf said in Singleton pattern... the wrong way.:

    The last point is what you seem to be missing. If there is some other object (container) that ensures only one instance of the class is ever created, you're only hard-coding the assumption "there can only ever be one" in a single class instead of everywhere in your application. I would say it's pretty obvious that this is preferable.

    Yeah, but in the singleton pattern, the place where you code the assumption is in the static property that grabs the instance. All you did is move that static method from the singleton's definition to the IoC's definition.

    I mean, if you did it with a const public variable, then you'd have that problem.


  • Discourse touched me in a no-no place

    @asdf said in Singleton pattern... the wrong way.:

    Instead, only the services each class actually depends on should be passed the constructor. The only code that talks to the object maintaining the global state should be setup code and factories.

    The first rule of Spring DI is don't talk about Spring DI.



  • @dkf Also DI, AutoFac makes everything more complicated and half the time nobody cares about the fucking unit tests.


  • Discourse touched me in a no-no place

    @xaade said in Singleton pattern... the wrong way.:

    Yeah, but in the singleton pattern, the place where you code the assumption is in the static property that grabs the instance. All you did is move that static method from the singleton's definition to the IoC's definition.

    The point is (with some minor concretisation of the situation) your class instance happens to use this DAO object for talking to its database, but it doesn't need to know or care if there are other DAO objects about, or how many databases are in use. Those things are none of its business. Therefore the DAO shouldn't be done with static anythings at all, despite the fact that it is entirely plausible for an application to only actually happen to have a single DAO for all it does.



  • @dkf said in Singleton pattern... the wrong way.:

    Therefore the DAO shouldn't be done with static anythings at all, despite the fact that it is entirely plausible for an application to only actually happen to have a single DAO for all it does.

    Your IoC can literally be this.

    IoC
    {
        public static SingletonService
        {
            get { /* code that doesn't return the same instance every time */ }
        }
        public static make<T>()
        {
            if (typeof(T) == "beetle") return new beetle(IoC.SingletonService);
        }
    }
    
    CreatingClass
    {
        public method()
        {
            var beetle = IoC.make<beetle>();
        }
    }
    

    It's still a static method that could return a singleton. No different from

    Singleton
    {
       public static SingletonService
       {
           get { /* code that doesn't return the same instance every time */ }
       }
    }
    CreatingClass
    {
        public method()
        {
            var beetle = new beetle(Singleton.SingletonService);
        }
    }
    

    Where the second code is exactly the Singleton pattern, but can just as easily be altered to not be singlton.


  • Discourse touched me in a no-no place

    @xaade I think I don't understand what point you're making there. 😕

    EINSUFFICIENTCOFFEE



  • @dkf said in Singleton pattern... the wrong way.:

    @xaade I think I don't understand what point you're making there. 😕

    EINSUFFICIENTCOFFEE

    The second example IS the singleton pattern, but the same level of abstraction is provided sufficient to modify it to not be singleton.

    The only difference is who you're asking for the singleton.

    This is why I don't like the argumentation on patterns. It's almost always soapboxes.


  • Winner of the 2016 Presidential Election

    @xaade This example is not what we're proposing. You're still creating the object inside the method that uses it. So you're still hiding the dependency instead of injecting it into the constructor.

    You're absolutely right, that's just as bad as a Singleton. It's also not what we're talking about.



  • @asdf said in Singleton pattern... the wrong way.:

    You're still creating the object inside the method that uses it. So you're still hiding the dependency instead of injecting it into the constructor.

    No, there's a difference between dependencies and children. The dependencies should be injected, but if I'm writing UI and I have a list of cars in my display, those cars don't get injected.

    In my first example, the beetle is even created externally. It is requested.
    In my second example, the beetle is explicitly created by the parent.

    In neither case should the beetle be injected.

    The beetle has a dependency on the singleton service and that is injected both times. In the first example it's injected by IoC, in the second example it's injected by the parent.

    In both examples, we don't have to rewrite the entire program to change the singleton to not be a singleton.


  • Discourse touched me in a no-no place

    Classic Dependency Injection via Constructor (in sort-of Java syntax)

    class Foo {
        final Bar bar;
    
        Foo(Bar barService) {
            bar = barService;
        }
    
        doStuff() {
            bar.whatever(...);
        }
    }
    
    main() {
        Bar bar = new Bar();
        Foo foo = new Foo(bar);
    
        foo.doStuff();
    }
    

    With more complex containers, you don't do the instantiating of Foo and Bar by hand, but rather ask the container to do it; it solves what the order is and what gets passed where. All you really do at the top level is ask for a fully-configured-and-ready-to-go Foo and then use it.

    main() {
        Container app = ...;
    
        Foo foo = app.giveMeMy("foo");
    
        foo.doStuff();
    }
    

    The details may vary a lot.


  • Winner of the 2016 Presidential Election

    @xaade said in Singleton pattern... the wrong way.:

    In both examples, we don't have to rewrite the entire program to change the singleton to not be a singleton.

    But you didn't get rid of the static method call which hides a dependency. You just moved the problem up one layer to a God object, which is not what we proposed.

    I'm still not sure what you're trying to prove.



  • @xaade said in Singleton pattern... the wrong way.:

    In my first example, the beetle is even created externally. It is requested.
    In my second example, the beetle is explicitly created by the parent.
    In neither case should the beetle be injected.

    In your case, you should probably inject a factory.

    public class Beetle
    {
        private SingletonService _service;
        public Beetle(SingletonService service)
        {
             _service = service;
        }
    }
    
    public class BeetleFactory
    {
        private SingletonService _service;
        public BeetleFactory(SingletonService service)
        {
             _service = service;
        }
    
        public Beetle MakeBeetle()
        {
            return new Beetle(_service);
        }
    }
    
    public class CreatorClass
    {
        private BeetleFactory _factory;
        public CreatorClass(BeetleFactory factory)
        {
            _factory = factory;
        }
        
        public void DoStuff()
        {
            var beetle = _factory.GetBeetle();
        }
    }
    
    // at composition root
    var mainObject = IoC.Resolve<CreatorClass>();
    
    

Log in to reply