OOP的学校是什么?[已关闭]

2022-09-01 11:12:21

Smalltalk OOP和Simula OOP之间有哲学上的区别吗?

这是一个与Java和C#相关的问题,C++间接地。据我所知,C++是基于Simula的,但Java和C#或多或少来自Smalltalk家族。


答案 1

在更广泛的OOP横幅中,“风格”的几个关键“差异”。

在所有情况下,关于静态动态类型系统的陈述主要意味着一个或另一个,问题远未明确或明确定义。此外,许多语言选择模糊选择之间的界限,因此这无论如何都不是二元选择的列表。

多态性晚期结合

或“这是什么意思?foo.Bar(x)

  1. 类型的层次结构扁平化为每个实例的特定实现(通常通过 vtable 完成),并且通常允许显式引用基类实现。
    • 从概念上讲,您查看foo在调用站点上的最具体的类型。如果它为调用的参数 x 提供了 Bar 的实现,则选择 foo 的父级并重复该过程。
    • 示例:C++/Java/C#,则经常使用“Simula style”。
  2. 纯粹的消息传递。foo 中处理消息“命名”“Bar”的代码被要求接受 x。只有名称很重要,而不是呼叫网站可能对Bar的确切含义有任何假设。与之前的样式相反,在以前的样式中,所讨论的方法为 Bar,已知它是根据编译时定义的类型层次结构的任何已知内容定义的(尽管层次结构中的精确位置留到运行时)。

1通常用于静态类型框架中,它是一个错误,在编译时检查是否存在这样的实现。此外,如果 x 和 y 是不同的类型,则语言通常会区分 Bar(x) 和 Bar(y)。这是方法重载,并且具有相同名称的结果方法被视为完全不同。

2通常用于动态语言(倾向于避免方法重载),因此,在运行时,foo类型可能没有名为“Bar”的消息的“处理程序”,不同的语言以不同的方式处理它。

如果需要,两者都可以在幕后以相同的方式实现(通常是第二种的默认值,Smalltalk风格是调用函数,但这并非在所有情况下都成为定义的行为)。由于前一种方法通常可以很容易地实现为简单的指针偏移函数调用,因此可以更容易地相对快速地实现它。这并不意味着其他样式不能快速制作,但可能需要做更多的工作来确保在这样做时不会损害更大的灵活性。

继承/重用

“或者”婴儿从哪里来?

  1. 基于类
    • 方法实现被组织到称为类的组中。当需要实现继承时,将定义一个类来扩展父类。通过这种方式,它获得了父项的所有公开方面(字段和方法),并且可以选择更改某些/所有这些方面,但不能删除任何方面。您可以添加和更新,但不能删除。
    • 示例:C++/Java/C#(请注意,SmallTalk 和 Simula 都使用此功能)
  2. 基于原型
    • 对象的任何实例都只是标识的方法(通常由名称标识)和状态(再次命名)字段的集合。每当需要此“类型”的新实例时,可以使用现有实例来克隆新实例。此新类保留前一类的状态和方法的副本,但随后可以对其进行修改以删除、添加或更改现有的命名字段和方法。
    • 示例:自写/JavaScript

同样,1往往发生在静态语言中,2发生在动态语言中,尽管这绝不是他们简单地适合这种风格的要求。

基于接口或类

或“什么或如何?

  1. 接口列出了所需的方法。它们是一份合同
    • 示例:VB6
  2. 类列出了必需的方法,但可以选择提供其实现
    • 示例:模拟

这在很大程度上不是一个二元选择。大多数基于类的语言都允许抽象方法的概念(那些尚未实现的方法)。如果你有一个类,其中所有方法都是抽象的(在C++中称为纯虚拟),那么这个类相当于一个接口,尽管它可能还定义了一些状态(字段)。一个真正的接口不应该有状态(因为它定义了什么是可能的,而不是它是如何发生的。

只有较旧的OOP语言才倾向于完全依赖于其中一种。
VB6 仅在接口上,没有实现继承。
Simula 允许您声明纯虚拟类,但您可以实例化它们(使用时出现运行时错误)

单个或多个继承

“或者”爸爸是谁?

    • 只有一种类型可以是另一种类型的父级。在上面的基于类的表单中,您只能扩展(从中获取实现)一种类型。通常,这种形式包括接口的概念,作为语言的第一类方面来弥补这一点。
    • 优点包括更清晰的元数据和内省,更简单的语言规则。
    • 复杂性包括使有用的方法更难进入范围(如MixIns扩展方法试图缓解此类问题)
    • 示例:C#/java
  1. 多个 - 您可以扩展多个类
    • 优点包括某些结构更容易建模和设计
    • 复杂性包括用于冲突解决的复杂规则,特别是当存在可能采用任一父类型的重载方法时。
    • 示例:C++/埃菲尔铁塔

这个问题引起了相当大的争论,特别是因为它是C++的OOP实现与许多现代静态类型语言之间的关键区别,这些语言被认为是可能的继承者,如c#和java。

易变性

“或者”你想对我做什么?

  1. 可变
    • 对象一旦创建,就可以更改其状态。
  2. 不可变
    • 对象一旦创建就无法更改。

通常,这不是全有或全无,它只是一个默认值(最常用的OOP语言默认为默认可变)。这可能会对语言的结构产生很大的影响。许多包含 OOP 功能的主要函数式语言默认对象具有不可变状态。

他们的OOP的“纯粹性”

“或者”一切都是对象吗?

  1. 绝对,系统中的所有内容都被视为一个对象(甚至可能归结为方法本身,这些方法本身只是另一种对象,可以像其他对象一样与之交互)。
    • 示例:SmallTalk
  2. 并非所有内容都是对象,您无法将消息传递给所有内容(尽管系统可能会跳过箍以使它看起来可以)
    • 示例:C++/C#/Java(请参阅注释*)

这是非常复杂的,因为像基元的自动装箱这样的技术使它看起来好像一切都是,但你会发现存在几种边界情况,其中发现了这种“编译器魔法”,并且在幕后发现了众所周知的Oz向导,结果是问题或错误。在默认为不可变性的语言中,这种情况不太可能发生,因为对象的关键方面(它们同时包含方法和状态)意味着与对象相似但不太复杂的可能性较小的事物。

  • 关于Java / C#,自动装箱(或在c#中)系统允许您在语法上将任何变量视为对象,但实际上并非如此,这在尝试锁定自动装箱对象(被编译器拒绝,因为这将是一个明显的错误)等领域表现出来。

静态或动态

“或者”你以为你是谁?

语言设计的一个更普遍的方面,不是在这里要讨论的,但这个决定中固有的选择会影响OOP的许多方面,如前所述。

多态性晚期结合的方方面面可以依赖于:

  • 要将消息传递到的对象的类型(在编译时/运行时)
  • 正在传递参数的类型(在编译时/运行时)

语言的动态程度越高,这些决策往往就越复杂,但相反,语言用户在决策中的输入就越多,而不是语言设计者。在这里举个例子是鲁莽的,因为静态类型语言可能会被修改以包括动态方面(如c# 4.0)。


答案 2

我会把Java和C#也放在Simula阵营中:

  • Smalltalk是动态类型的,与你引用的其他四种语言完全不同。

  • Smalltalk在结构上是类型化的(别名鸭子类型),而其他四个是名义上的类型。

(Java和C#与Smalltalk的共同点主要是基于VM,但对编程风格几乎没有影响)。