你给代码取了一个不好的名字

你给代码取了一个不好的名字

当他们开始在软件开发领域迈出第一步时,新程序员必须处理的首要任务之一就是命名。 文件夹层次结构,文件,类,对象,函数,变量甚至是较低级的内容(例如自定义协议消息),所有内容都需要一个名称。

就编译器和解释器而言,只要您能够唯一) . Y w,明确地识别它们,您给它起什么名字都没关系,但这与人类无关。 。 与硬盘驱动器或RAM模块不同,在硬盘驱动器 m ? N t s r或RAM模块中,存储的数据的性质基本上无关紧D P s i V w * U W要,而人类内存通过句法和语义关联来更有效地工作。 在编程世界之外,我们已经同意词干,前缀,后缀和其他9 | 9 K基本词汇元素来构成含义和关系,然后我们将其引导到整个语言中。 我们自然而然地将这种行为尽可能地扩展到编程语言,并尽可能地利用语义和助记符关联。

在本文中,我将为您服x }务:我想提高人们对命名的重要性的认识,讨论一些鼓励每个开发人员不要像我们看到的那样经常忽视这一主题的动机。

本文无关的内容

本文将不会讨论任何特定的大小写标准,语言范式首选项,明确的文档指南或参与无休止的文本编辑器冲突。

不仅仅是看起来很酷

当我在2003年首次开始编程时,我记得发现自己可以拥有自己的SomethingBrokers,WhateverAdapters,ThingyLaunchers和诸如此类的东西(是的,我还很年轻)非常整洁。 花哨的名字给他们(和我)带来了责任感,并说服我我的代码做了一些相当严肃的事情,即使那只是) 2 k一个Programming 101项目。 正X H X N d m $ X .如您所期望的,h j 8 ? A W (给事物起名字并不是(看起来只是:B)关[ 1 e Y Q于浮华和l33t。 但是请保持信念:良好的命名习惯通常可以提高可读性,更快地理解每个代码段的目的,可以更轻松地浏览项目的结构,并w G M P且经常提供程序正在执行的隐式文档。

这不仅关乎您,而且主观上您认为最有效。 编写代码实际上是团队合作B a k的经验,即使您暂时独自飞行,也应始终考虑未来的读u q @ . M U w 5者。 读您几个月前编写的一段代码的\"您\"可能会忘记大部分正在发生的事情,并且可能需要重新理解其复杂性,以进行正确,一致,有意义和优雅的更改。

面向对象基础知识-记住没有害处

随着它的发展,\"对象定向\"为我们提供了足够的动力来适当地区分描述结构及其- 5 l N = 2功能(类3 / ?和属性)的内容以及实际更改程序状态(方法)的内I X k K T G : {容。 这导致了一个非常基本的命名指南,通过该指南,我们用名词(描述)和动词(动作)对类和属性/属性进行洗礼。

你给代码取了一个不好的名字

> A basic Java class appearance

自从1990年代初期开始广泛支持OO技术的编程语言以来,这就是规范。 但是,焦点从Web开发转移到了崭新的设计模式,这种设计模式开始违背该标准,我们将在以后看到。

句法7 # 4平行

人脑喜欢某种程度的重复和标准化。 例如,在音乐,诗歌,建R X x ! P筑和……编程中,我们看到了这种繁S 9 1 ! o N荣。 相关实体的编写和命名组应考虑到这一点,因为它在读者的眼中更加明确地加强了这种关系。 实现此目的的一种技术是使它们遵循相同的句法模式。 查看以下两个列表:

你给代码取了一个不好的名字

两者都试图枚举家^ } / ( $ e务并传达相同的含义,但是右侧的人则以一种更整洁,更优雅且可预测的方式做到这一点。 在该1 V G特定示例中,所有条目均由不定式形式的动词和充当该动作的对象的名词或表达组成。 您完* i o : _ %全不需要考虑它,但是在阅读了其中的一些内容之后,您的大脑实际上期望下一个列表项将使用相同的a U W f ~ J n结构来构建。 除了美学,几乎就像是硬盘驱动器一样,它依赖于本地性原理来提前读取数据,了解有关下一步的信息通常可以更快地浏览和更快地理解所读取的消息。 这称为语法并行性,是您应该从本文中摘取的关键概念之一。 用它做一个记忆。

对象和集5 % ] b i / r

当开发人员必须在同一编码上下文中处l Y 3 z t s理单个对象及其集合时,他们将面临一个非常常见的命名权衡:

一方面,您可以使用经典的单/复数形式(例如account和account)来选择更简短,更简洁的名_ , w W P f称。 这将缩短代码行并最大程度地减少换行,从而提高代码块的可读性并降低整体代码的冗8 i $ - Z ] T长性。

