如何在swift 3中扩展协议的功能?

时间:2017-05-20 12:22:06

标签: swift3

我有以下代码:

protocol Protocol_1 {
    var prop_1: String { get set }
    var prop_2: Int { get set }
}

protocol Protocol_2 {
    var prop_3: Double { get set }
    var prop_4: Bool { get set }
}

extension Protocol_1 {
    mutating func set<T>(value: T, forKey key: String)
    {
        switch key {
        case "prop_1":
            if value is String {
                prop_1 = value as! String
            }

        case "prop_2":
            if value is Int {
                prop_2 = value as! Int
            }

        default:
            break
        }
    }
}

extension Protocol_2 {
    mutating func set<T>(value: T, forKey key: String)
    {
        switch key {
        case "prop_3":
            if value is Double {
                prop_3 = value as! Double
            }

        case "prop_4":
            if value is Bool {
                prop_4 = value as! Bool
            }

        default:
            break
        }
    }
}

struct MyStruct : Protocol_1, Protocol_2 {
    var prop_1: String
    var prop_2: Int
    var prop_3: Double
    var prop_4: Bool
}

var myStruct = MyStruct(prop_1: "hello", prop_2: 0, prop_3: 3.5, prop_4: true)
myStruct.set(value: "bye", forKey: "prop_1")

现在游乐场给我一个错误,因为不清楚应该调用什么 set 函数。 游乐场执行失败:错误:ProtocolsPlayground.playground:59:1:错误:模糊使用&#39; set(value:forKey :)&#39; myStruct.set(值:&#34;再见&#34;,forKey:&#34; prop_1&#34;)

这很清楚,但我怎样才能实现这样的功能扩展或是否有解决方法?特别是如果 Protocol_1 不可编辑。

1 个答案:

答案 0 :(得分:2)

我不知道如何使用协议扩展现有功能或其他协议的功能。

我建议您将使用第三个协议分配属性的机制与两个协议分开。

这是解决这个问题的一种方法:

定义一个类,它将根据名称(键)和变量引用之间的映射来处理获取和设置属性:

class PropertyMapper
{   
   static var sharedGetter = PropertyMapperGet()
   static var sharedSetter = PropertyMapperSet()

   var value   : Any = 0
   var success       = false
   var key           = ""

   func map<T>(_ key:String, _ variable:inout T) {}

   func clear()
   {
      value   = 0
      success = false
      key     = ""
   }
}

class PropertyMapperGet:PropertyMapper
{
   func get(forKey:String)
   {
     key     = forKey
     success = false
   }

   override func map<T>(_ key:String, _ variable:inout T) 
   {
      if !success && self.key == key 
      {
        value        = variable 
        success      = true
      }
   }

}

class PropertyMapperSet:PropertyMapper
{
   override func map<T>(_ key:String, _ variable:inout T) 
   {
      if  !success && self.key == key,
      let newValue = value as? T 
      { 
        variable     = newValue
        success      = true 
      }
   }

   func set(value newValue:Any, forKey:String)
   {
     key     = forKey
     value   = newValue
     success = false
   }
}

然后,您可以为所有能够按名称(键)分配其属性的结构和类定义协议:

protocol MappedProperties
{
   mutating func mapProperties(_ :PropertyMapper)
}

extension MappedProperties
{
   mutating func get(_ key:String) -> Any?
   {
      let mapper = PropertyMapper.sharedGetter
      defer { mapper.clear() }
      mapper.get(forKey:key)
      mapProperties(mapper)
      return mapper.success ? mapper.value : nil
   }

   @discardableResult
   mutating func set(value:Any, forKey key:String) -> Bool
   {
      let mapper = PropertyMapper.sharedSetter
      defer { mapper.clear() }
      mapper.set(value:value, forKey:key)
      mapProperties(mapper)
      return mapper.success
   }
}

您的协议可能要求采用它们的结构提供命名分配。 (进一步了解如何使属性映射成为协议的一部分)

protocol Protocol_1:MappedProperties 
{
    var prop_1: String { get set }
    var prop_2: Int    { get set }
}

protocol Protocol_2:MappedProperties 
{
    var prop_3: Double { get set }
    var prop_4: Bool   { get set }
}

您的结构需要实现属性映射才能采用您的协议。键/变量映射在整个结构的集中函数中执行,需要为两个协议中的所有变量提供密钥。

struct MyStruct : Protocol_1, Protocol_2 
{
    var prop_1: String
    var prop_2: Int
    var prop_3: Double
    var prop_4: Bool

    mutating func mapProperties(_ mapper:PropertyMapper)
    {
       mapper.map("prop_1", &prop_1)
       mapper.map("prop_2", &prop_2)
       mapper.map("prop_3", &prop_3)
       mapper.map("prop_4", &prop_4)       
    }
}

这允许struct按名称(键)分配属性:

var myStruct = MyStruct(prop_1: "hello", prop_2: 0, prop_3: 3.5, prop_4: true)
myStruct.set(value: "bye", forKey: "prop_1")

为了进一步优化并使属性映射成为协议的一部分,您可以向协议添加映射函数,以便采用它的结构不必知道此映射的详细信息。

他们仍然需要实现映射协议的功能,但他们可以简单地使用协议提供的功能来完成这项工作。

例如(我只显示了Protocol_1,但您可以同时使用它们):

extension Protocol_1
{
   mutating func mapProtocol_1(_ mapper:PropertyMapper)
   {
       mapper.map("prop_1", &prop_1)
       mapper.map("prop_2", &prop_2)
   }
}

使用协议提供的功能,结构不需要知道映射了哪些属性。这应该使结构和协议的维护不易出错,并避免重复。

struct MyStruct : Protocol_1, Protocol_2 
{
    var prop_1: String
    var prop_2: Int
    var prop_3: Double
    var prop_4: Bool

    mutating func mapProperties(_ mapper:PropertyMapper)
    {
       mapProtocol_1(mapper)
       mapper.map("prop_3", &prop_3)
       mapper.map("prop_4", &prop_4)       
    }
}