如何在 Excel VBA 中使用类接口
- 2026-11-11 02:57:03

📦 5 PARTS · 精选 3 处
👉 滑动
PART 01
不用接口先跑通
声明 + If 判断
PART 03
接口登场
Implements 约定
PART 05
完整代码
五个模块全览
今天我们来学习一下如何在 Excel VBA 中结合类模块来使用接口。接口在很多编程语言里都有应用,它们到底是什么,是我们编写面向对象代码过程中的哪一部分呢?因此我们有必要来学习在 Excel VBA 里如何实现这一点,为了完完全全搞懂接口相关的内容,我们搭配了代码示例。
01
PART
不用接口,先跑通一版
THE PLAIN WAY · 从零写起
首先我们要搞明白一个核心问题:我们为什么要使用接口?想要理解这个问题,最好的办法就是先看一个不使用接口的示例,之后我们再改用接口重写,对比看看两者的差别。
在下面的这个示例当中,我们的数据非常简单,就只有金额和利息类型两项内容,我们需要根据利息类型来计算对应的利息。如果是 A 类型,就按照一种规则计算利息;如果是 B 类型,就按照另一种规则计算利息。

把数据区域读进来
接下来我们来看代码该怎么写。第一步,我们先新建一个标准模块,在模块里写一个简单的主过程,名字就叫main。我们首先读取表格里的数据区域,读取区域的写法是固定的:先声明一个区域变量,再把这个变量绑定到工作表上,这里shData是工作表的代码名称,我们可以在左侧工程窗口看到它。
我们指定从 A1 单元格开始,再用CurrentRegion属性读取这块区域所有相连的数据。拿到数据区域之后,我们就开始读取里面的数据。
我们先写Dim i As Long,接着写循环For i = 2,之所以从第 2 行开始,是因为我们跳过表头行,循环范围是从第 2 行到这块区域的最后一行,最后用Next i结束循环。
我们的目标是读取每一行的金额和利息类型,所以先声明对应的变量:Dim amount As Double,还有interestType As String。读取数据的写法很简单,amount等于区域里Cells对应的单元格,行号就是循环变量 i,列号写 1,代表第一列,也就是金额列;读取利息类型的写法差不多,只是改成读取第二列。这样我们就把每一行的金额和利息类型都读出来了,拿到这两个值之后,我们就可以根据利息类型,选择对应的计算方式算出利息。

写两个计算利息的类
接下来我们新建两个用来计算利息的类模块,依次插入两个类模块,给它们重命名,前缀cls代表类,第一个叫clsInterestA,第二个叫clsInterestB。两个类里面都要写一个Calculate函数,函数接收一个 Double 类型的金额参数,最后返回一个 Double 类型的计算结果。
为了示例足够简单,我们只写很简易的计算逻辑,clsInterestA 里是金额乘以 1.1,clsInterestB 里是金额乘以 1.5。实际项目里的计算逻辑会复杂很多,这也正是我们拆分不同类来处理的原因,这里只是用极简的例子演示思路。写完之后我们执行一次调试编译,确认代码没有报错,再回到标准模块。

在主过程里判断并调用
我们先声明两个对象变量:Dim oInterestA As clsInterestA,还有oInterestB As clsInterestB。之后根据读到的利息类型来创建对应的对象,这里用If 判断:如果interestType等于 A,就使用Set语句新建一个 clsInterestA 对象,然后调用它的 Calculate 方法;如果是 B 类型,就用ElseIf分支,新建 clsInterestB 对象并调用 Calculate方法。
我们还要增加对无效类型的判断,这种情况很容易出现,要避免代码继续错误执行,在完整项目里这里需要完善的错误处理,现在先用消息框提示“无效的利息类型”,同时输出这个异常的类型,方便我们定位问题。
我们还要定义一个 result 变量接收计算结果,result 接收函数返回值的时候,因为前面写了等号接收返回结果,调用函数时必须带上括号,如果只是单独调用过程或者函数,就不需要括号。拿到结果之后我们把结果打印出来,再次执行调试编译,然后运行代码,就能看到所有计算出来的值。

