VBA遗传

VBA遗传,vba,inheritance,Vba,Inheritance,我听说很多VBA缺乏继承性。我做了一些变通,现在在我看来,这正是继承的作用。我离pro=)还很远,可能遗漏了什么。所以我非常感谢你对可能的不利因素的想法 当我发现您仍然可以在接口类(而不仅仅是签名)中完全实现函数时,我感到非常惊讶,这让我想到了下面的内容。我看到一些人在构图的帮助下做了类似的事情,但他们在界面中只使用了一个签名 IBird级 Public Sub SayFromInterface() Debug.Print "Tweet from IBird" End Sub Publ

我听说很多VBA缺乏继承性。我做了一些变通,现在在我看来,这正是继承的作用。我离pro=)还很远,可能遗漏了什么。所以我非常感谢你对可能的不利因素的想法

当我发现您仍然可以在接口类(而不仅仅是签名)中完全实现函数时,我感到非常惊讶,这让我想到了下面的内容。我看到一些人在构图的帮助下做了类似的事情,但他们在界面中只使用了一个签名

IBird级

Public Sub SayFromInterface()
    Debug.Print "Tweet from IBird"
End Sub

Public Sub SayFromInstance()
End Sub
乌鸦级

Implements IBird

Private pBird As IBird

Private Sub Class_Initialize()
    Set pBird = New IBird
End Sub

'let you use already implemented code from "abstract class", to avoid
'duplicating your code, which is the main point of inheritance
'in my opinion
Public Sub IBird_SayFromInterface()
    pBird.SayFromInterface
End Sub

'you can override the IBird function (common use)
Public Sub IBird_SayFromInstance()
    Debug.Print "Tweet from Crow"
End Sub
测试模块

Sub testIBird()

 Dim Bird As IBird

 Set Bird = New Crow

 Bird.SayFromInterface
 Bird.SayFromInstance
 Debug.Print TypeName(Bird)
 Debug.Print TypeOf Bird Is IBird

End Sub
输出:

Tweet from IBird
Tweet from Crow
Crow
True
这是组合,而不是继承——是的,通过组合,您可以模拟继承。如果类实现了封装对象的接口,那么事情开始看起来像某种装饰器模式

除非在
IBird
中没有任何实现代码。接口应该是纯粹抽象的。创建一个
新的
接口实例,使类不再是接口:现在它只是另一个类,公开了一个默认接口,任何其他类都可以实现,而
I
前缀变得相当混乱:

很奇怪的是,客户机代码现在需要考虑他们是想让那只鸟从实例发出啁啾声,还是从接口发出啁啾声,这些都是非常“元”的标识符,使得事情不能像继承那样工作

如果我们有一个
Crow:Bird
,其中
Bird
有一个
IBird.Chirp的实现:

公共虚拟字符串Chirp()=>“Chirp!”;
…然后,
Crow
有这样一个:

public覆盖字符串Chirp()=>“craaw!”;
然后调用哪个方法取决于运行时类型-这应该很明显:

IBird bird1=新鸟();
bird1.Chirp();//“叽叽喳喳!”
IBird bird2=新乌鸦();
bird2.Chirp();//“嘎嘎!”
但是,请想象一个接收
IBird
参数的方法:

public void DoSomething(IBird bird)
{
Debug.Print(bird.Chirp());
}
如果
bird
bird
,它会打印“Chirp!”;如果
bird
是一个
Crow
,它将打印“craaw!”:运行的方法是最派生的重写,它不一定在最派生的类型上定义

继承将允许
GiantCrow:Crow
Crow
继承
Chirp
方法,而不必重写它。这就是你不能用VBA类来模拟的:你不得不写一个等价的

public覆盖字符串Chirp()=>base.Chirp();
…这在技术上是多余的,如果您每次都要这样做只是为了让“基本”成员在默认界面上可见,那么它会变得非常重复

我们实际上不是继承基成员,而是包装对封装对象的调用。decorator模式正是这样做的,它提供了一种非侵入式的方式来扩展VBA类或接口

装饰器实现其扩展的接口,并封装该类型的私有实例。因此,基本上,对于装饰器,“crow继承层次结构”设置如下所示:

Dim bird As IBird
Set bird = Crow.Create(New BaseBird)
'@PredeclaredId
Implements IRepository
Private loggerInternal As ILogger
Private wrappedInternal As IRepository

Public Function Create(ByVal internal As IRepository, ByVal logger As ILogger) As IRepository
    Dim result As LoggingRepository
    Set result.Wrapped = internal
    Set result.Log = logger
    Set Create = result
End Function 

Public Property Get Wrapped() As IRepository
    Set Wrapped = wrappedInternal
End Property

