Can dependency injection like Typhoon replace multiple singletons?
Asked Answered
K

1

5

I have an app that has about 11 different Singleton instances used throughout the many app's methods and classes; it's getting out of hand, and I want to replace all of them with dependency injection, like Typhoon. However, I am unable to find any doc, sample or mention of how to replace the singletons with dependency injection, including Typhoon. For instance, do I use multiple instances of Typhoon, replacing each singleton with an instance of Typhoon?

Kimberli answered 30/12, 2014 at 22:46 Comment(0)
J
9

Edit: Pilgrim is the official Swift successor to Typhoon!!


Typhoon creator here. Yes, one of the uses of dependency injection it to provide the benefits of singletons without the drawbacks. But you don't necessarily need a library to apply the dependency injection pattern and replace your singletons. In fact it helps to understand the pattern by looking at how to implement it without a framework first:

Hollywood Principle: Don't call us, we'll call you

High level classes, like view controllers defer to collaborators to do their work. As you've mentioned you have around 11 of these. Now there's two ways to couple your class to the collaborator:

Seek out (call) the collaborator:

initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle 
{
    self = [super initWithNibName:nibname bundle:bundle];
    if (self) {
        _serviceClient = [MyServiceClient sharedInstance];
    }
} 

And the above way works, but its not good because:

  • If you want to provide an alternative implementation, then you have to go and change each class that uses it.
  • It makes writing clean unit or integration tests difficult. You have to look at the class's internals (glass box testing) instead of focus on the external interface contract (black box testing).
  • It provides overly tight coupling, which leads to poor cohesion.

The alternative:

Simply pass the collaborator in, via an init method or a property.

initWitServiceClient:(id<ServiceClient>)serviceClient
{
    self = [super initWithNibName:@"MyNib" bundle:[NSBundle mainBundle]; 
    if (self) {
        _serviceClient = serviceClient;
    }
} 

How is this different to just . . . passing arguments?

Instead of hard-wiring a collaborator you pass it in as an argument. But now the next class up is hard-wired! So you keep doing this until you have a top level assembly class, that knows how to build your view controller (and other classes) from the individual pieces. If you do this:

  • All of the references to each singleton points to one place. So replacing one of your singletons with a compatible implementation requires only one line of code change. Similarly you have encapsulated the configuration for this class.
  • Its easy to write unit and integration tests for your classes. In the latter case, you can patch out one component for another. This can overcome two of the drawbacks of integration-style tests. The first being that its difficult to put the system in the required state for a test scenario, and the second being that they can have unwanted, side-effects. Meanwhile, pure unit testing is simplified too, since its now easy to see the dependency contract and pass in mocks or stubs for these.
  • Classes clearly document their "seams" and what are the significant collaborators they'll be delegating to in order to perform their tasks. This leads to "high cohesion".

Now Using Typhoon:

You'll have one retained instance of Typhoon per normal app. It will hold your singletons.

If after studying the above material you have a more specific question we'd be very happy to help you.

Jeffreyjeffreys answered 31/12, 2014 at 3:49 Comment(4)
HI Jasper... this is what I've been looking for... let me "cogitate" on it for a bit and I'll probably get back to you. Thank you very much. Have a safe New Year! :D By the way, I like your website(s)... very creative!Kimberli
You're welcome and thanks for the kind words about the website(s) :) (Actually, if you're interested, its a simple process: purchase some nice HTML5 templates online, and then add content. For the AppsQuick.ly one, the illustration was kindly done by Loud&Clear in Melbourne, Australia <--- I asked for an island and some coconuts and sure enough. . . Wishing you a happy & peaceful 2015 too!Jeffreyjeffreys
Why do you use self.serviceClient = serviceClient; in initWitServiceClient instead of _serviceClient = serviceClient as per this SO question?Deutzia
@Ríomhaire No reason, the way you suggested is almost always better. Edited answer.Jeffreyjeffreys

© 2022 - 2024 — McMap. All rights reserved.