Singleton Instance vs Class Methods

2020-02-02 11:58发布

问题:

While recently working with Objective-C and various libraries written in it, I've noticed two really popular singleton patterns. One version fetches the singleton instance and calls its instance methods and other version only exposes class methods and never gives you an instance to work with. All have the purpose of abstracting access to a single resource (StoreKit, CoreData, Parse API etc.). For example, here's the former approach used in MKStoreKit:

// initialize singleton during app boot
[MKStoreManager sharedManager]

// sometime later in the app
[[MKStoreManager sharedManager] buyFeature:kFeatureAId 
                                onComplete:^(NSString* purchasedFeature)
 {
     NSLog(@"Purchased: %@", purchasedFeature);
 }
                               onCancelled:^
 {
     NSLog(@"User Cancelled Transaction");
 }];

or alternatively NSUserDefaults, UIApplication etc.. The other approach can be seen in MagicalRecord or here with Parse API:

// configure API credentials sometime during app boot
[Parse setApplicationId:@"123456"
              clientKey:@"123456"];

// sometime later
PFObject *testObject = [PFObject objectWithClassName:@"TestObject"];
[testObject setObject:@"bar" forKey:@"foo"];
[testObject save];

What are some pros and cons of the two approaches and is one of them fundamentally better than the other?

Not having to retrieve the shared instance saves some screen estate (the performance difference is likely irrelevant), but am I screwing myself in some other way, for example, testability-wise?

Thanks!

回答1:

There are two different ways to implement the approach based on class methods:

  • Make a singleton instance using a class hidden from everybody, and hide its methods behind wrapper class methods with identical signatures, or
  • Make class methods that do all the work

The implications of the first implementation are that everything you can do with a singleton, you can do with the hidden singleton:

  • using a subclass becomes a possibility
  • switching the instance in the middle of the run is easy
  • the state lives in instance variables
  • initialization follows the familiar pattern

If you go for an implementation that does not use a singleton, you would be relying on static variables to keep your current state. That is a legitimate choice, but the initialization pattern becomes different (perhaps even using a dispatch_once), you cannot switch the implementation in the middle without relying on some ugly if conditions, and using a subclass becomes a lot more tricky.

Testing the first implementation is somewhat easier than testing the second one, because you can provide a separate implementation of the singleton for testing, perhaps through the back door; with a static-based implementation, this route cannot be taken.

To summarize, I would use a singleton-based solution, with the singleton optionally hidden behind a "facade" that provides access to singleton's methods. I would not use an implementation where all state must be placed in static variables.



回答2:

One advantage of the singleton approach is that it becomes trivial to allow other instances if you need to. If you take the class method approach, that's all you get without a lot of refactoring.