协议,结构,但没有具体类型

时间:2017-07-17 23:11:39

标签: swift generics protocols

好的,我有一个名为Environment

的协议
protocol Environment {
  var rootURL: String {get}
}

然后是两个结构:

struct Production: Environment {
  var rootURL = "www.api.mybackend.com/v1"
}

struct Development: Environment {
  var rootURL = "www.api.mydevelopmentbackend.com/v1"
}

具有检索环境的函数的设置对象:

class Settings {
  func getEnvironment<T>() -> T where T: Environment {
    let environmentRaw = self.retreiveEnvironmentFromLocalStore()
    switch environmentRaw {
    case 0:
        return Development() as! T
    case 1:
        return Production() as! T
    default:
        return Development() as! T
    }
  }

  func retreiveEnvironmentFromLocalStore() -> Int {
    //Realm, SQLLite, Core Date, I don't really care for this example. 
    //Let's just say it defaults to returning 1
  }
}

我真的想继续向Protocol Oriented Programming方向移动,但现在当我在一个设置对象上调用此函数并尝试使用rootURL属性时,编译器会抱怨它无法弄清楚类型。所以,为了更好地理解并找到解决方案:

1)如果我访问一个协议定义的属性,它至少知道返回类型是否符合?为什么它关心类型?

2)结构不具有遗传。我应该定义基类并忘记泛型吗?

3)我可以让我的getEnvironment功能更好吗?我不喜欢强制施法,这似乎是一种代码味道。

4)我甚至在这里正确使用泛型吗?

编辑1:

要清楚,我想要一个函数返回一个我知道将具有此属性的结构。

2 个答案:

答案 0 :(得分:3)

  

我是否在这里正确使用泛型?

不,我不这么认为。您说getEnvironment将返回T,它可以是客户端代码指定的任何类型,只要它实现Environment即可。但是,该方法的实现不会这样做。它只返回两种Environment,它返回的类型不是由客户端代码决定的。返回的类型取决于retreiveEnvironmentFromLocalStore返回的内容。因此,在这种情况下,泛型不适合。

由于它返回的环境类型是由方法的实现决定的,而不是客户端代码,你应该在这里使用多态 - 让它返回Environment代替:

  func getEnvironment() -> Environment {
    let environmentRaw = self.retreiveEnvironmentFromLocalStore()
    switch environmentRaw {
    case 0:
        return Development()
    case 1:
        return Production()
    default:
        return Development()
    }
  }

答案 1 :(得分:1)

  

我真的想继续推进面向协议的编程方向

我建议您尝试以明确,可理解的代码方向移动,无论时尚方向如何。

这是你的功能声明:

func getEnvironment() - &gt; T其中T:环境

这表示getEnvironment()将返回某种类型T的对象,其中T在编译时推断 ,基于调用{{}的代码1}}。

getEnvironment()可以使用哪些类型?它可以是TProduction。例如,您可以写:

Development

这使得编译器可以推断let e: Production = Settings().getEnvironment() 在此调用站点返回getEnvironment()Production}。

但是有一个问题:Environment可能会尝试根据getEnvironment()内的随机数生成器返回Development。如果无法将retreiveEnvironmentFromLocalStore投射到Development,您将在运行时发生

为什么你认为Production需要是通用的?根据您问题中的代码,它不应该是。

getEnvironment()

样式说明:Swift API Design Guidelines建议我们

  
      
  • 根据副作用命名功能和方法

         
        
    • 没有副作用的人应该读作名词短语,例如: import Foundation protocol Environment { var rootURL: String {get} } struct Production: Environment { var rootURL = "www.api.mybackend.com/v1" } struct Development: Environment { var rootURL = "www.api.mydevelopmentbackend.com/v1" } class Settings { func getEnvironment() -> Environment { let environmentRaw = self.retreiveEnvironmentFromLocalStore() switch environmentRaw { case 1: return Production() default: return Development() } } func retreiveEnvironmentFromLocalStore() -> Int { return Int(arc4random()) } } let settings = Settings() let e = settings.getEnvironment()
    •   
  •   

除非x.distance(to: y), i.successor()中的方法有重要的副作用,否则更好的名称将是Settingsenvironment()