Can I simulate traits/mixins in Swift?

前端 未结 5 655
南旧
南旧 2021-02-01 16:45

Does Swift have a way of mixing in traits, a la Scala? The section of the Swift book on using extensions to add protocols to existing classes comes tantalizingly close. However,

5条回答
  •  谎友^
    谎友^ (楼主)
    2021-02-01 17:20

    Here's my (not yet widely tested) way of doing what I think are Scala traits in Swift 2.1.1, Playgrounds-ready, two versions:

    Less flexible:

    protocol BigBadProtocol {
        func madFunc() -> String;
        // func otherFunc();
        // Maybe a couple more functions here.
    }
    
    protocol BlueMadFuncUser: BigBadProtocol {}
    
    extension BlueMadFuncUser {
        func madFunc() -> String {
            return "Blue"
        }
    }
    
    protocol RedMadFuncUser: BigBadProtocol {}
    
    extension RedMadFuncUser {
        func madFunc() -> String {
            return "Red"
        }
    }
    
    class ClearClass: BigBadProtocol {
        func madFunc() -> String {
            return "Clear"
        }
    }
    
    class BlueClass: BlueMadFuncUser {}
    
    class RedClass: RedMadFuncUser {}
    

    More flexible:

    protocol BigBadProtocol {
        func madFunc() -> String;
        // func otherFunc();
        // Maybe a couple more functions here.
    }
    
    protocol BlueMadFuncUser {}
    
    extension BigBadProtocol where Self: BlueMadFuncUser {
        func madFunc() -> String {
            return "Blue"
        }
    }
    
    protocol RedMadFuncUser {}
    
    extension BigBadProtocol where Self: RedMadFuncUser {
        func madFunc() -> String {
            return "Red"
        }
    }
    
    class ClearClass: BigBadProtocol {
        func madFunc() -> String {
            return "Clear"
        }
    }
    
    class BlueClass: BigBadProtocol, BlueMadFuncUser {}
    
    class RedClass: BigBadProtocol, RedMadFuncUser {}
    

    Sanity check:

    var classes: [BigBadProtocol] = [ClearClass(), BlueClass(), RedClass()]
    
    // Prints "Clear, Blue, Red\n"
    print((classes.map { $0.madFunc() }).joinWithSeparator(", "))
    
    // Print another way for Playgrounds, which appears to bug out on the lines above
    var s = ""
    for klass in classes {
        s += klass.madFunc() + " "
    }
    print(s)
    

    BlueMadFuncUser and RedMadFuncUser are two versions of a trait. My terminology might be off, but then you can independently create a second trait like that and mix and match in your classes as you please.

    Would be much more challenging or boiler-plate-y to reuse logic like that with an inheritance-based approach.

    I ended up wanting this pattern after finding it very useful in Hack for PHP, where from what I can tell traits are very similar to Scala's: https://docs.hhvm.com/hack/other-features/trait-and-interface-requirements)

提交回复
热议问题