02
PART
四个绕不开的麻烦
THE PROBLEMS · If 开始散落
这段代码虽然可以正常运行,但存在不少问题,接下来我们就梳理这些问题,再用接口来解决它们。
01每一种类型,都要一个变量
第一个问题:我们需要为每一种利息类型单独声明对象变量,这个例子里只有 A、B 两种,所以是 oInterestA 和 oInterestB,假设后续扩充到十种类型,就要一次性声明十种不同类型的对象变量,而且运行时不一定全部都会用到,写法会很杂乱。
02加一个方法,两个类都得改
第二个问题,我们再设想一个场景,给类里面新增一个过程,比如PrintResult。我们修改这两个类,新增一个私有成员变量mAmount,类型是 Double,调整 Calculate 不再直接返回数值,而是把计算出来的结果存入这个私有成员变量,这也是开发里很常用的写法;然后新增 PrintResult 过程,在里面用Debug.Print输出内容,我们这里用TypeName获取当前对象所属类的名称,拼接上计算好的金额,打印信息会更加清晰。

我们把 clsInterestA 和 clsInterestB 都改成这个结构,修改完成后执行调试编译,这里会提示 Calculate 相关报错,是因为我们不再用它返回值了,把原来接收返回值的 result 相关代码删掉就可以,clsInterestB 也做一模一样的修改,再次调试编译确认无误。

回到主过程,我们调用对象的 PrintResult 来打印结果,清空立即窗口后运行代码,打印出来的结果和之前基本一致。

03换一处调用,就多一组 If
如果我们在计算完成之后,想再调用 PrintResult 打印结果,就必须再写一组 If 判断语句,只能调用对应类型对象的 PrintResult。这个示例里类型很少,看起来不算麻烦,可如果我们要新增很多不同的过程,同时类型扩充到十个、二十个,代码里就会到处散落 If 判断,整体会变得非常混乱。
04要动态指定规则,只能继续堆 If
还有最后一个问题,我们设想新增一个 Account 类,每一个 Account 实例都会持有一个利息对象,类型可以是 A 或者 B,我们希望在程序运行的时候,动态指定它使用哪一种利息计算规则。但受限于现在的写法,我们只能分别定义 oInterestA、oInterestB 这类独立变量,再搭配 If 判断去赋值,类型一多,代码同样会变得臃肿杂乱。
而接口就可以一次性解决上面所有这些麻烦,我们借助 VBA 里的Implements关键字来使用接口。接下来我们修改代码,接入接口方案。
03
PART
接口登场:先有一份约定
THE INTERFACE · Implements
只写声明的接口类
第一步,新建一个类模块当作接口,我把它命名为iInterest,看到 i 开头就知道这是接口类。在这个接口类里面,我们只写过程或者函数的声明原型,不在里面写任何业务代码,它的作用相当于一份约定:任何实现这个接口的类,都必须包含接口里定义好的这些过程、函数或者属性。

让类用 Implements 去履约
接下来我们打开 clsInterestA,在代码最上方写Implements iInterest,写完执行调试编译,编译器会提示报错,提示对象模型需要为接口 iInterest 实现 Calculate。这个报错信息很明确,意思就是这个类实现了 iInterest 接口,就必须按照约定实现 Calculate。我们已经写好了 Calculate 过程,但是格式需要调整,过程名称前面要加上iInterest_前缀,改成iInterest_calculate。clsInterestB 也要做同样的操作。
✦ 提示
报错不是语法写错了,而是接口在提醒你:写了 Implements,就必须兑现约定。

主过程只留一个变量
接口的价值就在于这份统一约定,方便我们后续调用。我们回到主过程,原来需要分别声明多个不同类型对象变量的写法可以舍弃,现在只需要声明一个 iInterest 类型的变量就足够了,Set 语句可以把这个变量赋值成任意一个实现了 iInterest 接口的类实例。这就是接口带来的强大能力。我们调整 If 判断内部的代码,不再在分支里调用 Calculate,只负责创建对应的对象实例、赋值给这个 iInterest 变量,把 Calculate 调用挪到 If 判断的外面,If 分支的代码一下子清爽很多。

调用时 VBA 自己认类型
调试编译之后我们继续往下看,最亮眼的地方来了:之后我们想要调用对象的方法,不需要再写 If 去判断它到底是 A 类型还是 B 类型,VBA 会自动识别这个对象实际的类型,直接写oInterest.PrintResult就可以,VBA 会自动路由到对应类里面的 PrintResult 过程。清空立即窗口,调试编译之后运行代码,结果和之前完全一致。

