省略编程语言中的继承


10

我正在开发自己的编程语言。它是一种通用语言(不建议将其用于桌面,请考虑在桌面上使用静态类型的Python,即int x = 1;)。

您认为不允许继承或Mixins可以吗?(假设用户至少具有界面)

例如: Google Go,一种系统语言,通过不允许继承而震惊了编程社区。

Answers:


4

实际上,我们越来越发现,使用继承是次优的,因为(除其他外)继承会导致紧密耦合。

实现继承总是可以用接口继承加组合来代替,并且现代的软件设计趋向于越来越少地使用继承,而倾向于组合。

因此,是的,特别是不提供继承是完全有效且非常流行的设计决策。

另一方面,mixin 甚至还不是主流语言功能,并且确实提供了“ mixin”功能的语言通常通过它了解非常不同的事物。随时提供或不提供。我个人认为它非常有用,但是正确实施它可能很难。


13

在没有考虑其余语言语义的情况下,就是否有必要继承(或确实具有任何单个功能)进行推理是没有意义的;你在真空中争论。

您需要的是一致的语言设计理念;语言需要能够优雅地解决其设计的问题。实现此目标的模型可能需要继承,也可能不需要继承,但是如果没有大局观,很难判断这一点。

例如,如果您的语言具有一流的功能,部分函数应用程序,多态数据类型,类型变量和泛型类型,则您所涵盖的基础与经典OOP继承几乎相同,但是使用了不同的范例。

如果您有较晚的绑定,动态类型输入,作为属性的方法,灵活的函数参数和一流的函数,那么您也将涵盖相同的基础,但再次使用不同的范例。

(概述的两个范例的查找示例留给读者练习。)

因此,考虑一下您想要的语义类型,使用它们,看看它们在没有继承的情况下是否足够。如果不是,则可以决定将继承放入混合中,也可以决定缺少其他内容。


9

是。

我认为不允许继承是可以的,特别是如果您的语言是动态键入的。您可以通过例如委托或组合来实现类似的代码重用。例如,我用JavaScript 编写了一些中等复杂的程序-好吧,不是那么复杂:)-没有任何继承。我基本上将对象用作代数数据结构,并附加了一些作为方法的功能。我还有很多函数,这些函数不是对这些对象进行操作的方法。

如果您具有动态类型输入(我假设您已经这样做了),那么也可以具有不继承的多态。另外,如果您允许在运行时向对象添加任意方法,则实际上并不需要太多诸如mixins之类的东西。

另一个选择(我认为是不错的选择)是模拟JavaScript和使用原型。它们比类简单,但也非常灵活。只是要考虑的事情。

所以,总的来说,我会去的。


2
只要有一种合理的方法来完成通常用继承处理的任务,那就继续吧。您可能会尝试一些用例,以确保(可能甚至可以征求他人的示例问题,以避免自我偏见...)
即将

不,它是静态和强类型的,我会在顶部提到。
Christopher

由于它是静态类型的,因此不能只在运行时向对象添加随机方法。但是,您仍然可以进行鸭子输入:查看Gosu协议作为示例。整个夏天,我使用了一些协议,它们确实很有用。Gosu还具有“增强”功能,这是在事实发生后向类添加方法的一种方式。您也可以考虑添加类似的内容。
Tikhon Jelvis'1

2
拜托,拜托,漂亮,请停止关联继承和代码重用。众所周知,继承对于代码重用而言确实是一个糟糕的工具。
Jan Hudec 2012年

3
继承只是代码重用的一种工具。通常,它是一个非常好的工具,而有时却是可怕的。优先于继承而不是组成并不意味着根本不使用继承。
Michael K

8

是的,忽略继承是完全合理的设计决定。

实际上,有非常好的理由删除实现继承,因为它会产生一些极其复杂且难以维护的代码。我什至认为将继承(因为继承通常在大多数OOP语言中实现)视为不适当的功能。

例如,Clojure不提供实现继承,而是提供一组正交功能(协议,数据,函数,宏),这些功能可用于获得相同的结果,但更为简洁。

这是一个使我对这个一般主题很有启发性的视频,其中Rich Hickey指出了编程语言(包括继承)中复杂性的基本来源,并提出了每种替代方法的含义:简单变得容易


在许多情况下,接口继承更为合适。但是如果与节制一起使用,则实现继承可以删除很多样板代码,这是最优雅的解决方案。它只要求比一般的Python / JavaScript猴子更聪明,更谨慎的程序员。
埃里克·阿拉帕(ErikAlapää)2016年

4

当我第一次遇到VB6类不支持继承(仅支持接口)的事实时,这确实让我很烦(现在仍然如此)。

但是,它如此糟糕的原因是它也没有构造函数参数,因此您无法执行常规依赖项注入(DI)。如果您有DI,那它比继承更重要,因为您可以遵循优先于继承而不是继承的原则。无论如何,这是重用代码的更好方法。

