如何避免协议导向沉迷于编程

大约一年前,我正在观看2015年的WWDC视频,当时一次特别的会议演讲引起了我的注意。 似乎引起了所有人的注意。 鉴于在过去一年中创建了与Swift相关的内容,许多人已跃入全新的编程范式。

我当然是指大卫·亚伯拉罕斯(David Abrahams)提出的有关面向协议的编程的精彩演讲。 如果您还没有看到它,并且使用Swift编写应用程序,那么绝对值得一试! 认真地做吧。

接下来的事情并不是试图减轻演讲中所呈现的内容的酷感。 如果正确应用协议扩展,它将是一个很棒的工具,它具有许多实际用途,我们将在本文的最后进行介绍。

问题是我们所有人都感到内gui(绝对包括我自己)。 当您拥有闪亮的新锤子时,一切都将变成钉子。

因此,在过去的一年半中,协议一直在敲定,我们的项目是钉子。 我们学到了什么?

POOP的起源

它总是以一种天真的渴望学习新事物而开始。 与在圣诞节打开Nintendo 64的包装并整夜陪同兄弟姐妹玩Goldeneye并没有什么不同。

是的,我知道那与我约会的时间。

协议定向痴迷编程是过度使用协议扩展需求的产物,尤其是在不需要它们的情况下。

POOP是对令人敬畏的新工具的一种完全可以理解的反应,但是正如我之前所说的,在编程中所做的任何事情总会有取舍。 仅查看收益是远远不够的。 我们必须始终考虑成本。

那么面向协议编程的权衡是什么? 为什么我们要避免将所有内容重构为协议和协议扩展的冲动? 是什么使POOP便便?

一切都取决于 代码的 可读性

痴迷于协议定向的编程如何危害代码的可读性。

与开发人员团队合作时,有效的沟通是成功的关键。 可读的代码可导致更好的沟通。 不可读的代码使团队工作变慢,因为每个人都需要更多时间来弄清楚它的作用。

作为团队中的开发人员,您应该始终努力减少代码对同事的认知负担。

拥有可读的代码不仅仅意味着选择正确的类和函数名称。 与在单个位置中显示适当数量的信息有很多关系。

另一个开发人员应该只查看一个文件就可以弄清楚代码的作用。

这个有一个名词。 我们称其为本地推理。

这意味着人们应该能够推理您的代码,而不必到处移动太多。 它是一种支持预先的显式代码的设计哲学。

当您出于保留局部推理的目的而编写代码时,尝试避免使用复杂的类层次结构和设计,这些层次结构和设计迫使其他开发人员从一个文件跳到另一个文件,只是为了清楚地了解代码的作用。

协议定向痴迷编程是有问题的,因为它使本地推理几乎不可能。

为了更好地理解为什么会出现这种情况,我请您取悦

惊人的爆炸结构

假设您有一个基本的Milkshake结构。 它具有您可能熟悉的一些变量和功能。

现在我们可能不一定同意授予Milkshake结构的功能,但是我们可以同意这里有可读的代码块。

就是说,所有这些都为我们阐明了。 我们不需要去其他地方去了解这个结构的作用。 很容易推理。

如果我们将奶昔的一部分重构为一些单独的协议,将会发生什么?

好吧,这并不可怕。 从技术上讲,它仍然像我们的其他结构定义一样工作。

不幸的是,它的可读性差得多。

我了解,经验丰富的程序员应该可以毫无疑问地推断出我的奶昔结构通过协议一致性获得了将男孩运送到院子的能力。

但是,这个结论尚不明显。 阅读代码的人必须花费一些精力才能达成共识。

在像这样的小例子这样的受控环境中,这很好。 但是,当您与多个团队成员一起从事一个大型项目时,您就要将该成本强加给必须与您的代码进行交互的每个人。 这会使项目变慢。

强迫人们思考,这样做没有明显好处时,只会浪费大家的时间。 像这样的代码会将大量的现金烧掉。

当您开始将协议和扩展交付单独的文件时,情况将变得更加糟糕。 除非您命令单击这些协议中的每一个,否则您将不知道该结构的作用。 它扼杀了当地的推理。

想到它,考虑到协议趋向于减少代码的直接性,术语面向协议的混淆可能更合适。

POP和POOP之间的区别。

希望我不会反过来改变你的意见。 我无意说我们应该举起手来,永远不要再使用协议扩展。 我喜欢我的新玩具! 它做得很棒。

我只是在提出一些适度的理由。 协议扩展的应用不要太草率。 因为存在可读性的折衷,所以我们应该谨慎选择何时何地使用它们。

