返回首页

Design patterns by Tutorials— The power of OOP (part 2)

Design Pattern Journey--The Power of OOP 2

Design patterns by Tutorials — The power of OOP (part 2)

Singleton Pattern: pure-singleton and semi-singleton design pattern in Swift Singleton Pattern: Pure Singleton and Semi-Singleton Design Patterns in Swift

Prerequisites — This blog series requires an intermediate level of expertise in object-oriented programming. You should have basic knowledge about class, object, constructor, inheritance, value and reference type. An Intermediates will gain knowledge and experts will sharpen his or her knowledge by reading this series from start to finish. Prerequisites – This blog series requires intermediate expertise in object-oriented programming. You should have a basic understanding of classes, objects, constructors, inheritance, value and reference types. Intermediate users will gain knowledge, while experts will improve their knowledge by reading this series from cover to cover.

Singleton class

Singleton class

In object-oriented programming, a singleton class is a class that can have only one object during the whole life-cycle of an application or project. In object-oriented programming, a singleton class is a class that can have only one object during the entire life cycle of an application or project.

Singleton design pattern is a part of iOS Applications life-cycle code. For example, in an iOS project, the UIApplication class is the best example of a singleton class, which is created by the iOS system on app launch and passed to the AppDelegate as a parameter in application:didFinishLaunchingWithOptions: method. The singleton design pattern is part of the iOS application lifecycle code. For example, in an iOS project, the UIApplication class is the best example of a singleton class, which is created by the iOS system when the app is launched and passed to AppDelegate as a parameter in the application:didFinishLaunchingWithOptions: method.

There are two types of Singleton design patterns.

There are two types of singleton design pattern.

  1. Pure-singleton design pattern Pure-singleton design pattern

  2. Semi-singleton design pattern Semi-singleton design pattern

Pure-Singleton Design Pattern:

Pure-Singleton design pattern:

In this pattern, a programmer who is using the functionality of a pure-singleton class is not allowed to create an instance of the class. A programmer can only call methods and access properties available for the singleton class by using the predefined instance of that class. In this pattern, programmers using the functionality of a pure singleton class are not allowed to create instances of that class. Programmers can call methods and access available properties only by using a predefined instance of a singleton class.

The object of a pure-singleton class is automatically created during application launch with pre-defined parameters mentioned by the developer who created that class. During application startup, objects of pure singleton classes are automatically created using predefined parameters mentioned by the developer who created the class.

Pure-singleton classes must be marked as final to avoid inheritance confusion. In swift, you can use structure also to achieve the same concept. Pure singleton classes must be marked final to avoid inheritance confusion. In swift, you can also use structs to implement the same concept.

A programmer cannot inherit the pure-singleton class. When you try to inherit any class in swift, you must call the constructor of the superclass. Calling the constructor of the superclass is not possible in pure-singleton class because all constructors of the pure-singleton class are always marked as private. Programmers cannot inherit from pure singleton classes. When you try to inherit from any class in swift, you have to call the constructor of the super class. It is not possible to call the constructor of the super class in the pure-singleton class because all the constructors of the pure-singleton class are marked as private.

Let understand this design pattern in a simple way with below example. Let us understand this design pattern in a simple way through the following example.


//PureSingletonDesignPatterExample1.swift :

// Hitendra Solanki
// Pure-singleton design pattern Playground example
final class LogManager {
  //shared and only one available object
  static let logger: LogManager = LogManager(databaseURLEndpoint: "https://www.hitendrasolanki.com/logger/live")
  
  private var databaseURLEndpoint: String
  
  //marked as private, no one is allowed to access this initialiser outside of the class
  private init(databaseURLEndpoint: String) {
    self.databaseURLEndpoint = databaseURLEndpoint
  }
  
  func log(_ value: String...){
    //complex code to connect to the databaseURLEndpoint and send the value to server directly
  }
}

//This is function in playground which executes our test code
func main(){
  LogManager.logger.log("test log from medium blog") //this will log on "/live" endpoint
}

//call main to execute our test code
main()


In the above example, PureSingletonDesignPatterExample1.swift we have marked all init methods as private of LogManger class so no one can create an instance of that class. If we try to create a sub-class of LogManger, the compiler will give you an error. In the above example, PureSingletonDesignPatterExample1.swift we have marked all init methods as private methods of the LogManger class, so no one can create an instance of this class. If we try to create a subclass of LogManger, the compiler will give you an error.

UIApplication, AppDelegate are examples of the pure-singleton class. Have you ever tried to create an object of UIApplication class manually? Try to do so and run the app. [What happened? App crashed, right? -I have tested this crash on swift 5, XCode 10.2] UIApplication and AppDelegate are examples of pure singleton classes. Have you ever tried to manually create an object of class UIApplication? Try doing so and running the application. App crashes, right? - I’ve tested this crashing in swift 5, XCode 10.2]

Limitations of Pure-Singleton Design Pattern,

Limitations of the pure singleton pattern,

We cannot test the pure-singleton class with the test data. In the above example PureSingletonDesignPatterExample1.swift, suppose you want to test the LogManager class by pointing it to some test mode URL instead of production mode URL, we can not do that when we are writing test cases in XCTest target. We cannot test pure singleton classes with test data. In the above example, PureSingletonDesignPatterExample1.swift, assuming you want to test the LogManager class, point it to some test mode URL instead of the production mode URL, we cannot do this when we write test cases in XCTest target.We can overcome this limitation by converting our pure-singleton class to semi-singleton class or by using the dependency injection pattern. I will write a dedicated article on dependency injection pattern. For now, let’s continue with the semi-singleton design pattern. We can overcome this limitation by converting our pure singleton class into a semi-singleton class or by using the dependency injection pattern. I will write an article specifically about dependency injection patterns. Now, let’s continue using the semi-singleton design pattern.