另外,您可以选择更长,更明确的区别,例如5 O G ~ - oat R r R c 5 ~ccountObject和accountCollection,这反过来又有助于清[ n y晰度和单个指针的可读性。

最后但并非最不重要的/ : l , )一点是,两全其美,为单个对象编写account和为列表编写accountCollk Z r } $ 5ection可能很诱人s A F a

我通常会从最后一本书中脱颖而出g 0 E 7 :,因为它打破了语法上的并行性,x ^ | P Q b并可能给读者留下印象,即它们在语义上的行为比单数/复数的事物更多。 经过一段时间的思考之后,我会说这是表格1和表格2之间的一场激烈的战斗。一方面,多年来我经历了几次小的生产力挫折,而忽略了尾随的s,因此,我倾向于 后者。 但另一方面,我当然可以看到,如果一个团队建立一种强大而横向的文化,那么单数和复数是必经之路,那么仔细检查这种东西可能会成为每个人的第二天性,并将规模推向第一 选项。

迭代器

遍历一系列项目并与它们一起做事是编程生活中的头等大z p +事之一。 无论您/ _ v ! / ( f =编写什么内容,代码中都会有迭代器。 即使更高级的编程语言可能对您来说是抽象的,但仍有一些不是,而对1 X z他们来说,有一个非常简单的命名技巧:与其为迭代器变量编写i,j,k等, 尝试使z B , i a %用ii,jj,kk等。 这一轻松的更改将可搜索性**提高了十倍,同时保持了对这些变量名称的标准期望。

**我知道这是有争议的,并且有很多具有高级) Q z .搜索功能的代码编辑器都可以在不使用技巧的情况下帮助解决此问题,但是它如此轻松,可移植且与编辑器无关,我仍然认` : [ p y L r为它很不错 理念。

服务对f % ? U

服务对U a (象(SOD W P :)是一个很棒的概念,在当今的网络应用程序中具有很大的空间,但通常会引起一些命名错误。 查看此服务对象文件夹,例如:

你给代码取了一个不好的名字

这是围绕服务对象命名的有争议的很好(真实的)例证。 首先,请注意,即使有些人倾向于} 7 $ 3 ! F e 1将这些类称为SO,但SO本身实际上是描述SO的类的实例。 然后记住,对于类,自然的方法是用名词来命名它们,这就是问题所在。 我们看到分页器Deal_notifier和一些以公制结尾的很酷的代码。 但是,我们也看到了clE v Mone_investment,upsert_investment和cancel_ (上帝保佑这混乱)。

等等,动词???

嗯是的。 发生的事情是,SO作为一种设计模式,其目的是实施对您的业务逻辑,为他们提供的实际服务以及存在的理由有意义的单个操作。 它们的公共接口通常由唯一的方法(可能是静态的)组成,通常称为call。 而且由于他们无能为力,开发人员开始认为在调用它们后将要执行的动作后给它们命名是合理的,从而使他们摆脱了学术OO最具标志性的命名标准~ S j之一的束缚。

我抵制了一段时间,以为可能是不良药。 \"为什么我们不能只用名词来命名它们并以_ ( h f k $完全相同的方式使用它们?InvestmentCloner.call()看起来并不那么糟糕\"。 事情是它们的实例与大多数实例有所不同,因为它们具: x Q g l有非常明确的生命周期:将创建它们,并要求它们去做它们的事情,并(希望)优雅地死掉*。 因此,开发人员(5 U F `尤其是以5 _ ] 2 y ^ F可读性闻名的ruby社区)( x Y [开始认为,在他们执行诸如NotifyCustomer或AbortMission之类的动作后命名它们,可以更快地理解代码而不会造成任何语义损失。

*使用单例实现服务对象是一种反模式。 单例的全局可见性,长寿命和可能的状态性很难测试且容易产生副作用。 可以在此处和, N p ! { ) q 9 }链接的问题中找到有关e - u B ] d a ? A此主题的更深入的讨论。

最小化抽象泄漏

2002年Joel SpL | !olsky出色而永恒的文章\"泄漏抽象法则\"揭示了编程中的一个基本缺陷,这被理解为实现增量,分层抽象的X S % $ X技巧。 简而言之,他认为\"所有非平凡的抽象在某种程度上都是泄漏的\",在这种情况下,它们在完全保护用户免受其内部工作影响的同时,没有按照预期执行操作。 这对我们而言尤其重要,因为命名错误是抽象泄漏的巨大来源,这反过来意味着我们可以通过适当注意我们所说的那样来最小化* @ B甚至完全避免它们。

