单例实例和类方法的区别

39

最近在使用Objective-C及其各种库时,我注意到了两种非常流行的单例模式。一种版本获取单例实例并调用其实例方法,另一种版本仅公开类方法,从不提供可操作的实例。它们都旨在抽象化访问单个资源(如StoreKit、CoreData、Parse API等)。例如,以下是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");
 }];

或者使用NSUserDefaults、UIApplication等等。另一个方法可以在MagicalRecord中看到,或者在此处使用Parse API:

或者使用NSUserDefaults、UIApplication等等。另一种方法可以在MagicalRecord中看到,或者在这里使用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];

这两种方法各有优缺点,它们中的一种是否从根本上更好呢?不需要检索共享实例可以节省一些屏幕空间(性能差异可能不重要),但是我是否在其他方面搞砸了自己,例如在测试方面?

谢谢!

2个回答

29

有两种不同的方式来实现基于类方法的方法:

  • 使用一个对所有人隐藏的类创建一个单例实例,并在具有相同签名的包装类方法后面隐藏其方法,或者
  • 创建执行所有工作的类方法

第一种实现的含义是,您可以使用隐藏单例实现的任何内容:

  • 使用子类成为可能
  • 在运行中间切换实例很容易
  • 状态存在实例变量中
  • 初始化遵循熟悉的模式

如果您选择不使用单例的实现,则会依赖于静态变量来保持当前状态。这是一个合法的选择,但初始化模式变得不同(也许甚至使用dispatch_once),您不能在中间切换实现而不依赖于一些丑陋的if条件,使用子类变得更加棘手。

第一种实现的测试比测试第二种实现略微容易,因为您可以通过后门提供单例的单独实现进行测试; 对于基于静态变量的实现,无法采用此路线。

总之,我会使用基于单例的解决方案,单例可以选择隐藏在提供对单例方法访问权限的“门面”后面。我不会使用必须将所有状态放置在静态变量中的实现。


很好的回答,但是一个小例子会让理解更容易。 - atulkhatri

7

单例模式的一个优点是,如果需要,轻松允许其他实例。如果采用类方法的方式,没有大量重构的情况下,这就是你得到的全部内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接