【问题标题】:What are the schools of OOP? [closed]OOP的学校有哪些? [关闭]
【发布时间】:2010-11-11 18:06:12
【问题描述】:

Smalltalk OOP 和 Simula OOP 之间是否存在哲学差异?

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

【问题讨论】:

  • 好问题。虽然这主观的有一些微妙之处,但值得指出的是,答案应该尽量避免从经验上说一个比另一个更好。存在大量不同的实现,而且还有更多被广泛使用这一简单的事实应该清楚地表明,其中一种尺寸并不适合所有。

标签: c# java c++ oop


【解决方案1】:

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

在所有情况下,关于 staticdynamic 类型系统的陈述主要意味着其中一个或另一个,问题远未明确或明确定义。 许多语言也选择模糊选项之间的界限,因此这绝不是二元选择列表。

Polymorphic late binding

或“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. Class based
    • 方法实现被组织成称为类的组。当需要实现继承时,定义一个类扩展父类。通过这种方式,它获得了父级(字段和方法)所有公开的方面,并且可以选择更改某些/所有这些方面,但不能删除任何方面。您可以添加和更新,但不能删除。
    • 示例:C++/Java/C#(注意 SmallTalk 和 Simula 都使用这个)
  2. Prototype based
    • 对象的任何实例都只是已识别方法(通常由名称识别)和以(再次命名)字段形式的状态的集合。每当需要这种“类型”的新实例时,都可以使用现有实例来克隆一个新实例。这个新类保留了前一个类的状态和方法的副本,但随后可以进行修改以删除、添加或更改现有的命名字段和方法。
    • 示例:Self/JavaScript

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

基于接口或类

或“什么或如何?”

  1. 接口列出了所需的方法。他们是契约
    • 示例:VB6
  2. 类列出了必需但可以选择提供其实现的方法
    • 示例:模拟

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

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

单继承或多继承

或“谁是爸爸?”

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

这个问题引发了相当大的争论,尤其是因为它是 C++ 的 OOP 实现与许多现代静态类型语言(如 c# 和 java 等可能的继任者)之间的关键区别。

可变性

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

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

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

他们的 OOP 的“纯粹性”

或“一切都是对象吗?”

  1. 绝对系统中的一切都被视为一个对象(甚至可能下至方法本身,它们只是另一种对象,可以像其他对象一样进行交互)。
    • 示例:SmallTalk
  2. 并非所有事物都是对象,您不能将消息传递给所有事物(尽管系统可能会跳出圈子来让它看起来像是可以传递的)
    • 示例:C++/C#/Java(见注释*)

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

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

静态或动态

或“你认为你是谁?”

语言设计的一个更为普遍的方面,这里不赘述,但这一决定中固有的选择会影响前面提到的 OOP 的许多方面。

只是多态后期绑定的一些方面可以依赖于:

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

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

【讨论】:

  • 我最大的抱怨——继承的副标题应该是“babby是如何形成的?”
  • :) 我背叛了我的英语
  • 很好地比较了我从未理解过的东西,比如原型继承 :)
  • 正确,如果这是某些链接接受的回答时间
  • 现在这就是我所说的答案!
【解决方案2】:

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

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

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

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

【讨论】:

  • +1,不仅是动态类型,而且是基于消息传递的。
  • 据我了解,C++ 更倾向于 ADT,而目前的 Java 和 C# 则将一切都视为对象。这些观点有区别吗?
  • 并非所有事物都是 Java 中的对象...
  • @Ben Hughes...嗯,这是为了性能,而不是设计。
  • @AraK 不,这是暴露给外界的(在 java 中自动装箱之前更是如此),所以设计而不是实现。将其设计为就语言用户而言一切都是对象,但随后在幕后进行操作以提高性能很好(因为 Java 6 这实际上发生了),但这不是 java 所做的。很明显,有些东西称为原语,它们看起来有点像对象,但在几个关键方面具有不同的语义。
【解决方案3】:

Java 和 C# 绝对不属于 Smalltalk 家族。 Alan Kay 甚至说,当他创建 OOP 时,他并没有考虑 Java 或 C++ 之类的东西。 Java、C# 和 C++ 都以几乎相同的方式解释 OOP。

Smalltalk 和 Ruby 等语言有一个完全不同的模型,它基于消息传递。在 C++ 中,类本质上是方法和状态的命名空间。方法调用在编译时绑定。 Smalltalk 直到运行时才绑定“方法调用”。其结果是在 C++ 中

foo->bar

被编译为“调用 foo 对象的 bar 方法”。如果 bar 不是虚拟的,我想 bar 方法的地址是专门引用的。

在 Smalltalk 中

foo bar

表示“将消息栏发送到 foo 对象”。 foo 可以在这条消息到达时为所欲为。默认行为是调用名为bar 的方法,但这不是必需的。此属性在 Ruby 中用于 ActiveRecord 列访问器。当您有一个 ActiveRecord 对象并将其数据库表中的列名称作为消息发送给它时,如果没有定义该名称的方法,它会检查表上是否有该名称的列,如果有返回值。

