VBA 类模块系列之三——为什么要使用类

本篇我只想说一件事实:在国外的Access VBA开发者社区,会使用类是一件很酷的事情。大家去国外论坛验证一下 … 继续阅读“VBA 类模块系列之三——为什么要使用类”

本篇我只想说一件事实:在国外的Access VBA开发者社区,会使用类是一件很酷的事情。大家去国外论坛验证一下就知道了。接下来的内容,吹牛逼的成分比较多,但是作为一个严肃的系列文章,该吹的牛逼还是要吹的:)。大家要是忙,可以直接跳到下一篇。

可以说,你在Access中的绝大多数编程任务都可以用标准模块来完成。那么你为什么要使用类模块呢?这个问题可以从以下几个角度来看。

数据和行为的封装

VBA的类模块能带给你的一个最大的好处,就是你可以将一些相关的数据和行为封装在这个类模块中。

我记得好几年前,我还在市场部做数据分析预测的时候,经常要查将来5年还是10年每年节假日的具体日期。元旦,五一,国庆这3个假期,最简单了,每年都是固定的日期。清明和中秋就比较麻烦了。中秋是按农历,每年都不一样,清明是按节气,虽然每年都差不多是4月4日或者5日,但是你拿不准到底是哪一日。为了一劳永逸的解决这个问题,我在网上找到了一个Excel VBA 类模块,里面有封装了天干,地支,节气,星座等等你能想得到的数据,还有很多函数,公有的函数提供对外的调用,私有的函数供类内部计算使用。有了这个类,你能将任何将来的日期转化为对应的农历日期。喜欢刨根究底的我,特意去研究了类中的代码,但是还是看不懂,不是代码看不懂,而是对农历历法一无所知,所以不知道代码为什么要这么写。我进一步去研究农历历法,结果我找到了一片国外的论文,看着从中文翻译成英文,又在我脑中转成中文,那种感觉特别别扭。从外国人对中国的农历历法的研究中,我得出一个结论:从地球绕太阳公转的角度来看,农历比公历更加准确。呃,这牛逼吹到民族自豪感上去了:)

一个类中,既有数据,又有方法,它就能实现一定的特定功能。难道这个就非得用类模块来实现吗?标准模块不行吗?这是个很好的问题,我们来看看如果是标准模块,怎么来实现。标准模块中,你可以声明一些私有的模块级常量和数组变量,将这些天干,地支,节气数据放在其中,然后私有和共有的函数或方法保持与类模块中的一致。传入参数调用某个公有函数,第一次调用的时候,模块及的变量当然是没有初始化的,需要检查一下是否初始化,若没有就初始化一下,然后执行公有函数的代码,也能得到一样的结果。你唯一不能做的,就是同时创建一个类的多个实例。标准模块不存在实例的说法,它一旦被调用,永远只在内存中保留一份私有模块级变量数据。所以你无法同时创建2个日期对象数据。一旦你遇到需要同时保留一份以上的日期数据的时候,你能做的,就是再创建一个一模一样的标准模块。这就回到了该系列第一篇里面谈到的,在多处维护相同代码的窘境。

将复杂的实现逻辑隐藏起来

这个好处是显而易见的。我并不需要事先知道农历历法是怎样计算的,就能将一个公历日期转换为农历日期。我只需要知道类的接口,知道如何调用它就好了。这种示例在Access中还有很多,比如ADODB库中的数据集Recordset类,天知道它是怎么将表中的数据读取到内存,还有一个游标说它目前指向的那一行数据,你执行一下MoveNext方法,它就指向到了下一行。我只需要知道这样操作,就能得到相应的结果,至于它背后是如何实现的,你可以心安理得的保持无知状态。因为这不是你需要操心的事情。正式因为类有这种独特的隔离特性,使得程序员与程序员之间的分工协作变得容易。一个大型的项目就是由很多程序员合作编出来的。在这里你也能感受到接口的重要性。还是那句话,一旦发布了的接口,就不能去修改了,但是可以添加新的接口。

让程序员之间的分工写作变得容易

上面已经谈到了,这里谈谈自己的经验。我使用Access一般都是做一些部门级的系统,说大不大,说小也不小,都是一个人开发,还未有与他人合作开发的情况。所以不存在分工的问题,尽管如此,我还是习惯将用户界面与数据读写相分离,中间用类来做2层之间的交互,同时还有一些其他的辅助类,实现一些特定的商业逻辑。当然你可以说这已经脱离了Access的快速开发的初衷,谁说不是呢?Access初学者习惯使用绑定窗体,但你知道的,用户的需求是很刁钻的,我举个例子,绑定窗体如何能保存数据更改的历史记录?在多用户环境中,你还需要记录下是谁在什么时间做的更改?是将哪个旧数据改为哪个新数据的?当然你可以在绑定窗体中,对每个绑定的控件的change事件编写代码,来实现,但万一修改又被撤销了呢?将业务逻辑代码散落在每个窗体里面,也不值得提倡。窗体最理想的状态,是只做用户交互。

这些个人风格上的事情,仁者见仁智者见智了。牛逼吹完了,欢迎拍砖:)

 

VBA 类模块系列之二——什么是类

我记得小时候包饺子,包得不好看,但是有一个包饺子的工具,塑料的,展开就是一个圆型,中间凹下去,边缘有很多褶子, … 继续阅读“VBA 类模块系列之二——什么是类”

我记得小时候包饺子,包得不好看,但是有一个包饺子的工具,塑料的,展开就是一个圆型,中间凹下去,边缘有很多褶子,把饺子皮放在上面,中间放坨肉馅,然后把这个工具折叠起来,用力一压,一个漂亮的饺子就包好了。用这个工具包出来的饺子,漂亮极了,大小一样,每个褶子也都一样。

