由于每次都可能有人是单独阅读这个系列的文章。我想重申一下我的苛评VCL的意义。不在于说VCL的不好,而是重点将发现到一些问题暴露出来,以此来说明我认为VCL原来处理不合适的地方。这永远是一个设计问题,而不是技巧问题。中间难免有争议。不过请大家将争议的目标锁定在设计本身。毕竟,针对特定情况的争议才是有意义的。
说起Application,稍微知道Delphi的人都知道。我们最常使用的Application的几个方法是:
这些方法带领我们逐渐去认识Application。事实上,Delphi中并不止一个Application。我们平常见到最多的是Forms单元中定义的TApplication。如果你创建一个控制面板应用的话,会发现在CtlPanel单元中定义了一个TAppletApplication。但是它在dpr中的使用方法和Application一样。同样,如果你创建一个服务应用的话,会在SvcMgr单元中,发现另一个Application,叫TServiceApplication。这些Application都是从TComponent派生出来的,但是之间并没有任何关系,也没有公共的接口。但是都同样有三个办法:Initialize,CreateForm,Run。此乃Application的第一孤独,虽有众多兄弟,但是却只能有一个人出现。
今天我们重点说Forms单元中的TApplication。在李维先生的《Inside VCL》中对这个类的功能做了非常详细的解释。其主要作用是创建了一个隐藏的窗体,来统一处理消息循环,以此简化TForm的设计。另外你如果了解以上几个另类的Application,就会发现,你可以非常简单的将自己的Form移植到其他类型的应用中,而不需要修改太多的代码。
可是,我们在说的时候,前提都是Application在一个Exe程序中的。如果我们将它放到Dll中呢?因为Application是定义在Forms单元中,可以想像,如果我们在Dll中使用了窗体。Exe和Dll之间将会出现两个Application,而且这两个Application是不一样的。最典型的现象就是如果在Dll中直接ShowModal一个窗体,它和MainForm不会共享一个任务栏Button。解决过这个问题的人都知道,这个原因在于Handle不一样。
但这不是在讨论如何解决不一样的技巧,事实上我们在实际编程中,都会采用很多方式来规避这个问题。可是,如果我们将这个问题提升到设计领域,那么几乎可以认为,Application并没有处理好Exe和Dll之间的共享问题。
有一个很奇怪的现象,不知道大家注意到了没有。Application的类型是声明在Forms单元中的,但是创建代码却是在Controls单元中调用的。通过看创建代码,可以看出,Application中模式上讲,应该接近于单例模式。而且是直接创建的。只要你使用Controls单元,Application对象就会被创建起来。这个直接决定了Application的第二孤独,天生没有同伴。除了Application,还有几个相同遭遇的难兄难弟:Screen/Mouse。只要有窗体,Dll中的Application不可避免地被创建出来,而且是不可替换的。
我认为这个应该是设计上的遗漏。当然了,最好的情况,是约定在Dll中不要使用窗体了!不过这个约定并不是每个人都知道,而且Delphi也没有禁止在Dll中使用窗体。而且从实现上讲,FastMM4,这个和Application同样是类单例模式的内存管理器,就非常好地实现了Exe和Dll之间的共享。
Application是强大的,强大到你完全不需要关心它。我想这个正是VCL设计它的初衷。可是,当这位英雄遇到一堆英雄的时候,却发现,他们之间是互相独立的。协同不好的英雄除了发挥原有的作用外,也付出了不协调的代价。这正是Application的第三孤独,大家明明就是一个人,却总是不能合体。
从我们实践的角度,其实就是几个关键值的共享,因此从架构上无需多说什么。但是既然是设计,就必须比应用多考虑到一点,