因此,我们正在寻找一方面赋予实体权力,暗示其总体\"专业领域\"的名称,另一方面又要谨慎行事以限制这些期望,以避免暗示过多的权力。 不足为, L 0 N奇的是,总体架构通常会从中获利,因为开发人员有义务更加关注实体之间的关系及其角色界限。

好的,很酷,但是让我们看一7 / q t ) 0 o 4 I些代码

因此,让我们远离项目符号要点,逐步建立一些东西,同时避免其他不良命名。

假设我们要编写一项服务以连接到Instagram,并做instagrammy的工作,例如获取某人的帖子,将评论爬到某个帖子,喜欢说评论和排序。 我们可以开始考虑它的名称:

class Instagram # ... 
end% Z b ~

关于这堂课,这没什么多说的,除了它与Instagram有关系,h。 据我们所知,它还可以有方法远程爆破Instaj h ~ Igram总部。 让我们尝= ? g l s { d l试一些更清晰的方法:

class InstagramCrawler # ... 
end

好的,这不是完美的方法,但是关于我想代表的内容有很多说明。 我已经期望有一个班级,其对象将在Instagram上搜寻内容。 尽管如此,它仍然使我感到过于强大和单一,它可能包含太多的代码行,比需要的混乱得多,需要进行过多的研究才能弄清楚,容易造成抽象泄漏或以上所有问题。 也许我们可以分解一下:

class InstagrV e k _ / amCrawler 
def initialize
@client = Insq b A W ^ ] 3tagramClient.new
eF 1 . snd # ... methods that possibly call @c7 J ) v T $lient.somet5 R J ~ e I v hing()
end
class InstagramClient # ...
end

好的! 现在,我们可以进一步限制对搜寻器e K 3 g b抽象的期望,并相信它7 k J 7 # j & }不会在乎与instagram API本身进行对话,而是将这些内容转发给InstagramClient *。 同时,我们创建了一个完美合理的角色,用于抽象担心与Instagram API接口的服务,并具有InstagramC) ! f A R 4 Drawler甚至不知道或不在乎Q z { W = W ) J这种通信是否通过REST,GraphQL或其他某种通信的额外好处。 奇怪( 2 q的协议。

为了这个示例,我们将在2 } [ V M ~ L这里停止,但是如果您觉得\"抓取工具\"做了太多的工作以至于不能放在一个单独的类中,或者您的\"客户\"也可以进一步细分,则不一定 停止完善抽象的正确点。

*实际应用程序可能会允许注入客户端以进行测试和解耦。

现在,让我们一步一步来,仔细考虑一下错误处理,然后从创建一个自定义的InstagramErG D V u & ^ror扩展Ruby的StandardError开始:

class InstagramCrawv 5 4 u P L & ? ~ler 
def initialize(client: client)
@client = client
end # ... methods that possibly call @client.something()

class InstagramCrawlerError
< StandardError;
end
end

class IS 3 { G a ? Y R znstag@ = QramClient #~ * # ...
end

因此,尽管这可能表示我们的搜寻器可能吐出的每个错误并迅速解决了我们的问题,但它却错失了一个绝佳的机会,使开发人员的工作变得更4 R P d h } % .加轻松。 通过在这一点上付出更多的努力并更多地限制异常抽象,我们可以通过更好地描述发生了什么类型的错误来节省调试时间的时间:

class InstagramCrawle}  ~ S 4 S wr
def initialize(% ) 4 Gclient: client)
@client = cliena t - *t
end

# ... methI P g 8 G ods that possibly callu [ ~ Z 9 K L @client.something()

class Insta| Y t m + O T } BgramCrawlerError < StandardError; end
class UserUnavailableError < InstagramCrawlerError; end
class InexistentLocationError < InstagramCrawlerError; end
class ExpiredMediumError < InstagramCrawlerError; end
class InvalidAccessTokenError < InstagramCrawlerError; e] _ A }nd
class MalformedRequestError < InstagramCrawlerError; end
class ServiceUnavailableError < InstagrT L F k P m eamCrawlerError; end
cp ) G 2 W V nlass TimeoutError <6 _ H; InstagramCrawlerError; end
end

class InstagramClient

# ...

end

(当然,为了正确引发这些错误,出于分析目的,我们需要故意跳过一些其他f H H v错误处理代码。)

现在呢? 在调试期间,这已经看起来像是一个巨大的节省时间,在此期1 N c Z N间,开发人员将能够更快地识别出发生了什么问题,而无需查看日志或巨大的响应消息输出。

