煽风点火:也论Bjarne Stroustrup的"基于对象"的含义, 同时B4一下大师

这篇文章在我看过原文之后,  确认是一种误解, 当个消遣看的话, 里面还有点东西, 不过价值不大, 切不可当真! 理解本文的关键是, 确认其中"编译时刻确定"所包含的内容:

本文中"编译时刻确定", 包括了对象创建逻辑的确定. 根据Bjarne Stroustrup原文上下文理解, 则不包括对象创建逻辑的确定.
 
如果谁在之前看过这篇文章, 造成了误解, 我在这里做一个真诚的道歉, 并保证这是我唯一一篇如此不谨慎的文章; 以后我是宁可不写也不能造成误导, 作为一个博客园的新作者, 这给我上了很关键的一课, 也请后来人引以为鉴. 关于大师原文的意思, 请参考我当天的第二篇文章:


反弹和补遗:再论Bjarne Stroustrup的"基于对象"的含义

上面文章中的另一种解释, 理解上和大师的原意不会有太多偏差, 其中涉及到本文的部分, 可以跳过, 其它部分请放心阅读和讨论.

以下是文章原来的部分.

在The C++ Programming Language 3rd (Bjarne Stroustrup, 1997)里:
第9页:
Objects of some user-defined types contain type information.
Such objects can be used conveniently and safely in contexts in which their type cannot be deter-mined at compile time. Programs using objects of such types are often called object based.

一些用户定义类型的对象包含了类型信息(前因)。
这些对象可以便利和安全的应用于上下文,它们的类型不能在编译时被决定(后果)。程序对这些类型的对象的使用经常被叫做基于对象。

说实话,翻译的比较蹩脚,因为大师的英文真的不那么深入浅出。不过要说的是,很多人的翻译和理解是绝对错误的,包括deerchao把compile time误认为是run time, 同时把determined翻译成了分辨; 也包括徐少侠对这一段话的理解。这段话的前提首先要搞清楚: C++是一门静态面向对象语言,所以基本不存在什么运行时决定类型的问题. 很自然的矛盾就指向于: 对象的类型是编译前就定死了, 或编译时确定之。搞清楚了矛盾在哪儿, deerchao就不必放弃这句话作为他的论点基础了, 因为deerchao原贴基本上就是围绕这句话展开的, 自然弹药也就充足了.

接下来详细说明。

Object o;
if(情况A) {
    o= new Book();
}
if(情况B) {
    o = new Dog();

}
o.ToString();

以上流程中,变量o所指代的对象的实际类型是在编译时被决定的。这个对象的实际类型在情况A下是Book,然后再另一种情况下则是Dog.

Book b;
if(情况A) {
    b = new TextBook();
}
if(情况B) {
    b = new GraphBook();
}
b.Open();
b.DoSomething();
以上流程同理。

b的类型被限制为基类Book(通过定义时Book这个类型信息),但它所指代的对象的实际类型具体是TextBook还是GraphBook,虽然在这个流程中有所变化,但这些变化都在编译时刻决定(就决定—相对于动态语言或某些后面说到的做法,才决定—相对于用户声明直接把类型限制死),编译的结果中就包含着创建不同实际类型的这一被指代对象的逻辑或信息。同时,由于b被声明为Book, 假设b是public的,那么b所指代的那个对象的实际类型到底是什么,也被限定为Book的子类。如果有另外一个新的模块(比如另外一个DLL)含有跟创建该相关的流程,调用了这个b,并给这个b赋值,这个过程是在新模块的编译中被确定的,同时也就确认了变量b所指代的对象在流程中实际类型

很显然对于Book b这样的做法使得b所指代的对象在编译时, 其实际类型可以是不固定的(但按照静态语言本身的能力来说,不能在被编译之后的其它过程中才固定),由编译的结果来确定在这种情况下是什么,在那种情况下是什么。这叫做编译时刻确定. 但是如果TextBook b, 假设TextBook是继承链上的最后一个类,那么其实际类型由于被声明定死不能改变, 也就是说不具有推后到任何一个编译过程中去确定类型的能力,这叫做不能在编译时刻确定。而我们经常用的拥抱变化的手段之一,就是把b所指代的对象的实际类型的决定,放到其它单独编译的模块里去,让其它编译结果决定我们现在写的程序的对象的实际类型。

那么关于运行时确定类型这种误解来自于哪里呢? 关键在于以下具体做法的流行:

模块一:
Book b = BookFactory.Instance.Create(最终用户决定的参数);
b.Open();
b.DoSomthing();

public class BookFatory {
    public static BookFatory Instance {
        return (BookFactory)根据配置文件获得的实例.       //注意: 不是编译时确定, 而是运行时确定, 后面解释!
    } 

    public abstract Book Create(String param);
}