消息传递可能看起来像一个微小的、无关紧要的细节,但除此之外,OOP 的其余部分很容易流动。

“对我来说,OOP 仅意味着消息传递、本地保留、保护和隐藏状态进程,以及所有事物的极端后期绑定。它可以在 Smalltalk 和 LISP 中完成。可能还有其他系统可以做到这一点可能,但我不知道他们。” ——Alan Kay,Smalltalk 的创建者

【讨论】:

    【解决方案4】:

    Eiffel 是一种静态类型、编译、多重继承的纯 OOP 语言。

    http://dev.eiffel.com

    【讨论】:

    • Bertrand Meyer 在出版他的第一本 O-O 书籍并通过合同提出编程时有过那个短暂而闪亮的时刻。然后他出版了《面向对象的软件构建》的第二版,这是一本 1200 页的巨著,从那以后就不再相关了。埃菲尔从未去过任何地方。
    【解决方案5】:

    在现代(我用这个词很轻)OO 编程语言中,Objective C 最像 smalltalk。

    消息:

    在 C++、C# 和 Java 中:消息在编译时绑定。
    您可以将方法调用视为发送给对象的消息。

    在 Objective C 中,Smalltalk:消息在运行时绑定。

    【讨论】:

      【解决方案6】:

      我会说静态类型和动态类型的 OOP 是同一 OOP 学院中的两个独立学科。

      【讨论】:

        【解决方案7】:

        Java、C# 和 C++ 都遵循类似的 OOP 策略。它基于在编译时绑定的函数调用。根据他的调用,当编译发生时,直接函数调用或到 vtable 的偏移量是固定的。相比之下,Smalltalk 的 OOP 基于消息传递。从概念上讲,每个方法调用都是给接收对象的消息,询问它是否有一个名为“Foo”的方法。

        Smalltalk 没有接口的概念。它只有类似的外观方法。在 C++ 语言组中,一切都绑定到接口。如果不实现 QueryInterface(即使它只是一个存根),就无法实现 AddRef 和 Release,因为它们都是 IUnknown 接口的一部分。在 Smalltalk 中,没有 IUnknown。只有3个函数的集合,可以实现也可以不实现。

        【讨论】:

          【解决方案8】:

          我想说基于类的 OOP(其中 Smalltalk、Simula、C# 和 Java 都是示例)和基于原型的 OOP(从 Self 开始并且最普遍)之间在概念上也有很大的区别在 JavaScript 中)。

          【讨论】:

            【解决方案9】:

            除了以上几点之外,还有 Smalltalk 与 Simula 的概念分解。

            从概念上讲,“Smalltalk 风格”通常表示调用消息时运行的方法是在运行时确定的,有助于多态性。

            另一方面,“Simula-style”通常似乎表明所有方法调用实际上只是编写重载函数调用的一种方便方式——没有运行时多态性。 (如果我错了,请纠正我。)

            在中间,我们有 Java:默认情况下所有方法都是虚拟的,但是是静态类型的,并且具有编译时类型分派。

            例子:

            // C++
            class Base {
              void doSomething() {
                cout << "Base::doSomething() called!\n";
              }
            }
            class Derived : Base {
              void doSomething() {
                 cout << "Derived::doSomething() called!\n";
              }
            }
            int main() {
              Base* b = new Base();
              Derived* d = new Derived();
              b->doSomething(); // prints "Base::doSomething() called!"
              d->doSomething(); // prints "Derived::doSomething() called!"
              Base* d2 = d;     // OK; Liskov substitution principle.
              d2->doSomething(); // prints "Base::doSomething called!"  (!)
              delete b;
              delete d;
              return 0;
            }
            

            VS:

            // Objective-C
            //Base.h
            @interface Base
            {
            }
            -(void)doSomething
            @end
            //Base.m
            #import "Base.h"
            @implementation Base
            -(void) doSomething {
              printf("doSomething sent to Base!");
            }
            @end
            //Derived.h
            #import "Base.h"
            #import "Base.m"
            @interface Derived : Base
            {
            }
            @end
            //Derived.m
            #import "Derived.h"
            @implementation Derived
            -(void) doSomething {
              printf("doSomething sent to Derived!")
            }
            @end
            
            //Main.m
            #import "Base.h"
            #import "Base.m"
            #import "Derived.h"
            #import "Derived.m"
            int main() {
              Base* b = [[Base alloc] init];
              Derived* d = [[Derived alloc] init];
              [b doSomething]; // prints "doSomething sent to Base!"
              [d doSomething]; // prints "doSomething sent to Derived!"
              Base* d2 = d;
              [d2 doSomething]; // prints "doSomething sent to Derived!"
              [b release];
              [d release];
              return 0;
            }
            

            【讨论】:

            • simula 肯定具有运行时多态性。实际上,您可以实例化一个没有实现方法的类,调用它会导致运行时错误。
            猜你喜欢
            • 2011-04-19
            • 1970-01-01
            • 2011-01-01
            • 2010-10-05
            • 1970-01-01
            • 1970-01-01
            • 2011-10-30
            • 1970-01-01
            • 2010-09-05
            相关资源
            最近更新 更多