当前位置: 首页 > news >正文

技术演进中的开发沉思-45 DELPHI VCL系列:6种方法

用过VC++后,发现再用Delphi就觉得so easy!例如:将 TButton 控件轻轻拖到窗体上,然后双击它,写下一行简单的ShowMessage('Hello')代码。运行程序,那个按钮如同被赋予了生命,轻轻一点,对话框便如灵动的精灵般弹出。在这个看似简单的操作背后,隐藏着两个支撑起整个 Delphi 开发世界的核心要素:对象导向程序语言(OOP)和VCL Framework。对象导向程序语言宛如构建代码世界的神奇语法,而 VCL Framework 则像是一套精心预制好的积木套装,二者相辅相成,今天我们将围绕这两个方面进行梳理。

一、对象导向与 VCL

对象导向(OOP)的核心思想,是将现实世界中的事物和概念巧妙地映射到代码世界中。打个比方,在我们生活的现实空间里,家具是一个大类,其中又细致地分为桌子、椅子等不同的小类。在代码的宇宙中,Delphi 里的 “TComponent”(组件)就如同现实中的 “家具” 这个大类别,而 TButton(按钮)、TEdit(输入框)等则是从属于 TComponent 的具体子类,就如同桌子和椅子从属于家具一样。从本质上讲,现实里的各种家具,尽管用途各异,但追根溯源,大多是由木材等原材料加工而成。在代码世界中也类似,TButton 和 TEdit 等组件,虽然功能不同,但它们都继承自 TComponent,共享着 TComponent 定义的一些基本特征和行为,就像家具们都有从原材料而来的共性。

VCL Framework,即可视化组件库框架,它的出现极大地改变了 Delphi 开发的效率和体验。可以说,它是 Delphi 开发团队精心为开发者打造的一套 “预制家具套装”。以往,若没有这样的框架,开发者想要创建一个按钮或者输入框,就如同要亲自去砍伐树木,再一步步将其加工成家具一样,需要从最基础的代码开始编写,这无疑是一项繁琐且耗时的工作。有了 VCL Framework 后,一切变得简单便捷。开发者可以直接从框架中获取现成的 “组件家具”,例如 TButton 按钮组件。不仅如此,还能轻松地对这些组件进行个性化定制,就像给家具选择不同的颜色、添加抽屉等。在 VCL Framework 里,修改组件的属性,如改变按钮的颜色,就如同给家具上色;为组件添加事件,如按钮的点击事件,就像给家具增加一些特殊功能。这种体验,就如同购买了宜家的家具套装,既节省了从零开始打造的大量时间和精力,又为开发者留下了充分的自主改造空间,让每个开发者都能在这个基础上发挥自己的创意和想法。

二、VCL 框架的 6 种 方法

VCL 之所以能够让开发工作变得如此高效和便捷,很大程度上得益于它运用面向对象程序设计思想精心设计的 6 种独特手法。这些手法并非枯燥难懂的专业术语,而是无数老程序员在长期的实践过程中,经过反复的尝试、失败与总结,最终提炼出来的宝贵 “省力法则”,每一种手法都蕴含着深厚的编程智慧和实践经验。

1. 抽象类别法

想象一下,你热爱烘焙,有一本神奇的食谱,其中有一个 “基础蛋糕模板”。这个模板里只是大致说明了制作蛋糕需要的基本要素,比如需要烤箱、面粉等,但对于具体要做巧克力蛋糕还是草莓蛋糕,并没有给出明确的做法,而是把这个选择权交给了你。在 VCL 的世界里,TComponent 类就如同这个 “基础蛋糕模板”。它规定了作为一个组件所必须具备的一些基本特征和行为,例如组件要有名称,这样在代码中才能方便地识别和调用;组件要能显示在界面上,这是其作为可视化组件的基本功能。然而,TComponent 类并没有具体实现这些功能的细节,而是将具体的实现任务交给了它的子类,比如 TButton 和 TEdit。TButton 类会根据自身的特点,具体实现如何成为一个可点击的按钮,包括按钮的外观绘制、点击事件的处理等;TEdit 类则会实现如何成为一个可输入文本的输入框,包括文本的显示、输入的处理等。这就好比你根据 “基础蛋糕模板”,结合自己的喜好,选择制作巧克力蛋糕或者草莓蛋糕时,需要具体确定巧克力的添加量、烘焙的时间和温度等细节。

