简体   繁体   中英

Factory pattern with different protocols in swift

I am trying to use factory pattern in swift, and give is my code

I have two protocols

protocol MyProtocol1{
    func callLoginAPI()
}

protocol MyProtocol2{
    func callPaymentAPI()
}

I have two structures which conform to these protocols

struct MyManager1: MyProtocol1{
    func callLoginAPI()
    {
        debugPrint("Inisde--MyManager1 and ready to call an Login API")
    }
}

struct MyManager2: MyProtocol2{
    func callPaymentAPI()
    {
        debugPrint("Inisde--MyManager2 and ready to call Payment API")
    }
}

I want to use the factory pattern to create the instance of Manager by passing the Protocol and returning a concrete object of the struct that conforms to that protocol

Example: ManagerFactory.create(MyProtocol1) --> should give me instance of MyManager1 and doing ManagerFactory.create(MyProtocol2) --> should give me instance of MyManager2

I assume that this may not work as I am asking for a concrete type at runtime, so I ended up doing something like this

protocol IManagerFactory{
    func getMyManager1() -> MyProtocol1
    func getMyManager2() -> MyProtocol2
}

struct ManagerFactory: IManagerFactory
{
    func getMyManager1() -> MyProtocol1 {
        return MyManager1()
    }

    func getMyManager2() -> MyProtocol2 {
        return MyManager2()
    }
}

But I am curious if what I am trying to do in the example is achievable, I am using swift 4.2.

I have seen other examples but all of them have the same protocol which they conform to like rectangle, square and circle conform to same protocol shape.

In my case, I have two separate protocols which do completely different things so is that even possible what I am trying to do in the example? OR the way I ended up with is the only way to go about it.

Please suggest what is the best possible approach.

Suggest a swifty option:

protocol Manager {
    // a generic protocol for all managers
}

protocol BadManager: Manager {
    // properties and behaviour of BadManager
    mutating func noBonus()
}

protocol GoodManager: Manager {
    // properties and behaviour of GoodManager
    mutating func bigBonus()
}

protocol ManagerFactory {
    associatedtype Manager
    mutating func createManager() -> Manager
}

struct gm: GoodManager {
    mutating func bigBonus() {
        print("tons of cookies & coffee")
    }
}

struct bm: BadManager {
    mutating func noBonus() {
        print("all fired")
    }
}

enum ManagerCreator<Manager>: ManagerFactory {
    func createManager() -> Manager {
        switch self {
        case .goodManager:
            return gm() as! Manager
        case .badManager:
            return bm() as! Manager
        }
    }
    case goodManager
    case badManager
}

First, I'm very suspicious of a "manager" that is a value (a struct) rather than an instance (a class). Do you really mean to be using structs and protocols here at all? Structs have no identity; two structs with the same properties must be completely interchangeable for each other, and things like that don't usually get named "manager."

What you're describing is certainly writeable. It's just kind of useless. Here's how you write it:

struct ManagerFactory {
    static func create(_ type: MyProtocol1.Protocol) -> MyProtocol1 {
        return MyManager1()
    }

    static func create(_ type: MyProtocol2.Protocol) -> MyProtocol2 {
        return MyManager2()
    }
}

let mgr1 = ManagerFactory.create(MyProtocol1.self)
let mgr2 = ManagerFactory.create(MyProtocol2.self)

But this is just an elaborate way to use method overloading to replace names.

What you seem to want is a single method, but what would its return type be? Even in C#, I don't think this is writable without adding nasty downcasts. (This is the point you and paulw11 discuss in the comments.) This isn't a limitation of Swift; it's a fundamental characteristic of types. Even in JavaScript, you'd need to know what you expect to get back or else you won't know what methods you can call on it (it's just you track that expectation in your head and docs rather than in the compiler and code).

You seem to have created a lot of protocols here, and I'm betting they mostly have exactly one implementation. That's bad Swift. Don't create protocols until you have a specific need for one. Don't create attractions for fun. They will burn you very quickly.

If your protocols really look like this, and you really have different implementations of these methods (ie don't do this if there is only one implementation of MyProtocol1 "just in case"), what you really want are functions:

struct API {
    let login: () -> Void
    let payment: () -> Void
}

let myAPI = API(login: myLoginFunction, payment: myPaymentFunction)

This will let you create different APIs if you need them. But again, only if you need this flexibility. If not, just use classes for instances, and structs for values, and avoid protocols until you have at least 2, and better 3, implementations in your program.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM