Swift中的关联类型

动机

可重用性是软件开发中的重要组成部分。 我认为,软件体系结构中最重要的考虑因素之一就是即将推出的功能的实现时间。 我经常有片刻,然后躺在床上,直到实施了该死的功能后才入睡。 因此,快速的实施时间可以为我节省大量的睡眠时间(这是我绝对需要的)。

继承是重用超类功能的一种方法。 但是事情会迅速发展,随之而来的是复杂性的增加。

幸运的是,我们在Swift中有了面向协议的方法。 但是协议必须是通用的,以便将其定义重新用于不同的类型。 类允许我们指定通用参数。 协议为我们提供了“关联类型”

实际示例:用于CoreData处理的CRUD协议

我想为我的CoreData实现创建一个协议。 这将是常见的CRUD (创建,读取,更新,删除)功能。 没有通用协议,我必须在所有地方都使用NSManagedObject,这真的很不舒服,因为我有Xcode生成的子类。 我的实体是: TaskMOItemMO 。 我都需要他们的经理班。 对于TaskMO,协议定义必须匹配一次,对于ItemMO,协议定义必须匹配一次。 可以使用“ associatedtype ”关键字来实现。

  协议持久{ 
  关联类型实体 
  } 

如您所见,实体只是一个占位符,而不是具体类型。 具体类型将仅在实现中指定。

使用约束来缩小类型

Persistable协议使我们能够处理CRUD功能。 但是我要确保将其用于CoreData。 我们可以通过向我们的关联类型添加约束来确保这一点。 就我而言,我只想允许在NSManagedObject子类上使用。 此步骤与通用参数非常相似。

  协议持久{ 
  relatedtype实体:NSManagedObject 
  } 

完整协议如下所示:

编写实现

现在按照您习惯的方式实现类型。 名为“类型推断”的编译器功能会自动检测具体类型的定义。 因此,我们不必明确指定它。

煮咖啡机

凉。 我们创建了一个通用的可重用协议。 但是,让我们深入一点。 我想制造一台咖啡机。 不只是基本的黑咖啡。 我想要一杯很棒的拿铁玛奇朵。

首先,我指定成分:

我的咖啡由主要的咖啡成分和某种牛奶组成。 我将协议命名为“ Drinkable ”。 一切可饮用的东西都有成分。 一个可饮用的饮料可以自我填充(哦,这在现实生活中会不会很生气吗?☕️) ,并且有两种提供方法。 一种用于主要咖啡成分,一种用于类似牛奶的成分。

fill方法每次都会做同样的事情。 它将咖啡和牛奶放入杯中。 无论我们选择哪种咖啡。 因此,让我们通过协议扩展来创建默认实现。

 扩展名可饮用 { 
 更改func fill(){ 

Ingredients.append([provideMilk(),ProvideCoffee()])
}
}

而且我的大部分咖啡都是浓缩咖啡。 因此,为其创建默认实现也很有用。

  func ProvideCoffee()-> 浓咖啡 { 

返回Espresso()
}

喝牛奶有点困难。 也许您有一些素食主义者。 在这里,您必须选择豆浆而不是传统的牛奶。 但是过程每次都是一样的。 将牛奶倒入咖啡中。 因此,我也为这种情况编写了另一种默认实现,但是这次我使用通用关联类型而不是具体类型。

区别在于主要成分(Espresso,混凝土类型)将在实现中自动使用。 因为我们使用类似牛奶的成分的类型,所以我们必须在所有实现中明确指定类型。

现在,我创建了我所服务的不同类型的咖啡。 首先,我最喜欢的是:经典的玛奇朵咖啡。 它将用传统牛奶制成。

  struct LatteMachiato可饮用 { 
      var成分: [任何] = [] 
      typealias SecondaryIngredient =牛奶 
  } 

Typealias定义告诉编译器,我们的通用关联类型“ SecondaryIngredient ”现在是具体的“ Milk”类型。 因此,该机器将为我们的LattéMacchiato使用“牛奶”。

现在是大豆格子呢。 这次我用豆浆。

  struct SoyLatte可饮用 { 
  var成分: [任何] = [] 
  typealias SecondaryIngredient =大豆 
  } 

最后。 让我们煮咖啡。

  拿铁玛奇朵Latte Macchiato): [[__lldb_expr_1.Milk(成分:“🥛”),__ lldb_expr_1.Espresso(成分:“☕️”)]]] 
  大豆格子: [[__ lldb_expr_1.Soy(成分:“🌱”),__ lldb_expr_1.Espresso(成分:“☕️”)]]] 

正如您在输出中看到的那样,大豆拿铁使用的是“大豆”类型,因为我们用类型别名指定了它。 很棒的是,我们在实现中抽象了“ fill”方法并将类型具体化为“ Soy”。

结论

关联类型使我们可以创建更灵活,更灵活的应用程序。 有了它,实现时间将大大缩短。 我从上一个应用程序PursCreate获得了经验,同时将意大利面条重构为模块化体系结构。

今天就这样。 现在该喝一杯好咖啡了☕️

您有任何疑问,改进建议,还是想成为我最好的朋友? 在Twitter上关注我:https://twitter.com/jbrunhuber。