// 抽象类(类似模板)typeTMyAbstractComponent = class(TComponent)publicprocedure DoSomething; virtual; abstract; // 只声明,不实现end;// 具体子类(按模板做具体事)typeTMyButton = class(TMyAbstractComponent)publicprocedure DoSomething; override; // 实现“按钮该做的事”end;procedure TMyButton.DoSomething;beginShowMessage('按钮被点击了');end;

在这段代码中,TMyAbstractComponent类定义了一个抽象方法DoSomething,它只声明了方法的存在,但没有给出具体的实现代码,就像 “基础蛋糕模板” 只规定了蛋糕的基本要素,却没有具体的制作方法。而TMyButton类继承自TMyAbstractComponent,并实现了DoSomething方法,这里的实现代码ShowMessage('按钮被点击了');就如同你按照自己的方式制作出了一个具有特定功能(点击弹出提示)的 “按钮蛋糕”。

2. Place Holder 方法

回想早年我用 Delphi 开发一个登录界面的经历。在设计这个界面的过程中,我深知用户登录成功后需要进行一系列的后续操作,比如跳转到主页。但在当时,我并不知道具体的跳转逻辑会是怎样,因为这可能会根据项目的具体需求而发生变化。于是,我在 TForm 类中预留了一个OnLoginSuccess方法。这个方法在当时就像是墙上一个空空的插座,虽然它本身没有任何实际的功能代码,但却为未来可能需要插入的新功能预留了接口。当项目的需求明确后,比如确定用户登录成功后要跳转到一个特定的主页地址,我只需要将实现跳转功能的代码 “插” 入到这个OnLoginSuccess方法中即可。这就是 VCL 的 Place Holder 方法的实际应用。

在 VCL 框架中,父类的一些虚拟方法被设计成空白的函数,而不是声明为抽象方法。以 TCanvas 类的虚拟方法 CreateHandle 为例,它采用了 Place Holder 设计。这样做有诸多好处,一方面避免了抽象类的一些缺点,比如抽象类要求子类必须实现所有抽象方法,这在某些情况下可能会限制代码的灵活性;另一方面,如果父类在后续的开发过程中,觉得需要为这个方法加入具体的实现,那么可以直接在这个空白的 Place Holder 方法中添加实现代码,而且由于这个方法原本是空白的,不会增加执行码的大小。当子类面对父类中这种 Place Holder 方法时,开发者拥有很大的自主性。如果子类有自己特定的需求,可以选择直接在子类中实现该方法的功能代码;如果子类希望在执行自身功能的同时,也执行父类后来添加到 Place Holder 方法中的新功能,那么可以通过使用inherited关键字来调用父类的新实现,然后再执行子类自己的代码。这种设计就像在一个可扩展的电路系统中,预留了许多插座,你可以根据实际需求随时插入不同功能的设备,而且不会影响整个系统的基本架构和性能。

3. 逐渐增加法

VCL 里的 TForm(窗体)的形成过程,就如同孩子们搭积木一样,是一个从简单到复杂、逐步累加的过程。想象一下,最开始有一块最基础的积木,它代表着 TObject 类,TObject 类是 Delphi 中所有对象的根基,它包含了一些最基本的属性和方法,就像这块基础积木具有一些通用的形状和特征,是构建更复杂结构的起点。接着,在 TObject 的基础上,增加了一些功能,比如具备了显示的能力,就像给这块积木添加了一些可以用来连接其他积木的特殊结构,从而形成了 TControl 类。TControl 类能够处理一些与显示相关的操作,比如接收和处理窗口消息,进行基本的绘制工作等。最后,再给 TControl 类添加一些特定的窗口属性,例如标题栏、边框样式等,就像给已经有一定结构的积木组合再添加一些装饰性和功能性的部件,使其变成了一个完整的、具有特定功能的 TForm 类,也就是我们在应用程序中看到的窗体。这种 “从小到大” 的设计方式,使得每个类都专注于自己的功能,不会变得臃肿不堪。当程序出现问题时,排查起来也相对容易,因为可以按照类的继承层次,逐步定位到可能出现问题的地方,就像检查一个搭好的积木塔,从底层开始,一层一层地查找松动或者错误搭建的积木。