这跟编程有一毛钱关系吗?呃,有的,这个工具就是,用这个工具包出来的饺子就是这个类的对象,或者称之为类的实例

在Access VBA开发中,你其实一直在使用类。比如你新创建了一个窗体,从窗体设计功能区控件组中拖了一个文本框控件到这个窗体上,你就是创建了这个文本框类的一个实例,然后你调整了一下这个文本框的大小和位置,实际上你是在调整这个文本框实例对象的 Left,  Top, Width, Height 属性。当你的程序发布以后,用户在这个文本框上输入一些信息,就会触发这个文本框实例对象的 Change 事件,如果你为这个事件写了代码,那这个代码就会响应这个事件,自动执行。如果这个文本框绑定到了某个数据源,在一个多用户的环境中,你想知道这个数据源最新的数据,你会调用这个文本框的Reqeury方法。你创建的这个窗体,其实也是一个类,当你加载这个窗体的时候,实际上你是在实例化这个窗体类。

用面向对象编程(OOP)的术语,为基于它创建的所有对象定义了属性(Properties)方法(methods)事件(Events)。这些属性,方法和事件构成了这个类的接口(Interface)

我是苹果的粉丝,不过还没达到脑残的级别:) 买过3部苹果手机:iPhone1,iPhone5,iPhone7。iPhone1的数据线接口30pin,非常宽,还区分正反面,晚上要充电的话,50%的几率要插2次,第一次插不进去,就知道插反了。等到iPhone5出来的时候,数据线换成了Lighting接口,方便多了,不区分正反,随便插。以前买的iPhone1的数据线就不能用到iPhone5上了。iPhone7与iPhone5的数据线相同,以前多买的几根iPhone5数据线还能继续用。据说苹果以后打算使用Type-C接口,与其他手机厂商保持一致。这个手机数据线接口变来变去对消费者不是一件好事。我一般都是家里,办公室,车上各一根,接口变一次,我就得多花钱买2根。家里其他人如果不是用的苹果,这数据线还无法共用。

类的接口其实与手机数据线的接口有很多相通的地方。一旦你设计好了某个类的接口,不要去变他。为什么不能变呢?要多花钱买2根?开玩笑😝,这可不是多花钱的事。一个设计好了的类,肯定是要投入使用的,在你的项目的某个地方,肯定会用到这个类,写了一些代码,实例化这个类,然后修改了这个类对象的属性,调用了这个类对象的方法,可能还写了一些响应这个类对象的事件代码。好了,你通过这个类完成了这个项目的一些功能。不久以后,你越看这个类越不顺眼,水平提高了嘛,你把这个类的属性,方法,事件全部删掉了,重新设计出一个你更加满意的类。当你洋洋得意的完工后,编译了一下你的项目,“咚”!编辑器跳出提示:编译错误,方法和数据成员未找到。傻眼了吧?

问题出在哪里?原来你修改了这个类以后,凡是项目使用这个类的地方的代码没有同步修改过来!你于是要将所有使用这个类的地方的代码全部修改一遍。这就好比iPhone5的数据线接口换成Lighting了,你还是使用的30pin的iphone1接口的数据线给它充电一样。

你会问,那要怎么弄?某个类你确实看它不顺眼了,还不能修改了?iPhone5当然可以使用更先进的Lighting接口,但是,请同时保留30pin的旧接口!一台iPhone弄2个充电接口,这手机也太奇怪了,所以Lighting接口刚出来的时候,有一个接口转换器,一头可以插30pin接口数据线,另一头是Lighting接口,问题就解决了。

回到编程的角度,你可以修改类,但是类的旧有接口一个都不要变,你只需要给它添加新的接口就行了。这样就确保了项目旧有的代码可以正确编译执行,同时你又为类添加了新的属性方法和事件。

VBA 类模块系列之一——前言

Access开发框架系列文章后续系列,因为各种原因,很久没更新了,这个坑还没填完,我准备给自己又挖一个新坑:P … 继续阅读“VBA 类模块系列之一——前言”

Access开发框架系列文章后续系列,因为各种原因,很久没更新了,这个坑还没填完,我准备给自己又挖一个新坑:P 写一个系列文章,讲讲类模块。有兴趣的读者欢迎留言给予支持和鼓励。

本系列文章适合哪些读者?

作为一个有一定经验Access VBA开发人员,你应该已经掌握了一些VBA的基础知识。比如知道如何创建窗体,在窗体上添加控件,如何给窗体事件编程,比如给Form_Load()事件写点代码,初始化一些窗体控件。对于一些比较常用的代码,比如根据生日计算年龄的自定义函数,知道将它放在标准模块中,这样,任何窗体都能调用这个自定义函数,你不必在每个窗体中都将这个函数写一遍。什么?复制粘贴不算写?呃,那你得在多个地方维护相同的代码,如果哪天想把年龄弄得更精确一些,让它从原来的整数变为带2位小数的情况,那么你就得在多处做相同的修改。如果你将它放在标准模块中,而在任何需要使用到这个函数的窗体里面,调用它,好处很明显啊:你只用做一处修改就完了。

偷懒是编程人员进步的原动力。为了偷一点点懒,你需要掌握更复杂一点的编程思维,偷懒是需要付出代价的^_^。不过一旦你习惯了更复杂一点的编程思维,你会获得更多的自由,解决以前无法解决的问题。什么?你说你很勤奋?呃,你确定你不是用行动上的勤奋掩盖思维上的懒惰:)?