最近学习了一下Smalltalk,然后深深的喜欢上了这个古老的语言。Smalltalk语言只具有一个很小的语言核心,这个核心由大约10几个关键字和一些基础的面向对象语义构成。而且关键字都是象: . ; ( ) [ ] | := 之类的简单符号,并没有提供最基本控制的流程。最开始的时候这让我很迷惑,虽然循环结构可以用递归表示,但是分支怎么办?然后发现了一个很酷的特性,Smalltalk可以仅仅通过面向对象的语义来实现分支结构(其实就是State Pattern),具体的代码如下

Boolean>>ifTrue: aBlock
  self subclassResponsibility

Boolean>>ifFalse: aBlock
  self subclassResponsibility

True>>ifTrue: aBlock
  ^aBlock value.

True>>ifFalse: aBlock
  ^nil.

False>>ifTrue: aBlock
  ^nil.

False>>ifFalse: aBlock
  ^aBlock value.

然后就可以,

4 〉3 ifTrue: [Transcript show: 'Hello']

因为在Smalltalk里,一切皆对象且从左到右求值,于是4 > 3 返回true,true是类True的唯一实例,然后就可以对它发送消息,ifTrue:,于是调用了^aBlock value.来对传进去的BlockClosure求值。


下面是类似的java的类似代码。

 

 1public class abstract Boolean {
 2   public static final TRUE = new True();
 3   public static final FALSE = new False();
 4
 5   public abstract Object ifTrue(Block aBlock);
 6   public abstract Object ifFalse(Block aBlock);
 7}

 8
 9class True extends Boolean {
10
11   public Object ifTrue(Block aBlock) {
12       return aBlock.execute();
13   }

14
15   public Object ifFalse(Block aBlock) {
16       return null;
17   }

18}

19
20class False extends Boolean {
21
22   public Object ifTrue(Block aBlock) {
23       return null;
24   }

25
26   public Object ifFalse(Block aBlock) {
27       return aBlock.execute();       
28   }

29}

30

4 〉3 ifTrue: [Transcript show: 'Hello']就可以对应翻译为:

 

1Boolean condition = new Integer(4).isGreaterThan(new Integer(3));
2condition.ifTrue(new BlockClosure() {
3   public Object execite() {
4      System.out.println("Hello");
5      return null;
6   }

7}
);
8
9

这个看似简单的应用,却带来了两个有深刻影响的性质。

第一,由于if,else等结构不再是预定义的语法了,而与我们自己写的代码一样,属于莫一个类的特定消息,那么也就意味着,我们可以像ifTrue一样,定义自己的分支结构。
比如
   aUser ifNotRegistered: [ do redirect to register page ]
         ifExpired: [ do redirect to active page ]

在不考虑性能优化的前提下,Smalltalk认为和
   4 >3 ifTrue: [do something]
        ifFalse: [do somthing]

是具有一样的语义的。并不因为Boolean属于Kernel就有什么不同。因此控制结构也属于一个可编程的因素,这就是Smalltalk的轻语法特性。

第二,在简单的语法和完全的面向对象语义中,构造与冯诺依曼式语言完全等价的能力(这种能力在语法上表现为赋值,分支,迭代和子程序调用),于是我们可以完全用一致的面向对象的方法来构造软件。

很长一段时间以来,我都认为面向对象方法论是在命令式的冯诺依曼式语言的基础上,通过引入类型系统然后修修补补的得到的,由于冯诺依曼语言式的语言是面向操作层面上的,只是为了更好的刻画操作计算的一个命令的序列,因此冯语言不可避免的具有不完备的语义,混乱的抽象表达以及等等一系列的问题。作为冯语言的一个大补丁的面向对象方法,我也想当然的以为他虽然有了些进步,但是基础问题上面还是不能避免的,加之面向对象缺乏一种一致的构造方法,很多时候我们不得不回归到命令式或者过程式的方法来构造系统,从而破坏掉一种一致清晰的思路,在过程和对象之间不住地权衡(比如Domain Model之争),这个让人非常的不爽。在尝试了一些面向对象语言之后(我是在95年接触C++的时候开始了解面向对象的,而后主要使用Java做为开发语言),我发现这个问题是很难避免,于是我断言这是面向对象技术本身的问题,现在看来不过自己所学有限,没有真正用过纯面向对象语言而已,汗颜得很啊。这里向面向对象方法道个歉,嘿嘿。