在哪些情况下我应该优先考虑存储属性的计算属性?

时间:2016-08-09 19:29:04

标签: swift

我今天看到了这段代码,并想知道为什么你不会使用简单的静态存储属性?

这是我很好奇的代码:

class ApiKeys {

// movie keys
class var HomePage: String { get { return "homepage" } }
class var Id: String { get { return "id" } }
class var Overview: String { get { return "overview" } }
class var PosterPath: String { get { return "poster_path" } }
class var ReleaseDate: String { get { return "release_date" } }
class var Runtime: String { get { return "runtime" } }
class var Tagline: String { get { return "tagline" } }
class var Title: String { get { return "title" } }
class var Rating: String { get { return "vote_average" } }


// query params
class var ApiKey: String { get { return "api_key" } }
class var Query: String { get { return "query" } }
}

这就是我编写相同代码的方式:

class ApiKeys {

static let homePage = "homepage"
static let id = "id"
static let overview = "overview"
static let posterPath = "poster_path"
static let releaseDate = "release_date"
static let runtime = "runtime"
static let tagline = "tagline"
static let title = "title"
static let rating = "vote_average"


//Query Params
static let ApiKey = "api_key"
static let query = "query" 
}

没有必要覆盖变量,因此使用静态应该没问题。我错过了什么吗?在第二种方法中使用第一种方法是否有任何优势或理由?

4 个答案:

答案 0 :(得分:2)

对于它的价值,我根本不会倾向于使用计算或存储的属性。而不是将其定义为class,这似乎是enum的教科书案例:

enum ApiKey: String {
    // movie keys
    case HomePage    = "homepage"
    case Id          = "id"
    case Overview    = "overview"
    case PosterPath  = "poster_path"
    case ReleaseDate = "release_date"
    case Runtime     = "runtime"
    case Tagline     = "tagline"
    case Title       = "title"
    case Rating      = "vote_average"

    // query params
    case ApiKey      = "api_key"
    case Query       = "query"
}

这更准确地捕捉了“键”可以是其中一个值的概念。

你会这样使用它:

if key == ApiKey.HomePage.rawValue {
    ...
}

或者

if ApiKey(rawValue: key) == .HomePage {
    ...
}

在回答您的原始问题时,“我应该何时更喜欢计算属性”,答案是您通常使用它们来检索从其他属性计算的值,并且可选地,如果您想要设置其他(可能是私有的)属性和间接的价值观。如果您只是要返回一些静态的,不变的字符串,那么使用计算属性几乎没有什么好处。

答案 1 :(得分:1)

类var可以被子类覆盖,而静态常量则不能。这是我能想到的第一个区别。

答案 2 :(得分:1)

如果需要,可以使用计算属性在运行时动态更改属性的值,就像在Objective-C中覆盖getter一样。您不能使用static let常量。

答案 3 :(得分:1)

可能有些偏离主题:但是如果使用默认实现定义非蓝图静态计算属性,那么静态存储属性无法使用的一种可能的设计使用方案是一些“常量”协议的扩展。符合这种协议的类/结构/等可以被允许访问类型约束泛型,其中这些泛型是唯一可以访问协议常量的上下文(限制对常量的可访问性),它们保证是常量(因为它们也可以直接从符合该协议的具体类型中使用,但是这些可以用新值“覆盖”“常量”。)

protocol HasAccessToConstants {
    /* since we don't blueprint 'theAnswer', the default
       implementation below will always be used for objects
       conforming to this protocol when used in a generic
       context (even if they attempt to "override" these
       "constants" with implementations of their own, these
       custom ones can only be accessed for concrete-types). */
}

extension HasAccessToConstants {
    static var theAnswer: Int { return 42 }
    /* for protocols: we may implement a default  
       implementation only for computed properties */
}


class Foo : HasAccessToConstants {
    /* Even if the developer implements its own "constant"
       implementation, this will not be used for accessing 
       Foo type in a generic context. */
    static var theAnswer: Int { return 9 }
}

func onlyForObjectsWithAccessToConstants<T: HasAccessToConstants>(obj: T) {
    // do something with obj ...

    // make use of constants available to the type of obj
    print("Constants available to the type of this object (e.g. '\(T.theAnswer)')")
}

onlyForObjectsWithAccessToConstants(Foo())
/* Constants available to the type of this object (e.g. '42') */

// not really "constants" as they can be "overridden" for concrete types
print(Foo.theAnswer) // 9 (since concrete type)

再次,设计,并包括在技术讨论中,因为我不能真正看到在哪种情况下这将比其他更好的替代品更有用。