但是,它使Crawler知道并必须处理诸如MalformedRequestError和C @ HInvalidAccessTokenError之类的通信和协议问题,从而暴露了巨大的抽象漏洞。 但是我们只是一个适合这些的地方,不是吗?

cl= u & k O Iass InstagramCrawler
def initialize(client: client)
@client = clienz { % @ _ ; c E $t
end

# ... methods that p. Z Hossibly call @client.something()

class InstagramCrawlerError < StandardError; end
class UserUnavailabl5 m u ` 8 I eError < InstagramCrawlerE} n u ? ,rror; end
class InexistentLocationError < InstagramCrawlerError; end
class ExpiredMediumError < InstagramCrawlerError; end
end

class InstagramClient
# ...

class InstagramClientErd b D D K x k C ror < StandardError; end
class InvalidAccessTokenError < InstagramCli; l & r a $entError; end
class MalformedRequestError < InstagramClientError; end
class ServiceUnavailab_ F 5 P gleError < InstagramClientError; end
class TimeoutErroJ n E a mr <G c j $ InstagramClientError; end
end

这是我们的小示例案例研究的最终形式,该案例研究是已投入生产多年的真实代码的一部分。 正确的命名方式有助于我们的团队避免抽象泄漏,在实体之间按语义划分职责,简c & U化调试过程,并使我们编写更清晰,更易解释且易于理解的代码。

无意义的抽象

在总结之前,我想展示一个非结构化的示例,该c $ t N w , t V示例在代码审查中很少出现,使我感到\"击穿心脏\":

import React from \"rg X w i ^ f 6 }eact\"

class Auth exten` D 2 Sds React.Component {
render() {
return <div>{this.props.children}</div>
}
}

export default Auth

这是一个非常简单的React组件,除了将其子元素包装在div标签中之! { 9 S B K a L #外,它并没有做任何其他事情。 尽管简单,但起初我不知道为什么有人认为将其命名为Auth是个好主意。 \H a X 1 ="d ] ^ % * J p f也许对于身份验证组件was是一个坏名字\"。 事实并非如此,因为它没有实现任何身份验证行为或外观。 它也不是一些怪异的标准React样板,因此我们得到了& m %D Q 1 p ,无意义/具有误导性的抽象,一对名称和实体根本无G A T 9 2 : B n法帮助我们辨别或为后者划界。 如果我们深入到兔子洞的深处,我们甚至可能发现它可以用于某种扭曲的目的(将身份验证子组件分组的主组件或测试选择器?),但是这个名称并没有将我们指向其架构相关性 该实现会立即引发黄旗。

结束语

有两点促使我写这篇博客。 首先,这是我的生活:每当我分配到一个已经在进行中的项目时,我就会[ ~ VG w / M t现我最大的入t S a职障碍取决于不清楚的项目架构/文件夹结构以及不良的命名。 通常感觉就像是Q p # 0 , W ~ z试图将Escher图纸的合理的工作模型内部化:即使它能以某种方式起作用t O ) 1,也没有任何意义, & J k b N并且似乎不可能在不创建新的松T ; v , l 2 3 &散末端的情况下捆绑所有末端。 其次,在特定语言或特定D # B I t 5 7 B l/反复出现^ f / o 0 P } S 2的命名难题的背景下,似乎似乎缺乏阅读材料来讨论该主题。

因此,6 L R % ( S }总而言之,这些天似乎是一个向他人提供痛苦的好时机,并写下一些想法以及既定做法的好时机。 希望一些新的开发人员会出现在这里并出来,因为他们知道,好的,正确的命名不是您要学习的东西之一,而一旦您完成学业便会忘记它x ! K 9 9 0们。 另外,我完全希望这些想法能促进讨论,并在经验丰富的人士中至少引起一些争议,因此,非常欢迎读者提出新的想法。

现在,j R % w让我们穿上大声的Bon Jovi,然后回到编码。 祝你有美好的一天!

附言 只是为了让我不会^ X @ d被误解,让我给大C 0 1 0 M P家一个值得相信的东西:整个文本中的随机大写单词根本不是随机的。 在评论中举手或鼓掌,如果全部找到! ;)

(本文翻译自Chico Carvalho的文章《You Give Code a Bad Name》,参考:https://goiabada.blog/you-give-c: 6 |ode-a-bad-name-f5e96f9x # , / c793c8)

上一篇

大屏上的短视频机会点:智能电视短视频用户洞察报告

你也可能喜欢

  • 暂无相关文章!

发表评论

您的电子邮件地址不会被公开。 必填项已用 * 标注

提示:点击验证后方可评论!

插入图片
返回顶部