反弹和补遗:再论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.

上篇《煽风点火:也论Bjarne Stroustrup的"基于对象"的含义, 同时B4一下大师》文章中,我觉得其逻辑还不是特别清晰,因为在做论述和回帖中的争论时,对“编译时确定”的具体含义只是处处带到,而没有强调。这个作为前提含义是:“编译时决定”包含了"类型的范围(可能性)和创建逻辑(规则)"的确定,首先"类型的范围"一定是编译才能有的,其次创建逻辑作为一种规则在编译时被确定从而也就被划归到“编译时确定”,在这个角度和静态语言的前提下,没有“运行”时确定类型的空间。这样把矛盾集中在"编译前决定“和”编译时决定“之间,在这个前提下讨论了一些问题。

而往往在我们平时的理解中,虽然可能性和规则是固定的,但最终得出实际类型还得运行,这样造成的结果都可以划归于“运行时确定”的。如果从这个角度去明确“不能编译时确定”的含义,则会包含前文中“编译前限定”了对象类型和一般的“运行时确定”对象类型两种。在这个新的前提下重新咀嚼大师原话,则有一个完全不同的观点包含其中。本文将要叙述的观点还是来自于Bjarne Stroustrup这句话,然而和上篇截然不同。

本文不代表Bjarne的原话可以存在两种理解: 我不敢轻易曲解大师反对“基于对象”的特征这一表达。这不是因为他是大师,而是因为他表达什么就是什么,和对错无关,和我正着理解反着理解无关。

闲话少说,如上段所述:

首先,先从"编译时刻决定"的定义中抛弃"类型的范围和创建逻辑"。 还是如下伪代码:
Object o;
if(情况A) {
    o= new Book();
}
if(情况B) {
    o = new Dog();

}
o.ToString();


原来我们是从编译时刻类型的范围和创建逻辑就被固定,从而o所指代的对象其类型范围和各种可能性在编译时刻被固定的角度来看现在换一个角度,认为因为创建逻辑是在运行时执行的,忽略创建逻辑不可改变所造成的可能性和规则的固定,而把到底o是啥算作运行时决定

或者如下伪代码:

模块一:
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,因为那在静态语言的能力之外)。

反过来再看大师的话,变成了什么?我个人是这么理解的:

请注意加深的字与我上篇文章的不同,同时去掉了最后一句关于“基于对象”的话 一些用户定义类型的对象包含了类型信息(前因)。
这些对象可以便利和安全的应用于上下文(后果),(虽然)它们的类型不能在编译时被决定。

正是由于定义了Book这一类型, 由于提供了足够的类型信息(同时隐藏了其它不需要的信息),使得我们可以便利的在上下文中安全的,方便的使用这些对象,而对象的实际类型,等到运行到创建逻辑时,比如上面伪代码中,获得用户输入之后,再作决定。这样就既获得了限定类型的好处,又获得了变化的能力

也就是说,基于某个类型的对象使用,反过来说是一种好事! 这是原来的争论所没有涉及,我原本计划在未来讨论一下的: 静态面向对象如何通过限制(而不是扩展)我们的能作的事来帮助我们。 总而言之,上下文和隐含的理解不同,这一句话就能表现出多种意思,看来大师们的话有一个特点,就是无论单拿出来,还是有一个语境,无论你怎么玩味,反正赫鲁晓夫总是有理,要不人家是大师呢 -__- 。

我这两篇文的“编译时被决定”的定义的不同主要在于,前文强调创建逻辑是编译结果造成了类型固定(可选择的类型范围和选择方式都是死的),而这篇文章强调创建逻辑在执行中得出了结果(实际类型到底是谁至少在编译结果的可能性中根据一定规则可选)。所以在这篇文章中,可以允许静态语言的“运行时确定”。需要提醒的是,如果你认同我上一篇文章的说法,或者我对Bjarne Stroustrup原文的第一种解释方式,你就一定要搞清楚“编译时被决定”的含义与这篇文章的不同所造成各种变化,否则理解就变成了误解。

在缩小“编译时刻决定”的含义,从而把编译结果在执行时所造成的类型不同(虽然这些逻辑都是编译时确定的)划归入“运行时决定”中去后,我们上一篇所提的概念如何表达呢? 如下:不要把你的对象变成“编译前确定”式的对象,从而失去了在运行时根据可用类型范围和创建逻辑等编译确定的结果决定其具体类型的能力。

换成deerchao想通过大师的话表达的则是:"编译前确定"式的对象使用方法,是基于对象,所以不是面向对象。不过提出前文这一理解,并不代表我和deerchao没有分歧:在前面一篇文章中,我也请大家注意大师原文中的“often”的提法,应该没有说“基于对象”就不能同时"面向对象"(在often之外),我的想法是这还是看具体的设计需要。

考虑到这篇文章的“反弹”特性, 在这里需要说明的是,我无意来回曲解大师的意思忽悠大家一把,只是希望从不同的特殊的角度,通过找些话题来突出一些观点。同时做一个提醒,无论对某事,还是对某大师的话,不要总是言之凿凿的去宣布什么结论。

等到这一波完了,我将对Bjarne Stroustrup这同一句话的两种理解,重新整理成一篇文章,将上一篇的主题"不要定死类型",和这一篇的主题“要适当的定义类型”连接起来,突出这一主题: "既要合理定义类型的功能,但不要设计或实现为不能变化的",这是一个设计中比较难于解决,但很多人处理的太轻易的问题。

P.S.
本文由于和前文说的驴唇不对马嘴,为了防止概念不清晰,编辑了多次头都开始痛,见系统标明的最后编辑时间就知道分裂的去想问题多么可怕了,但这往往是我探究事物的另外一个野路子,希望对思维方式和我相近的兄弟能有所启发。

暂时不发这篇到随笔到首页,首先先达到上篇文章的目的:加深一下“编译前确定”这种情况和“不要定死类型”的认识; 其次,等着某人不借用任何工具的,在上篇文章的那种"编译时刻决定"含义下,“运行时决定的”代码发上来再说 :)。省得这篇文章又掺乎进去,使得原来语境下"编译时刻决定"和“运行时刻决定”的含 义上产生了模糊,把前一篇讨论搞得一团糟。

我现在的估计是,徐老兄发上来的代码,要么在前文语境下的属于“编译时决定”(比如能明显看出类型范围和创建逻辑都在编译时刻确定 了),或者直接或间接的借助了来自.NET Framework的某些能力。要么就是我忽视了什么其它限制没有加上,天知道,到时候再说 :). 不过,无论怎么反复Bjarne Stroustrup这段话,也看不出大师有他这个意思:

“人家大师的意思是说
在编译时无法通过上下文来确定这些对象的类型
那么这种程序叫做基于对象的”

如果"基于对象"这个词在deerchao,亚历山大同志,徐少侠和更多人那里包含着“不够面向对象”这个意思的话。由于大多数动态语言能够非常容易的体现“封装”,“继承”和“多态”,甚至也可以实践“面向接口编程”等原则,我实在只能站在另外一边了。

posted on 2007-09-21 20:51  怪怪  阅读(573)  评论(6编辑  收藏  举报

导航