模块二:
public 某特殊BookFactory : BookFactory  {
    public override Book Create(String param) {
       switch(param) {
            case "参数A":
                return new TextBook();
             case "参数B":
                return new GraphBook();
             default:
                return new DefaultBook();
        }
    }
}

模块二中关于返回的对象到底是什么类型的, 是编译时刻确定的. 但是对于模块一, 有一个和我所说的冲突的地方(BookFactory的具体类型的确定), 也是为什么很多人理解为运行时刻改变类型的原因. 只所以出现这种误解, 是因为混淆了Activitor.CreateInstance(Type type)这类工具箱和语言的界限造成的. 对于C++和C#这类静态面向对象语言来说, 它本质上不具有在运行时刻改变类型的能力. 它还缺乏一些其它的能力, 所以我们需要Activitor和反射这些东西来帮助我们. 但在大师讨论这个问题的时候, 并没有包含额外的非语言的能力的.

于是我们讨论时, 也要排除其它因素的干扰. 考虑如下代码:
第一种:
TextBook b;
if(情况A) {
    b = new TextBook();
    b.Open();
}
b.DoSomething();
if(情况B) {
    b = new GraphBook(); //不行
}

第二种:
TextBook b = BookFactory.Instance.Create(用户输入参数); //不行

为什么不行? 因为对象定义类型时, 包括了过多的类型信息啊! 这就是不能在编译时刻确定的本质含义: 大师说, 用户定义类型的对象(在这里由b指代)包含了类型信息(TextBook b)的用法, 相对于(Book b)这种b所指代的对象在某种情况下是TextBook在另外一种情况下是GraphBook在过程的编译时刻才确定(而非很多人想像的在执行过程中确定)的用法,  把前者叫做"基于对象".

deerchao对大师把握的本意并没有错, 问题是他因为最初的翻译错误, 轻易的把这一条放弃了. 但是我发现, 基于对第一条的错误理解, 大家的讨论向着一边倒不利于deerchao的方向发展; 出于搅风搅雨, 煽风点火的目的, 我在此澄清几点:

1. 静态语言不能够"运行时确定", 但是能"编译时确定"(是TextBook/GraphBook/TextBook或GraphBook,根据编译时获得信息不同而不同), 这是相对于用户声明"包含类型信息"(某特殊Book), 所以实际类型不能推迟到"编译时确定", 因为它已经在编译前就定死了.
2. 让deerchao和咱们走入误区(把compile time和run time混淆)的根本原因在哪里: 引入了静态面向对象语言本身并不包含的特性, 这也是很多框架通过提供工具去扩展静态语言的能力的方向之一.
3. 对于TextBook b而不是Book b的用法, 大师确实说"常常把它们叫做基于对象", 但是请大家注意, 这里有个"often".

接下来说一下徐少侠兄弟的言论:

"举例来说
Javascript不是面向对象的
虽然他里面的变量在运行时实质都是有类型的,但是在非运行时无法知道是什么类型
同理很多脚本语言都是这样的
因此由于在语言中支持对象的使用,但又有类似这样的缺陷,这些脚本语言被称为基于对象的语言"

这个纯粹是一个误解. 把静态面向对象语言和原装面向对象语言给等同了. 其实类似于Javascript的脚本语言, 现在在国内外已经被广泛的认识了. 还抱着不是静态面向对象语言就不是面向对象的看法来否认这些语言面向对象的特性甚至本质, 那可以说知识稍微有些过时. 恰恰相反, 现在更多的讨论是, 静态面向对象语言对面向对象的曲解, 和由于其自身限制, 对我们开发软件带来的麻烦. 向动态语言的优点靠拢, 也是Activitor, 反射, 甚至Emit这些手段风行的原因. 但决不能说, 不是静态的, 就不是面向对象的, 这哪怕跟20年前的面向对象原始概念, 也是不相符的.

同时由于这些语言可以做到在运行时刻才确定其类型, 不说其更多的好处, 仅只与"编译时刻才确定其类型(或类型的范围及该对象的创建逻辑)"相对应, 按照Bjarne Stroustrup的观念, 也绝不可能说他们是基于对象....

多说的一句其实不该说的话是, 徐少俠兄弟, 在我在博客园的几天的感觉, 说实话, 怎么说呢, 就像他自己说的, 一看就像"培训师".., 我觉得我这种感觉, 对我对"老师"的认知是不符的. 当老师就应该认真负责一些, 别人(比如我)可以大嘴, 你负责着很多人对知识的把握, 不太熟悉的不能想当然的就乱说啊.... 如果有误会,我在这里道歉先。

最后, B4一下Bjarne Stroustrup的语言表达能力, over.

posted on 2007-09-21 15:04  怪怪  阅读(2470)  评论(41编辑  收藏  举报

导航