4. 三明治手法

以我们日常生活中常见的三明治为例,三明治有两片面包,中间夹着生菜等各种食材。在 VCL 里,“事件” 的设计就如同三明治的结构。以 TButton 的Click事件来说,按钮本身的基本功能和一些底层逻辑,比如按钮知道自己被点击时要触发某个事件,这就好比三明治的两片面包,它们是固定的部分,为整个结构提供了基础的支撑和框架。而当按钮被点击后具体要执行什么操作,比如弹出一个对话框显示提示信息,或者将用户输入的数据保存到数据库中,这些具体的操作就如同三明治中间夹的生菜等食材,是可变的部分,由开发者根据具体的业务需求来定制。VCL 框架通过这种设计,很好地保护了底层的逻辑不被随意修改,同时又为开发者提供了足够的空间来定制中间可变的部分,也就是事件的具体处理逻辑。开发者只需要关注如何填写 “生菜” 这部分内容,而不用担心影响到 “面包” 所代表的底层逻辑,就像我们在制作三明治时,只需要专注于选择和添加自己喜欢的食材,而不需要去改变面包的基本制作方法。

5. 覆盖父代实作法

曾经我接手过一个老项目,在这个项目中,原有的 TButton 按钮在被点击时,总是弹出英文提示信息。然而,由于项目的目标用户大多是国内用户,需要将提示信息改为中文。在 Delphi 中,我只需要写下procedure TMyButton.Click; override;这样的代码,然后在这个方法内部,将弹出英文提示的代码替换为弹出中文提示的代码即可。这一过程就如同我们有一份祖传的红烧肉菜谱,爷爷传给父亲,父亲再传给我们。这份菜谱(父类方法)有它传统的做法,比如规定了要用什么调料、大致的烹饪步骤等。但是随着时代的发展和个人口味的变化,我们可以在这份祖传菜谱的基础上,按照自己的喜好对某些步骤进行调整,比如改变调料的用量、调整烹饪的火候等(子类重写)。这样既保留了祖传菜谱的核心基础,又加入了新的变化和个人特色。在 VCL 中,通过覆盖父代实作的方法,子类可以根据自身的需求,对从父类继承来的方法进行重新定义,从而实现特定的功能,同时又不会影响父类在其他地方的正常使用,就像我们对红烧肉菜谱的修改,不会影响这份菜谱在家族其他成员按照传统方式制作时的效果。

6. BootStrap 设计法

还记得第一次使用 Delphi 时,当我双击运行一个 Delphi 程序时,看到窗体如同变魔术般自动弹了出来,当时的我对这个过程充满了疑惑,它是如何做到的呢?后来深入学习才明白,这背后是 VCL 精心设计的一套 “启动逻辑”。可以把这个过程类比为我们每天早上起床的过程。首先,我们要先从床上坐起来,这就相当于 VCL 框架先加载核心组件,这些核心组件是整个框架运行的基础,如同我们身体的核心部分,为后续的动作提供支撑。接着,我们要穿衣服,这类似于 VCL 框架对窗体进行初始化的过程,为窗体设置各种初始属性,比如大小、位置、背景颜色等,就像我们穿上合适的衣服来展现自己的形象。最后,我们下床开始一天的活动,这对应着 VCL 框架将初始化好的窗体显示出来,呈现在用户面前,让用户可以与之进行交互。VCL 的这种 BootStrap 设计法,使得开发者在开发应用程序时,不需要手动编写复杂的启动代码,只需要专注于实现业务逻辑等核心功能即可,大大提高了开发效率,就像我们每天早上起床时,不需要思考如何让自己的身体从睡眠状态顺利过渡到活动状态,身体会自动按照一套既定的生理机制完成这个过程,我们只需要专注于当天要做的事情。

最后小结

回想起当年使用 VCL 进行开发的日子,那时候只觉得通过拖拖拽拽组件,再简单地编写一些代码,就能快速构建出一个应用程序,这种开发方式实在是太方便了,仿佛打开了一扇通往高效开发的大门。随着职业生涯的发展,在接触到了各种新兴的技术和架构理念。在这个过程中,我越发深刻地理解了当年 VCL 那些设计手法的精妙之处。