把创建对象抽成类工厂
我们还可以继续优化,把创建对象的逻辑单独抽成一个过程,这种专门用来生成对象的函数一般叫做类工厂。这个函数接收一个字符串类型的利息类型参数,返回值类型是iInterest。在函数内部声明 iInterest 类型的变量,根据传入的类型参数创建对应的对象,最后用 Set 把这个对象作为函数返回值返回。调整完成后,主过程里只需要一行 Set 语句,调用这个类工厂函数,把利息类型传进去,拿到对象实例。调试编译运行,确认所有功能正常,代码变得更加干净。
04
PART
加一种类型,验证扩展性
SCALE UP · 只加一个类
接下来我们来测试新增类型的效果,我们新增一行测试数据,金额 5000,利息类型是 C。

我们新建 clsInterestC 类模块,复制之前类里的基础结构,修改计算逻辑,规则是金额加上 1000。这个类同样要写上Implements iInterest,并且把 Calculate 改成iInterest_Calculate,调试编译确认无错。回到类工厂函数,在里面新增一条判断,如果传入类型等于 C,就新建 clsInterestC 对象。清空旧的测试数据,调试编译之后运行代码,可以看到C 类型的数据也能正确计算利息。

到这里我们就能体会接口真正的优势了:当我们有多种行为规则、后续还可能持续新增规则的时候,接口可以让我们轻松扩展类,不用在代码里到处堆砌 If 判断,把具体的业务逻辑交给各个类自身去处理。
一句话总结这一节
新增规则只加类,老代码一行不动
05
PART
完整代码,一共五个模块
FULL CODE · 可直接抄
示例中完整代码如下:
模块 1:main 与类工厂
OptionExplicit
Sub main()
Dim rg AsRange
Set rg = shData.Range("A1").CurrentRegion
Dim oInterest As iInterest
Dim amount AsDouble, interestType AsString
Dim i AsLong, result AsDouble
'读取数据
For i = 2To rg.Rows.Count
amount = rg.Cells(i, 1).Value
interestType = rg.Cells(i, 2).Value
Set oInterest = ClassFactory(interestType)
oInterest.Calculate amount
oInterest.PrintResult
Next i
EndSub
Function ClassFactory(ByVal interestType AsString) As iInterest
Dim oInterest As iInterest
If interestType = "A"Then
Set oInterest = New clsInterestA
ElseIf interestType = "B"Then
Set oInterest = New clsInterestB
ElseIf interestType = "C"Then
Set oInterest = New clsInterestC
Else
MsgBox"无效的利息类型" & interestType
EndIf
Set ClassFactory = oInterest
EndFunction
类模块 iInterest:只写声明
OptionExplicit
Sub Calculate(ByVal amount AsDouble)
EndSub
Sub PrintResult()
EndSub
类模块 clsInterestA:金额 × 1.1
OptionExplicit
Implements iInterest
Private m_Amount AsDouble
Sub iInterest_Calculate(ByVal amount AsDouble)
m_Amount = amount * 1.1
EndSub
Sub iInterest_PrintResult()
Debug.PrintTypeName(Me) & ": " & m_Amount
EndSub
类模块 clsInterestB:金额 × 1.5
OptionExplicit
Implements iInterest
Private m_Bmount AsDouble
Sub iInterest_Calculate(ByVal amount AsDouble)
m_Bmount = amount * 1.5
EndSub
Sub iInterest_PrintResult()
Debug.PrintTypeName(Me) & ": " & m_Bmount
EndSub
类模块 clsInterestC:金额 + 1000
OptionExplicit
Implements iInterest
Private m_Amount AsDouble
Sub iInterest_Calculate(ByVal amount AsDouble)
m_Amount = amount + 1000
EndSub
Sub iInterest_PrintResult()
Debug.PrintTypeName(Me) & ": " & m_Amount
EndSub
✦ 改动只有两处
以后再加一种利息规则,改动只为两处:新建一个实现 iInterest 的类模块,在类工厂里补一条分支。主过程的循环一行都不用动。
///
LAST
写在最后
THANKS · 下篇见
今天的分享就到这儿啦,非常感谢您对“Python SQL审天下”公众号的关注和点赞。
如果您觉得我的公众号能给您带来一丝丝的收获,请多多转发给您的朋友圈,让更多的人看到并了解。
也许您不经意间的点赞和转发,会给他人带来独特的体验和感受。