Semi-singleton design pattern:

Semi-singleton design pattern:

  • In semi-singleton design pattern, a programmer who is using the class is allowed to create an object of the singleton class if required. as well as also allowed to call methods and use properties of the singleton class. In the semi-singleton design pattern, the programmer using the class can create objects of the singleton class if needed. As well as properties that allow calling methods and using singleton classes.

  • A programmer can also inherit the semi-singleton class if the singleton is not marked as final by the developer of the singleton class. In semi-singleton design pattern to mark the class as a final is not necessary. Programmers can also inherit from semi-singleton classes if the developer of the singleton class does not mark the singleton class as final. In semi-singleton pattern, it is not necessary to mark the class as final.


//SemiSingletonDesignPatterExample1.swift
// Hitendra Solanki
// Semi-singleton design pattern Playground example
//In semi-singleton design pattern, marking the class as final is optional
final class LogManager {
  //shared object
  static let logger: LogManager = LogManager(databaseURLEndpoint: "https://www.hitendrasolanki.com/logger/live")
  
  private var databaseURLEndpoint: String
  
  //not marked as private, anyone is allowed to access this initialiser outside of the class
  init(databaseURLEndpoint: String) {
    self.databaseURLEndpoint = databaseURLEndpoint
  }
  
  func log(_ value: String...){
    //complex code to connect to the databaseURLEndpoint and send the value to server directly
  }
}

//This is function executes our main code
func main(){
  LogManager.logger.log("main log from medium blog on live server endpoint") //this will log on "/live" endpoint
}


// This is function executes our TEST MODE code
// Here in playground, Hitendra Solanki created this method for the demostratino purpose only
// Usually we write this kind of test codes, inside the test targe of the XCode-project
func testThatLogManger(){
  
  //we are allowed to create an instace of class LogManager,
  //because it follows the Semi-Singleton design patterns
  let logManagerTestObject = LogManager(databaseURLEndpoint: "https://www.hitendrasolanki.com/logger/test")
  
  logManagerTestObject.log("test log from medium blog on test server endpoint") //this will log on "/test" endpoint
}

main() //call main
testThatLogManger() //call test execution

  • In the above example, SemiSingletonDesignPatterExample1.swift, we have not marked the init method as private, so we can create multiple instances of the semi-singleton class to remove the dependency from the property databaseURLEndpoint. Now we can create an object of the LogManger class by using any databaseURLEndpoint value, now our class LogManger follows the semi-singleton pattern. In the above example, SemiSingletonDesignPatterExample1.swift, we did not mark the init method as private, so we can create multiple instances of the semi-singleton class to remove the dependency from the property databaseURLEndpoint. Now we can create objects of LogManger class using any databaseURLEndpoint value and now our class LogManger follows semi-singleton pattern.

UserDefault, FileManager, NotificationCenter are the example of semi-singleton classes. We can use pre-defined shared objects of UserDefault, FileManager, and NotificationCenter which are UserDefault.standard, FileManager.default and NotificationCenter.default. UserDefault, FileManager, and NotificationCenter are examples of semi-singleton classes. We can use predefined shared objects such as UserDefault, FileManager and NotificationCenter, which are UserDefault.standard, FileManager.default and NotificationCenter.default.

The pre-defined object of pure or semi-singleton classes always lives in memory and never destroyed until you close the application. A programmer-defined object of semi singleton class destroyed after the scope of the object is completed. Predefined objects of pure or semi-singleton classes always exist in memory and are not destroyed until the application is closed. A programmer-defined object of a semi-singleton class is destroyed after the object’s scope is completed.

Question: Which is a better way, “Pure-singleton design pattern” or “Semi-singleton design pattern”, when I should use the first one and when other one? Question: Which one is better, “Pure Singleton Design Pattern” or “Semi-Singleton Design Pattern”? Under what circumstances should it be used?

This totally depends on the responsibilities of your class. If you have created singleton class in your project and needs to change some of the properties at some point or if you want to add more responsibilities to the class by adding new methods in future and if you require its test cases also with mock data for newly added methods, you should go with the semi-singleton design pattern. It all depends on the functionality of your class. If you have created a singleton class project and need to change some properties at some point, or in the future you want to add new methods, and you need to use mock data for testing of the newly added methods, you should adopt the semi-singleton design pattern.

If you created your class, you are done with unit testing and want to publish it via a framework or library, you can go with pure-singleton design patterns. Because you created the class and some other developer will use it, so unit testing of your singleton-class is your responsibility before release, not the responsibility of the developer who is using it. If you have created your class, you have unit tested it and want to distribute it through a framework or library, you can use the Pure Singleton design pattern. Because you created this class, some other developers will use it, so unit testing the singleton class before releasing it is your responsibility, not the responsibility of the developers using it.

Click to view the original text

FAQ

读完之后,下一步看什么

如果还想继续了解,可以从下面几个方向接着读。

Related

继续阅读

这里整理了同分类、同标签或同类问题的文章。