抽象类别法教会了我一种重要的思维方式,即在做任何事情之前,先清晰地定义边界和规则,这样可以为后续的工作提供一个明确的框架,避免盲目地开始,从而提高工作的效率和质量。就像在规划一个大型项目时,先明确各个模块的功能和接口,各个团队成员就可以在这个框架内独立开展工作,最后再将各个模块有机地整合在一起。

三明治手法让我明白了在设计系统时,要巧妙地将固定不变的部分和可能会经常变化的部分区分开来。这样,当需求发生变化时,只需要修改可变的部分,而不会对整个系统的稳定性造成影响。这在应对复杂多变的业务需求时,显得尤为重要。

从桌面开发到如今的移动互联网时代,技术的浪潮一波接着一波,不断地推陈出新。VCL 里的组件,既不是完全固化的、无法改变的 “成品”,让开发者没有任何发挥的空间;也不是需要开发者从零开始、白手起家打造的 “原材料”,给开发者带来巨大的工作量和技术挑战。它恰到好处地预留了一定的空间,就像一个精心设计的舞台,每个开发者都可以在这个舞台上尽情地施展自己的才华,留下属于自己的独特痕迹。这或许就是老技术所具有的独特生命力,它不仅仅是一堆冰冷的代码,更是一代代程序员在不断探索和实践中,对 “如何高效创造” 这一永恒命题的深刻思考和智慧结晶。未完待续........

http://www.dtcms.com/a/297183.html

相关文章:

  • 关于新学C++编程Visual Studio 2022开始,使用Cmake工具构建Opencv和SDK在VS里编译项目开发简介笔记
  • RocketMQ常见问题梳理
  • 三、Spark 运行环境部署:全面掌握四种核心模式
  • 【内网穿透】使用FRP实现内网与公网Linux/Ubuntu服务器穿透项目部署多项目穿透方案
  • vue使用xlsx库导出excel
  • 编程语言Java——核心技术篇(三)异常处理详解
  • 字符串 “asdasjkfkasgfgshaahsfaf” 经过哈夫曼编码之后存储比特数是多少?
  • [实战] 用1 PPS 驯服本地恒温晶振(OCXO/TCXO)
  • 医疗AI跨机构建模实施总结:基于 Flower 联邦学习与差分隐私的实践指南
  • ESP32学习笔记_Components(1)——使用LED Strip组件点亮LED灯带
  • 迷宫生成与寻路可视化
  • 广州 VR 安全用电技术:工作原理、特性及优势探析​
  • 天通卫星赋能三防智能平板:AORO P1100打造全域通信新范式
  • 【数据结构与算法】数据结构初阶:详解二叉树(六)——二叉树应用:二叉树选择题
  • 【数据库】探索DBeaver:一款强大的免费开源数据库管理工具
  • 医疗数据挖掘Python机器学习案例
  • PAT 甲级题目讲解:1008《Elevator》
  • Agent领域,近年来的前沿研究方向:多智能体协作、认知启发架构、伦理安全、边缘计算集成
  • Modbus RTU转Profinet网关与涡街液体流量计:工业自动化数据传输的完美协同
  • 【橘子分布式】gRPC(番外篇-拦截器)
  • 关闭chrome自带的跨域限制,简化本地开发
  • XORIndex:朝鲜不断发展的供应链恶意软件再次瞄准 npm 生态系统
  • 《基于电阻抗断层扫描(EIT)驱动的肌肉骨骼模型表征人体手臂动态意图用于人机交互》论文解读
  • Linux: network: wireshark: esp attempt to detec null-encrypted esp payloads
  • chrome使用Modheader插件让浏览器直接预览图片,不下载
  • 算法思维进阶 力扣 62.不同路径 暴力搜索 记忆化搜索 DFS 动态规划 C++详细算法解析 每日一题
  • kafka如何保证数据不丢失
  • 机器学习中knn的详细知识点
  • Linux725 磁盘阵列RAID0 RAID1
  • OneCode3.0 Gallery 组件前后端映射机制:从注解配置到前端渲染的完整链路