为什么“显性”被视为好事?

时间:2009-12-17 21:08:03

标签: theory explicit

我经常听到人们称赞语言,框架,结构等是“明确的”。我试图理解这个逻辑。语言,框架等的目的是隐藏复杂性。如果它让你明确指定所有类型的细节,那么它不会隐藏太多的复杂性,只会移动它。显性有什么好处,你如何使语言/框架/ API“明确”,同时还能使其隐藏复杂性?

13 个答案:

答案 0 :(得分:30)

是否应该明确或隐含取决于具体情况。你是正确的,因为你经常试图隐藏复杂性,并且在幕后为你做的某些事情自动是好的。封装等。

有时虽然框架或构造隐藏了我们不应该的东西,但这使得更少清楚。有时某些信息或设置对我们隐藏,因此我们不知道发生了什么。假设我们不理解也无法确定。行为发生是我们无法预测的。

封装:好。隐藏:糟糕。做出正确的电话需要经验。逻辑属于哪里,它应该是明确的。

示例: 我曾经从页面背后的一系列代码中删除了大约90行代码;数据访问代码,业务逻辑等,不属于那里。我将它们移动到基页和关键业务对象。这是良好(封装,关注点分离,代码组织,脱钩等)。

然后我兴奋地意识到我可以从许多页面中删除最后一行代码,将其移动到基页。这是一行从url获取参数并将其传递给业务对象。好,对吗?好吧,不,这是糟糕(我隐藏)。这个逻辑属于这里,即使它在每一页上几乎都是同一条线。它将UI意图与业务对象相关联。它需要显式。否则我隐藏了,而不是封装。有了那条线,看着那个页面的人会知道那个页面做了什么以及为什么;没有它,确定发生了什么将是一件痛苦的事。

答案 1 :(得分:15)

我认为明确指的是在使用它时确切知道它在做什么。这与确切知道它是如何完成的不同,这是复杂的部分。

答案 2 :(得分:8)

显式并不是那么好(当然,密切相关的详细是坏的),因为当隐式出错时,它是很难说WTF正在进行中。

Hack C ++十年或两年,你就会明白我的意思。

答案 3 :(得分:7)

这是关于表达意图。读者无法判断默认是由于错误还是设计造成的。明确地消除了这种怀疑。

答案 4 :(得分:6)

代码比阅读更难阅读。在非常重要的应用程序中,给定的代码片段也会比写入代码更频繁地读取。因此,我们应该编写代码,以使读者尽可能轻松。做很多不明显的事情的代码不容易阅读(或者更确切地说,当你阅读它时很难理解)。因此,显性被视为一件好事。

答案 5 :(得分:4)

依赖默认行为会隐藏那些不熟悉语言/框架/其他人的重要细节。

考虑如何对不熟悉Perl的人很难理解那些广泛依赖短号的Perl代码。

答案 6 :(得分:4)

明确与隐含是关于你隐藏什么,以及你展示什么。

理想情况下,您会暴露用户关心或必须关心的概念(无论他们是否愿意)。

明确的优点是更容易追踪并找出正在发生的事情,特别是在失败的情况下。例如,如果我想进行日志记录,我可以使用一个API,该API需要使用日志目录进行显式初始化。或者,我可以使用默认值。

如果我给出一个明确的目录,但它失败了,我会知道原因。如果我使用隐式路径,并且它失败了,我将不知道出了什么问题,为什么或在哪里修复它。

隐式行为几乎总是隐藏消费者信息的结果。有时这是正确的事情,例如当你在你的环境中知道只有一个“答案”时。但是,最好知道何时隐藏信息及其原因,并确保让您的消费者更接近他们的意图,而不是试图隐藏重要的复杂项目。

经常隐式行为是“自我配置”对象的结果,这些对象查看其环境并尝试猜测正确的行为。我一般都会避免这种模式。

我可能总体上遵循的一条规则是,对于给定的API,任何操作都应该是显式的或隐式的,但绝不应该是组合。要么让操作成为用户必须做的事情,要么让它成为他们不必考虑的事情。当你将这两者混合在一起时,你会遇到最大的问题。

答案 7 :(得分:3)

框架等既可以是明确的,也可以通过为要完成的工作提供正确的抽象来隐藏复杂性。

明确允许其他人检查和理解原始开发人员的意思。

隐藏复杂性并不等同于隐含。隐含性会导致代码只能由编写代码的人理解,因为在这种情况下,试图理解引擎盖下的内容类似于逆向工程。

显式代码有理论证明是正确的。隐式代码在这方面永远不会有机会。

显式代码是可维护的,隐式代码不是 - 这链接到提供正确的注释并谨慎选择您的标识符。

答案 8 :(得分:2)

“显式”语言允许计算机查找软件中的错误,而不那么明确的语言不会。

例如,C ++对于其值永远不会改变的变量具有const关键字。如果程序试图更改这些变量,编译器可以声明代码可能是错误的。

答案 9 :(得分:2)

在向您的代码的读者清楚说明您打算做什么的背景下,明确性是可取的。

有很多例子,但这完全是为了让你的意图毫无疑问。

e.g。这些不是很明确:

while (condition);

int MyFunction()

bool isActive;         // In C# we know this is initialised to 0 (false)

a = b??c;

double a = 5;

double angle = 1.57;

但这些是:

while (condition)
    /* this loop does nothing but wait */ ;

private int MyFunction()

int isActive = false;  // Now you know I really meant this to default to false

if (b != null) a = b; else a = c;

double a = 5.0;

double angleDegrees = 1.57;

后一种情况不容错误解释。前者可能会导致错误,当有人未能仔细阅读它们,或者没有清楚地理解用于执行某些操作的可读性较低的语法,或者混合整数和浮点类型时。

答案 10 :(得分:2)

良好的抽象并不能掩盖复杂性,它需要最好留给编译器的决策。

考虑垃圾收集:释放资源的复杂性被委托给垃圾收集器,它可能(可能)比你的程序员更有资格做出决定。它不仅取决于你的决定,而且比你自己做出更好的决定。

显式性(有时)是好的,因为它使得在某些情况下最好留给程序员的某些决定不会由不太合格的代理自动完成。一个很好的例子是当你用c语言声明一个浮点数据类型并将它初始化为一个整数时:

double i = 5.0;

如果您要将其声明为

var i = 5;

编译器会理所当然地假设你想要一个int,稍后的操作会被截断。

答案 11 :(得分:1)

在某些情况下,相反的是“魔术” - 如"then a miracle occurs"

当开发人员的阅读代码试图理解或调试正在发生的事情时,显性可能是一种美德。

答案 12 :(得分:0)

框架移动的目的是删除代码中的重复,并允许更容易地编辑块而不会破坏整个事物。 当你只有一种做某事的方法时,比如说SUM(x,y); 我们确切地知道这将做什么,没有理由需要重写它,如果你必须你可以,但它极不可能。 与此相反的是像.NET这样的编程语言,它提供了非常复杂的功能,除了明显的简单示例之外,如果你做了什么,你经常需要重写它。