我说这很无聊,因为您一直在编写看起来像是具有相同形状的文本块和块的代码。赋值、条件、循环、函数等等。我可以提取出这个星球上最好、最智能的代码,并将其与另一段愚蠢的代码并排放在一起,这段代码是有人为了好玩而编写的。从远处看,它们看起来是一样的。
一遍又一遍地生产同样形状的东西是多么无聊啊。然而,信不信由你,我已经这样做了将近30年了,…。无论是专业上的还是爱好上的。所以有一些有趣的事情一直在拖着我做这件事。
你可以称它为工程学,或者任何你想要的,但我会考虑编程是一门艺术。编写好代码的实践不仅需要纪律,还需要一种不同的逻辑推理方式,从而导致一个不同的美丽世界。
每个人都知道如何编写看起来像线性、循序渐进的简单程序。这不是我要说的。实际问题需要更复杂的东西。要解决现实世界的问题,软件工程师必须有不同的想法。我再重复一遍--想法不同。
秘诀是抽象,或所谓的泛化。真正优秀的工程师会把自己从实际的术语中抽离出来,用更笼统的术语进行更深层次的思考。
比方说,我想要编写一个程序,该程序向网站发出请求,并在网站关闭时向某人发出警报。该函数可以简单地用Julia语言编写如下:
功能检查(Url)响应=HTTP.get(Url)如果响应.status!=200,则发送电子邮件(";某人@Somewhere.com";,";有问题";)endend。
这个函数没有什么问题。真的。它用最少的代码做它应该做的事情。事实上,这可能也是我想要在生产中部署的。
它是僵化的,因为它只做一件事,而且它只做那一件事,而不做其他事情。换句话说,它太僵化了。你可能会想,你还想要什么,伙计?
只要想一想这一点就行了。不难看出以下潜在问题:
HTTP请求只使用不带任何查询参数的GET方法。如果我想使用POST方法怎么办?如果我需要在URL中传递某种id作为查询参数的一部分,该怎么办呢?
代码对照200检查HTTP状态,看看是否成功。如果我还想处理其他2xx响应代码,该怎么办?
提醒术语的逻辑是硬编码的,可以发送电子邮件。如果我想要将警报发送到Slake或Zulip等聊天平台,该怎么办?
一位熟练的程序员看着这个问题说,嘿,为什么我们不构建一个抽象层来使它更灵活呢?下面让我们来介绍一下重构过程。
我们要做的是维护代码的结构/逻辑,并将代码的其余部分抽象为更一般的功能:
FETCH函数命中URL并返回某种响应对象。IS_SUCCESS函数分析响应对象并决定它是否成功。最后,警报功能将问题通知某人。
这些函数是纯粹的抽象,因为不需要知道它们是如何工作的。这和优秀的员工是一样的。你不需要告诉他们如何完成这项工作。他们只是知道,然后把工作做好。
回到示例中,警报功能可以发送电子邮件、将事件记录到数据库、寻呼支持人员或要求Siri叫醒您!你知道我的意思。关键是,无论这些小函数做什么,检查函数都包含相同的逻辑!
问得好。事实上,这正是它目前正在做的事情。让我们多做一点吧。首先,我们可以为函数提供一些上下文。第一步是认识到我们需要一个抽象的概念,或者知道如何获取数据的东西。从逻辑上讲,这样的概念可以编码为抽象类型:
然后,通过HTTP获取一些东西并检查结果只是众多可能的实现之一:
通知器知道如何通知某人。";";";抽象类型AbstractNotifier End";";";";发送通知警报消息。";";";函数警报结束。
请注意,报警函数没有正文。具有空函数的目的是定义接口的外观,并附加文档字符串以用于文档编制。
struct EmailNotifier<;:AbstractNotifier Recipient::Stringendfunction alert(::EmailNotifier,Subject,Messgae)SMTP.send(Notifier.Recipient,Subject,Messgae)endstruct SlackNotifier<;:AbstractNotifier频道::Stringendfunction alert(Notifier:SlackNotifier,Subject,Message)Slack.send(Notifier.channel,Subject,Message)end。
>;>;>;注意:SMTP和SLACK是虚构的软件包,仅用于说明目的。他们并不存在。
函数检查(url,Fetcher::AbstractFetcher,Notifier::AbstractNotifier)RESPONSE=FETCH(FETCHER,SITE)IF!IS_SUCCESS(FETCHER,RESPONSE)THEN ALERT(NOTIFILER,";$SITE IS DOWN";,";错误:$RESPONSE";)endend。
仅仅拥有这几个抽象就可以打开很多可能性。为什么?假设您有3种不同的取取器和3种通知器。现在,您可以轻松编写3x3=9种不同的健康检查。
过于抽象可能会带来伤害。在上面的示例中,只有一个抽象级别。当人们查看代码时,它仍然很容易理解和理解。但是,如果有多个抽象层,则很难对代码进行推理。只需看看Fizz Buzz企业版就可以看到过度工程的极端例证。
正如您所看到的,将您的代码提升到一个新的级别非常简单。使用抽象类型是构建这些抽象层的一种快捷而简单的方法。请注意,还有其他范例--例如,特征。我会把它留到以后的帖子里。如果你等不及了,可以去BinaryTraits.jl项目回购网站快速了解一下目前正在酝酿什么。
编程可以很有趣。它不一定非要枯燥乏味。在这一点上,我想我会收回我最初的索赔。虽然从远处看我的代码可能看起来很无聊,但当我仔细看时,我看到了一个美丽的东西。