虽然没有Mixins?如果您想实现一个接口并将该接口的所有工作委托给通过依赖注入的对象集,那么Mixins是理想的选择。否则,您必须编写所有样板代码以将每个方法和/或属性委托给子对象。我做了很多(由于使用C#),这是我希望不必做的一件事。


是的,促使这个问题出现的是SVG DOM的实现,其中代码重用非常有益。
Christopher

2

要不同意另一个答案:不,当功能与您想要的功能不兼容时,您将其丢弃。Java(和其他GC语言)放弃了显式的内存管理,因为它更希望类型安全。Haskell放弃了变异,因为它想要更多的方程式推理和幻想类型。甚至C都放弃(或宣布为非法)某些种类的别名和其他行为,因为它希望更多地进行编译器优化。

所以问题是:除了继承,您还想要什么?


1
只有明确的内存管理和类型安全是完全不相关的,并且绝对不是不兼容的。同样适用于突变和“奇特类型”。我确实同意一般的推理,但是您的示例相当不正确。
康拉德·鲁道夫2012年

@KonradRudolph,内存管理和类型安全性密切相关。一free/ delete操作使您能够以无效引用; 除非您的类型系统能够跟踪所有受影响的引用,否则会使该语言不安全。特别是C和C ++都不是类型安全的。的确,您可以妥协一个或另一个(例如线性类型或分配限制)以使它们达成一致。确切地说,我应该说Java想要具有特定,简单类型系统和不受限制的分配的类型安全性。
Ryan Culpepper 2012年

好吧,这取决于您如何定义类型安全性。例如,如果您采用(完全合理的)编译时类型安全性定义,并且希望引用完整性成为类型系统的一部分,那么Java也不是类型安全的(因为它允许null引用)。禁止free是相当随意的附加限制。总之,一种语言是否为类型安全的取决于您对类型安全的定义,而不是取决于该语言。
康拉德·鲁道夫

1
@Ryan:指针是完全类型安全的。他们总是按照自己的定义行事。可能不是您喜欢它们的方式,但这始终取决于它们的定义。您正在尝试将它们拉伸成不是它们的东西。智能指针可以在C ++中相当简单地保证内存安全。
DeadMG 2012年

@KonradRudolph:在Java中,每个T引用都引用一个null或一个扩展类的对象Tnull很难看,但操作时会null抛出定义良好的异常,而不是破坏上面的类型不变式。与C ++的对比:在调用之后deleteT*指针可能指向不再保存T对象的内存。更糟糕的是,使用分配中的指针进行字段分配,如果刚好将另一个类的对象的字段恰好放在附近的地址中,则可能会完全更新该字段。根据该术语的任何有用定义,这并不是类型安全。
Ryan Culpepper 2012年

1

没有。

如果您想删除基本语言功能,那么您就在全球范围内声明,它从来没有,永远没有必要(或者实现起来毫无道理,这在这里不适用)。在软件工程中,“从不”是一个强有力的词。您需要一个非常有力的证明来做出这样的陈述。


8
这几乎是不对的:Java的整个设计都是要删除诸如运算符重载,多重继承和手动内存管理之类的功能。另外,我认为将任何语言功能都视为“神圣的”是错误的。除了证明删除某个功能是合理的之外,您还应该证明添加该功能是合理的-我想不出每种语言都必须具备的任何功能。
迪洪

6
@TikhonJelvis:这就是Java是一种糟糕的语言的原因,除了“使用垃圾收集的继承”之外,它什么都没有,这是第一个原因。我同意,语言功能必须合理,但这是基本语言功能-它具有许多有用的应用程序,并且程序员必须在不违反DRY的前提下进行复制。
DeadMG 2012年

1
@DeadMG哇!相当强的语言。
Christopher

@DeadMG:我开始写反驳作为评论,但后来我将其变成了答案(qv)。
Ryan Culpepper'1

1
“完美的实现不是在没有剩余的东西要添加时,而是在没有剩余的东西要删除时”(q)
9000年

1

从严格的C ++角度来讲,继承对于两个主要目的很有用:

  1. 它允许代码可重用
  2. 与子类中的重写结合使用,它允许您使用基类指针来处理子类对象,而无需知道其类型。

对于第1点,只要您有某种方式共享代码而不必沉迷于杂技,则可以消除继承。对于第2点,您可以采用Java方式并坚持使用接口来实现此功能。

删除继承的好处是

  1. 防止长层次结构及其相关问题。为此,您的代码共享机制应该足够好。
  2. 避免更改父类以免破坏子类。

权衡主要是在“不要重复自己”和一方面的灵活性与另一方面的避免问题之间。我个人不希望看到C ++继承继承,因为其他开发人员可能不够聪明,无法预见问题。


1

您认为不允许继承或Mixins可以吗?(假设用户至少具有界面)

您可以在没有实现继承或mixins的情况下完成许多非常有用的工作,但是我想知道您是否应该具有某种接口继承,即一条声明,说明对象是否实现接口A,那么它也需要接口B(即, A是B的特化,因此存在类型关系。另一方面,生成的对象仅需要记录它实现了两个接口,因此那里没有太多的复杂性。完全可行。

但是,缺少实现继承有一个明显的缺点:您将无法为您的类构造数字索引的vtable,因此必须对每个方法调用进行哈希查找(或找出避免它们的聪明方法)。如果您通过这种机制甚至路由基本值(例如,数字),这可能会很痛苦。当您在每个内部循环中多次点击它时,即使是非常好的哈希实现也可能会很昂贵!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.