Sobes.tech
Middle+

How can the use of the Singleton pattern violate the principles of polymorphism?

sobes.tech AI

Answer from AI

The Singleton restricts the creation of class instances to a single object. Polymorphism, on the other hand, assumes the ability to work with objects of different classes through a common interface or base class.

A violation occurs when client code is tightly bound to a specific Singleton class, instead of working with an abstraction (protocol or base class). In this case, it is impossible to substitute another, alternative object that implements the same functionality, because the client expects exactly a Singleton instance.

// Bad practice - client directly depends on ConcreteSingleton
class ConcreteSingleton {
    static let shared = ConcreteSingleton()
    private init() {}

    func performAction() {
        print("Performing action from ConcreteSingleton")
    }
}

// Client
func clientFunction() {
    // Tight coupling to ConcreteSingleton
    ConcreteSingleton.shared.performAction()
}

// Good practice - client depends on protocol
protocol Service {
    func performAction()
}

class RealService: Service {
    func performAction() {
        print("Performing action from RealService")
    }
}

class MockService: Service {
    func performAction() {
        print("Performing action from MockService")
    }
}

// Singleton providing implementation via protocol (optional, can use DI)
class ServiceProvider {
    static let shared = ServiceProvider()
    private init() {}

    private var currentService: Service = RealService()

    func setService(_ service: Service) {
        currentService = service
    }

    func getService() -> Service {
        return currentService
    }
}

// Client
func flexibleClientFunction() {
    // Working through abstraction (protocol)
    let service = ServiceProvider.shared.getService()
    service.performAction()
}

// Examples of polymorphism with flexible client
// ServiceProvider.shared.setService(MockService())
// flexibleClientFunction() will use MockService

Thus, directly using the static shared property of Singleton in client code reduces flexibility and hampers the substitution of implementations, which is a key aspect of polymorphism when working with different object types through a common interface. The solution is to work with the Singleton instance through a protocol or base class.