设计二元性和表达问题(2018)

2021-04-16 02:47:42

我希望用这个系列完成的一部分是让更多的具体概念造成的“权衡”,这些概念造成了任何时间的节目设计。我仍然提出了我认为最重要的两个想法:

抽象设计中最基本的权衡是Power VS属性。当我们的人类似乎有一个普遍的偏见,因为获得正确的财产几乎总是更重要。但是,添加属性限制功率。对这个想法感到满意! :)

最基本的上下文是我们是否在系统边界上设计。大多数“规则”在系统边界上应用,其中代码无法修复,因为它会破坏外部用户。在系统边界之外,你可以始终犯错误,所以“小巧,简单,清晰,直接”可以超越遵守规则。

我还提到了我的一部分论文的是,我们的语言不会让我们有任何兴趣,因为它们通常不会完全完全接受两个对象和数据。(如果您想提出某些语言,您可能会发现他们在那篇文章中,并留下了未来的帖子!)我会随着时间的推移,这是一个以上的几个理由,但今天将是第一个最重要的理由。

我要挑选一些故意简单的东西,以确保我们都明白了发生了什么。在哪里代表坐标,我们想要支持两个表示:Polar和Cartesian.we可以对如何表示的两个不同的选择此类型:作为对象或数据。

界面Coord {浮动距离();课堂笛卡尔实现Coord {私有浮点X,Y;公共浮点距离(){返回Sqrt(x * x + y * y); }} Polar实现Coord {私人浮动角度,DIST;公共浮动距离(){return dist; }}

数据Coord =笛卡尔{x :: float,Y :: float} |极性{角度:: float,dist :: float}距离:: coord - > Floatdistance(笛卡尔{x,y})= sqrt(x * x + y * y)距离({{discle,dist})= dist

希望这些都简单地理解,没有进一步解释,如果你只懂一个,你可以转移那个知识来理解另一个。现在有两个重要的事情:

当其他代码导入我们的代码并尝试使用此类型时,它们可以做什么?

当我们想在将来修改我们的代码时,在外面的用户面前,我们可以在不打破它们的情况下改变什么?

使用对象,我们有一个固定的interface.no可以添加新方法,而不会破坏实现该接口的第三方类,因为它们缺少实现。(可以添加默认实现的新方法,因为有点罕见案例存在有明智的默认实现。)这意味着我们以后决定我们想要添加一个角度方法,这将是一些用户的破坏性变化。

Upside是我们可以自由地添加更多的表示。(也许是同质坐标,也许?)再次,这就是我们图书馆的用户和我们自己在图书馆的未来迭代中.Further,我们可以改变我们当前的陈述的内核,只要他们以同样的方式工作。(很难看到这个例子的好处,但它可能会发生更多的“真实世界”的例子。)

使用数据,我们有一个固定的架构。只是我们支持coordinates的两个固定表示。用户无法实现更多自己,我们无法在未来添加更多而不会妨碍用户,因为他们无法处理新的情况。

但我们可以自由地做的是添加更多“方法”。那就是这个类型的更多功能。用户可以自己写入角度,我们能够将其添加到我们的图书馆未来,而不会出现任何问题。

我们可以制作另一个选择,这是为了实际关闭,不允许用户添加新的变体也不是新的操作。这是一个实际上是一个好名称的“抽象数据类型”:我们基本上是一个好的名称:我们基本上是以一种好的名称:我们基本上取得了数据实现通过隐藏类型的实际构造函数来制作它“摘要”.presimually,您可以添加一些可以构建该类型的值的功能。(我们也可以通过微不足道地与物体进行附图。通过我们的运行Coord示例,某些语言允许“密封接口”,其中唯一的实现可以是同一包装中的那些。但是,否则就说“你不要自己实施这个,你已经警告过。”足够好。)

我们选择有几个潜在原因关闭类型的可扩展性:

我们希望限制类型的建设。考虑日期和validdate。一个是数据,另一个行为像数据一样,而且我们也知道价值不是“2月89日,而是为唯一的才能通过一个verfies首先的函数。

我们希望限制它可以检查其包装的数据的程度。考虑从数据库中占据哈希,并立即将其包裹在一个绝对的抽象数据类型中,从不让任何东西检查哈希;它只公开了一个IScorRectPassword方法。 (当然,这并没有帮助安全,这就是散列的东西。这有助于确保如果您的哈希方法发生变化,则需要更改的代码中的一个位置。没有其他代码可以依赖于哈希的样子喜欢因为他们没有看到它。)

可扩展性,我们作为作者从Adts获得最大的作者。用户无法添加新的表示或操作,但我们可以在未来的我们图书馆的迭代中进行,而不会破坏用户。所以我们已经有限,但授权了。体面的选择,很多时间。

值得注意的Quirk:作为一个领域,编程在具有良好的单词定义时发臭。(另见:强/弱打字。)啊,我们是一个年轻的领域。我认为在我的情况下显然有很大的价值'm致电“抽象数据类型”和“对象”。但我的使用这些单词不是Universal.So让我们注意一秒钟:

我叫密封(即“没有子类”)类“抽象数据类型”。

(对于这事实而言,我正在打电话自由检查类型的“数据”。)

一些来源(如wikipedia)使用“抽象数据类型”来指代我所说的ADTS和对象。其他来源也将使用“物体”谈论兼顾兼顾美学审美更接近后者:我认为ADTS最好代表作为不适用于子类的类。但我们应该以不同的方式考虑它们,所以我试图将它们命名为不同的帮助。

所以我得到一个选择:争论单词的定义,或发明新词。 (“封闭的物体”?)但我认为维基百科的使用“ADT”是off.few人们会看一个界面,并认为“抽象数据类型”是一个很好的名字,对吧?和利斯科夫和Zilles的纸质编程用摘要数据类型似乎使用与我的术语一致。

但后来我不得不写这个整个盒子关于我的符号,所以也许我只是让事情变得更糟。

这对如何扩展的选择通常被称为表达问题。它是一个稍微不幸的名字,要说是诚实的。在上图中,我们可以看到我们的两个主要设计选择:对象和数据。我们也看到了另外两件事注意。

为了让第一个左上角,在左上角,我们看到了一些奇怪的东西。这不是一个自己的盒子。

数据泄漏到第一个框中,因为即使我们“关闭世界”到新的操作,对于许多类型而言,它仍然可以编写任意新函数.Consider一个配对类型使用FST和SND。您可以对其函数无效。 t写下这些功能,因此即使“关闭”它仍然是open.ttepes,这种排序只是数据。您遇到了“折叠”或“通用函数”或“递归原理”,因此可以使用它,就像数据一样。(确实,如果您考虑了访问者模式如何使用对象模拟数据,这正是发生的事情。)

同样在第一个框中,对象泄漏。inespite名称是“抽象数据类型”(并且该名称实际上是一个很好的描述),我建议的方法是使用对象(如果您拥有密封类/接口,如果您拥有它们)来表示ADTS。(或者至少,这就是我认为我们应该在我们的语言未能实现的理想世界中做到这一点。使用它今天适合的东西。)抽象数据类型几乎只是你应该的对象't subclass.objects已经有适当的约定:表示封装,构造函数是我们预期的函数,我们可以抛出,所以我们想要在我们的设计中结合使用:日期是数据,validdate是一个对象这只是包裹着日期,但是验证它首先有效。我们可以轻松地在两者之间来回。但这可以通过我们所知道的日期有效,而无需诉诸定理证明或依赖类型或其他东西想要。

第二件事要注意,这个图的一部分给出了表达问题它的名字,是右下角。我们很少遇到我们无法做的东西,而不是将它标记为一个问题。为什么可以我们在两种方式都有可扩展性?我们想要更多的力量!

这不是一个设计问题,程序员发明了。它真的是一个基本的东西。先进的外星文明可能具有相同的概念。

我们看到相同的二元性在数学中显示出来。有时你有数据:自然数是这些东西。您拥有对象的模数:组是表现这样的东西。

但是这里有一件事是,数学是一个领域的邪教不是统治,这些邪教将自己限制在一个或另一个代表中。这部分是程序员所独有的。(嘿,看某人证明我错了。这不是说他们不要在扭曲他们的思想中以某种方式思考的价值。通用代数将是一个更多的对象 - y,数字理论是更多的数据 - 而不是我可以告诉我,这主要是更改他们试图解决的问题是什么样的问题,而不是他们如何发展解决方案。)

表达问题已以各种方式“解决”。当然存在于两个轴上为您提供可扩展的工具。即使在名为“对象代数”的Java中也有效的技术。在java中有几篇论文title.or“标记最终样式。”或者,您知道,只需用动态语言进行任何操作。

无论其他“精心设计的代码”是,它绝对是易于理解的代码。这个设计空间的右下象限的麻烦,具有可扩展性的变体和操作,是其中一些必须是真的:

有些域名特定的东西你能杠杆杠杆才能保持乐趣。

推理部分并不是太难了解。在数据中,您可以归因地理解,虽然它需要newbies一些工作来首先理解,但一旦你得到它,就符合“很容易”的东西。 ADTS,您可以在属性方面进行思考:简单情况的前提条件,后期,不变性等。但是,在两个方向上处理可扩展性的最少可怕的方式是完全放弃模块化推理,认为全程和挑选以上,您更喜欢的上述方法(可能是归纳推理)。但是整个计划是一个巨大的合理成本。面向方面的编程几乎在全程推理的应变下死亡。

意大利面条零件也很简单。如果您需要可扩展性,您已经拥有了可扩展性:基本模块,添加变量的模块,另一个添加操作的模块。您可能还需要交叉点:一个模块在新的Variant上添加了该新操作的实现。如果您在这些空中填充这些空白并在不同的地方分散在不同位置的脱离实施的小比特,则程序将崩溃。

故事时间:我的博士文论文涉及一种具有可扩展性的语言,这两个尺寸可能有资格出于案例3.实际上代表编译器摘要语法树,我们利用了一些属性来制作工作:

这是编译器,所以我们可以选择设计中的数据沉重的偏见。 (尽管我还包括双重表示作为另一个选择......)

这是一种语言AST,所以我们可以坚持新的变体可以代理(或“向前”或“宏观扩展”)到等同的“宿主语言”树。只是任何数据类型并不总是合理的。

我发现确保我们可以始终安全地自动生成新型变体和新操作的实现的适度限制。因此,意大利面问题被减轻,并且作为奖励用户可以一起撰写语言扩展,他们“只是工作”。漂亮。

但另一个原因我们不希望右下象限是我们真的不需要它。在几乎没有一些情况,似乎你可能是你可能想要它,但经过一些想法,你弄明白了你可以将事情分解为一系列类型,各种各样地表示为数据和对象,可以更准确地模拟您的内容。这是想要用语言语言的对象和数据的另一个有趣的原因,也许我会稍后检查。

通过几种方式混淆数据:有时我们可以敲打一些东西来使用ADT管理。 (参见例如,稍微最佳Java可选。)

有时我们放弃:我们的用例足够小,因为它只是太多的样板,因为我们“应该”而设计的东西,我们应该选择不同的设计。

对于ADTS,请不要从其模块导出数据类型的构造函数。

通过几种方式混淆对象:最简单的对象毕竟只是一个函数闭合。有时这就是我们所需要的一切。

有时我们可以更改设计以使用单个关闭,以响应数据形式的消息。 (从某种意义上,模拟具有单个功能的多种方法。)

有时我们可以通过我们的类型(以更大型签名的成本和更多的样品板)来通过额外参数来通过降压。

有时我们可以在没有映射到(或脱离)包装类型的情况下进行。 有时我们放弃,并尝试使设计与ADT一起工作。 最终,我会写一下所有这些技术。但是今天的帖子足够长。 抽象数据类型。 完全曝光,用户无法扩展,但我们可以在两个尺寸中将其扩展。 在一个理想的世界中,我们鼓励我们通过我们的编程语言“设计”的这些选项(在他们中间有意识地选择。在现实世界中,我们必须记住,无论我们选择什么语言 ,这些都不是最好的,我们有时必须解决对我们强加的限制。