Public Property Set Wrapped(ByVal value As IRepository)
    If Not wrappedInternal Is Nothing Then Err.Raise 5, TypeName(Me), "Instance is already initialized."
    Set wrappedInternal = value
End Property

Public Property Get Log() As ILogger
    Set Log = loggerInternal
End Property

Public Property Set Log(ByVal value As ILogger)
    If Not loggerInternal Is Nothing Then Err.Raise 5, TypeName(Me), "Instance is already initialized."
    Set loggerInternal = value
End Property

Private Function IRepository_SelectAll() As Object
    Log.Info "Starting IRepository.SelectAll"
    Dim t As Double
    t = Timer

    Set IRepository_SelectAll = wrappedInternal.SelectAll

    Log.Info "IRepository.SelectAll completed in " & Timer - t & " seconds."
End Function

Private Sub IRepository_Delete(ByVal id As Long)
    Log.Info "Starting IRepository.Delete(" & id & ")"
    Dim t As Double
    t = Timer

    wrappedInternal.Delete id

    Log.Info "IRepository.Delete completed in " & Timer - t & " seconds."
End Sub

Private Sub IRepository_Save(ByVal entity As Object)
    '...
    wrappedInternal.Save entity
    '...
End Sub

'...
更合适的装饰器模式示例可能是:

Dim repository As IRepository
Set repository = LoggingRepository.Create(BirdRepository.Create(connectionString), New DebugLogger)
其中,
BirdRepository
负责抽象与一些
Birds
表相关的数据库操作(
BirdRepository
实现了
IRepository
),而
LoggingRepository
是一个也实现了
IRepository
的装饰程序,但也包装了一个
IRepository
实例(在本例中是一个
BIRDepository
),以添加其自身的功能—可能如下所示:

Dim bird As IBird
Set bird = Crow.Create(New BaseBird)
'@PredeclaredId
Implements IRepository
Private loggerInternal As ILogger
Private wrappedInternal As IRepository

Public Function Create(ByVal internal As IRepository, ByVal logger As ILogger) As IRepository
    Dim result As LoggingRepository
    Set result.Wrapped = internal
    Set result.Log = logger
    Set Create = result
End Function 

Public Property Get Wrapped() As IRepository
    Set Wrapped = wrappedInternal
End Property

Public Property Set Wrapped(ByVal value As IRepository)
    If Not wrappedInternal Is Nothing Then Err.Raise 5, TypeName(Me), "Instance is already initialized."
    Set wrappedInternal = value
End Property

Public Property Get Log() As ILogger
    Set Log = loggerInternal
End Property

Public Property Set Log(ByVal value As ILogger)
    If Not loggerInternal Is Nothing Then Err.Raise 5, TypeName(Me), "Instance is already initialized."
    Set loggerInternal = value
End Property

Private Function IRepository_SelectAll() As Object
    Log.Info "Starting IRepository.SelectAll"
    Dim t As Double
    t = Timer

    Set IRepository_SelectAll = wrappedInternal.SelectAll

    Log.Info "IRepository.SelectAll completed in " & Timer - t & " seconds."
End Function

Private Sub IRepository_Delete(ByVal id As Long)
    Log.Info "Starting IRepository.Delete(" & id & ")"
    Dim t As Double
    t = Timer

    wrappedInternal.Delete id

    Log.Info "IRepository.Delete completed in " & Timer - t & " seconds."
End Sub

Private Sub IRepository_Save(ByVal entity As Object)
    '...
    wrappedInternal.Save entity
    '...
End Sub

'...
给定
IRepository
对象的方法不能(也绝对不应该)知道它是否给定了一个普通的
BirdRepository
,一个
LoggingRepository
包装一个
BirdRepository
,或者是一个
FakeRepository
,它封装了一个
集合
,而不是对数据库表的访问,而这种多态性就是整个要点


这是在不使用继承的情况下扩展类型的一种方法,VBA完全可以利用这种方法,而不会过分贬低模式。但这不是继承。

为什么将
声明为新IBird
只是为了立即用
新Crow
擦除该引用?只需将
声明为IBird
。我的问题是,播放后没有正确清理代码。谢谢你的评论。我很惊讶地看到VBA可以实现接口,但我想这是COM编程所期望的。我们现在非常熟悉C#和VB.NET的接口,以至于我们忘记了.NET Framework最初是COM编程的一个扩展,名为COM+。@ja72 VBA完全可以进行全面的OOP(,并为您提供单元测试(很快也会提供一个实际的模拟框架)以及其他现代IDE功能。免责声明:我编写了VBA+OOP战舰,并管理Rubberduck OSS项目。@ja72 Rubberduck是主机无关的(在VB6、Excel、Word等中工作)。有一个“导出活动项目”功能,可将整个项目导出到选定文件夹,是的,我们在导入对话框中启用了multiselect;-)