当我们不再思考协议可以帮助我们的根本原因时,POP就变成了POOP。 POOP是在协议总是优于无聊的旧结构和类的错误观念下对所有情况盲目使用协议的方法。

希望我已经说服了你。 现在,让我们尝试了解协议扩展的一些好处。

通过协议扩展我们可以获得什么?

那么为什么我们要把Swift变成一种面向协议的语言呢? 从实际意义上讲,我们要通过这样做来实现什么?

总之, 可组合性

协议扩展为我们提供了一种在类和结构之间共享函数和变量的方法,而无需使用继承。 只要两个单独的实体共享相同的必需属性,它们都会获得新的行为。 这很酷!

在拥有协议扩展之前,我们必须创建一个单独的对象以保留我们希望在它们之间共享的功能。 我们称其为对象组合。

直到今天,许多开发人员仍在使用对象组合。 您几乎可以肯定在工作中会使用它。

还记得那段时间您创建了可重用的视图,并将其放置在应用程序的多个屏幕中吗? 那是对象组成。

您没有强迫视图控制器从包含该行为的某些超类继承。 您是由可重用部分组成的视图控制器,其中之一是自定义视图。

通过协议扩展,我们获得了一种进行合成的额外方法。 现在,除了协议组成之外,还可以通过协议一致性添加新行为。

这是皮带中的附加工具,如果使用正确,它可以使我们生产率更高。

协议扩展的一些实际用途

鉴于使用协议扩展的主要好处是可组合性,大多数实际用途都等于在相似实体中重用行为。 如果您发现自己在多个地方都需要相同的行为,则可能需要考虑使用协议扩展以避免重复。

原因#1。 多个实体使用相同的行为

让我们回到我们的例子。 我们展示了如何通过将其移至协议扩展来排除“ SipBySipConsumable”行为。 当然,这样做似乎很愚蠢,因为没有其他实体使用该行为。

但是,如果我们正在编写某种旨在喝各种不同饮料的游戏,该怎么办? 那会这么傻吗?

不。 一点也不。

通过将代码移至协议扩展,我们避免了在所有其他类似饮料的实体中一遍又一遍地重复相同行为的需求。

请记住,我们必须始终考虑成本效益比。 与单个实现相比,维护相同功能的15个重复项要困难得多。

因此,当许多事物需要相同的行为时,我们可以接受对可读性的轻微要求。

POOP是由于没有充分的理由将代码移至协议扩展而产生的。 POP是协议扩展的明智应用,它可以使代码库更易于维护。

原因#2。 我们真的真的需要对这个东西进行单元测试

通过协议扩展,我们可以获得从实现那些行为的事物中提取行为的能力。 这意味着我们可以在孤立的环境中测试行为,在该环境中,没有任何类或结构的依赖项一定要应用。

这对单元测试是一个巨大的福音。 它不能被夸大。

这意味着我们的单元测试套件可以使用比生产代码中使用的实体更简单的实体。 它有效地使单元测试更加容易。 如果您对它的工作方式感到好奇,请参阅我的《使用Swift协议轻松模拟》一文。

在相关说明中,协议扩展不应成为将您的代码库完全毁灭的借口。 单元测试有其自身的权衡,这是我在另一篇文章中讨论的另一个主题,我们应该在我们的iOS应用程序中进行什么单元测试?

无论如何,当很明显您需要对某项进行单元测试时,协议扩展是执行此操作的强大工具。

原因#3。 在iOS和MacOS之间重用相同的代码

当协议扩展与关联类型结合使用时,在不同操作系统之间共享代码时,可能会打破一些障碍。

假设您有一个UIView子类,该子类具有某些要移植到MacOS版本的应用程序的行为。 不幸的是,可可与UIKit不同。 它使用NSViews,而不是UIViews。 子类化是不可能的。

但是,通过协议和关联的类型,您可以定义NSView和UIView子类都可以共享的行为。 它使跨平台的代码库更易于维护!

抱歉,我还没有写任何文章。 我继续。 当我这样做时,我将在这里引用。

让您的想法震惊,但保持您的:完好无损。

我们度过了一段美好的时光,每年都会推出各种出色的新工具。 我们有充分的理由感到兴奋,但我们也有很多理由来减轻这种兴奋。

面向协议的编程和Swift正在为迈向新的编程时代铺平道路。 它可以提高我们的生产力,也可以使我们绞尽脑汁。

只要我们将代码的可读性放在首位,我们将使某些应用程序具有POP功能,并避免加入大量的POOP。