简体   繁体   中英

Interface implementing multiple interfaces in VBA

I would like to create an Interface that implements multiple interfaces, and then make a class that implements that interface. For example:

IFlyable module:

Public Sub Fly()
End Sub

Public Function GetAltitude() As Long
End Function

ISwimmable module:

Public Sub Swim()
End Sub

Public Function GetDepth() As Long
End Function

IFlySwim module:

Implements IFlyable
Implements ISwimmable

Public Sub IFlyable_Fly()
End Sub

Public Function IFlyable_GetAltitude() As Long
End Function    

Public Sub ISwimmable_Swim()
End Sub

Public Function ISwimmable_GetDepth() As Long
End Function

This all compiles just fine. But if I declare an Object of type IFlySwim, the predictive text in VBA gives me:

IFlyable_Fly、IFlyable_GetAltitude、ISwimmable_Swim

And trying to create a Class that implements IFlySwimmable just doesn't work.

Class Duck:

Implements IFlySwim

Public Sub IFlySwim_IFlyable_Fly()
    Debug.Print "Flying with wings!"
End Sub

Public Function IFlySwim_IFlyable_GetAltitude() As Long
    IFlySwim_IFlyable_GetAltitude = 30
End Function

Public Sub IFlySwim_ISwimmable_Swim()
    Debug.Print "Swimming with feet!"
End Sub

Public Function IFlySwim_ISwimmable_GetDepth() As Long
    IFlySwim_ISwimmable_GetDepth = 20
End Function

Pressing the compile button gives me the error: "Compile error: Object module needs to implement 'IFlyable_Fly' for interface 'IFlySwim'.

So this leads me to believe that Implement ing an interface within another interface Doesn't Work. Or at least, it doesn't make sense from VBA's point of view. Which is understandable, because I'm not actually providing an implementation for IFlyable and ISwimmable in IFlySwim . What I really want to do is defer implementation for IFlyable and ISwimmable to a class that implements both of them.

So now, if my Duck class is:

Implements IFlyable
Implements ISwimmable

Public Sub IFlyable_Fly()
    Debug.Print "Flying with wings!"
End Sub

Public Function IFlyable_GetAltitude() As Long
    IFlyable_GetAltitude = 30
End Function

Public Sub ISwimmable_Swim()
    Debug.Print "Swimming with feet!"
End Sub

Public Function ISwimmable_GetDepth() As Long
    ISwimmable_GetDepth = 20
End Function

Then when I go to use it, the predictive dropdown gives me:

鸭子的用法

I could cast Duck to either the IFlyable or ISwimmable to use as such:

鸭子用法2

But what if I wanted to create a Function or Sub that accepts a Flyable/Swimmable? Do I have to create a completely separate interface called IFlySwim that contains all of the Sub/Functions from IFlyable and ISwimmable , and then modify my Duck class to only implement from the IFlySwim ?

Unfortunately, that is how inheritance by composition works. However you should realise that your Duck class is tying the interface to the concrete implementation of the function implementations. You need to use a second level of indirection

Implements IFlyable
Implements ISwimmable

Private Sub IFlyable_Fly()
    Fly
End Sub

|Private Function IFlyable_GetAltitude() As Long
    IFlyable_GetAltitude=GetAltitude
End Function

Private Sub ISwimmable_Swim()
    Swim
End Sub

Private Function ISwimmable_GetDepth() As Long
    ISwimmable=GetDepth
End Function

Public Sub Fly()
    Debug.Print "Flying with wings!"
End Sub

Public Function GetAltitude() As Long
    GetAltitude = 30
End Function

Public Sub Swim()
    Debug.Print "Swimming with feet!"
End Sub

Public Function GetDepth() As Long
    GetDepth = 20
End Function

From this you can see that GetAltitude and GetDepth could both refer to a property of VerticalPosition

Private Function IFlyable_GetAltitude() As Long
        IFlyable_GetAltitude=GetVerticalPosition
End Function


Private Function ISwimmable_GetDepth() As Long
    ISwimmable_GetDepth=- GetVerticalPosition
End function

Which may or may not be helpful top you.

Interface methods/properties (ie methods/properties prefixed with an underscore) should not be exposed as public. They must be implemented to fulfill the contract of the interface in the derived class, but they should be marked as private. Once you have done so, you can expose public members with method names like Fly or GetAltitude (see below):

Option Explicit

Implements ISwimmable
Implements IFlyable

'********************************************************
'Public Methods
'********************************************************
Public Function GetDepth() As Long

    GetDepth = 10

End Function

Public Sub Swim()

    Debug.Print "Swimming!"

End Sub

Public Function GetAltitude() As Long

    GetAltitude = 50

End Function


Public Sub Fly()

    Debug.Print "Flying with wings!"

End Sub


'********************************************************
'Private Methods
'********************************************************
Private Function ISwimmable_GetDepth() As Long
End Function

Private Sub ISwimmable_Swim()
End Sub


Private Function IFlyable_GetAltitude() As Long
End Function

Private Sub IFlyable_Fly()
End Sub

And your test code becomes the following: (see below)

Sub InterfaceTest()

    Dim ducky As New Duck

    ducky.Fly
    Debug.Print ducky.GetAltitude()

    ducky.Swim
    Debug.Print ducky.GetDepth()

End Sub

But what if I wanted to create a Function or Sub that accepts a Flyable/Swimmable? Do I have to create a completely separate interface called IFlySwim that contains all of the Sub/Functions from IFlyable and ISwimmable, and then modify my Duck class to only implement from the IFlySwim?

Short answer: Yes.

Why?

Because, VBA/VB6 does not support inheritance fully. To do what you ask, you would have create a third class that inherits from ISwimmable and IFlyable , and then implement that class in other classes.

In a fully OOP language like C# , you could to the following: (see below)

public interface IFlyable   {

    void Fly(); 
    int GetAltitude(); 

} 


public interface ISwimmable {

    void Swim();
    int GetDepth();

} 

//Base Class
public abstract class BaseDuck: ISwimmable, IFlyable    {

    public BaseDuck(){}

    public void Fly()   {

        Console.WriteLine("Flying with wings!"); 

    } 

    public virtual int GetAltitude()    {

        return 50; 

    } 

    public void Swim()  {

        Console.WriteLine("Swimming!"); 

    } 

    public virtual int GetDepth()   {

        return 10; 

    } 


}

//Derived Class
public class SeaDuck: BaseDuck  {

    public SeaDuck(){}

    public override int GetAltitude()   {

        return 50; 

    } 

    public override int GetDepth()  {

        return 100; 

    } 

    public void RideWaves() {

        Console.WriteLine("Riding waves!");

    }

}

//Derived Class
public class MallardDuck: BaseDuck  {

    public MallardDuck(){}

    public override int GetAltitude()   {

        return 100; 

    } 

    public override int GetDepth()  {

        return 10; 

    } 

    public void HeadToLandForWinter()   {

        Console.WriteLine("Headed for land!");

    }

}

Using the following to test: (see below)

using System;

public class Program
{
    public static void Main()
    {
        SeaDuck seaDuck = new SeaDuck();

        Console.WriteLine("Sea Duck:");
        seaDuck.Fly();
        Console.WriteLine(seaDuck.GetAltitude().ToString()); 

        seaDuck.Swim();
        Console.WriteLine(seaDuck.GetDepth().ToString()); 

        seaDuck.RideWaves(); 

        Console.WriteLine("\n");

        Console.WriteLine("Mallard Duck:");
        MallardDuck mallardDuck = new MallardDuck();
        mallardDuck.Fly();
        Console.WriteLine(mallardDuck.GetAltitude().ToString()); 

        mallardDuck.Swim();
        Console.WriteLine(mallardDuck.GetDepth().ToString()); 

        mallardDuck.HeadToLandForWinter();

    }
}

Produces:

Sea Duck:
Flying with wings!
50
Swimming!
100
Riding waves!


Mallard Duck:
Flying with wings!
100
Swimming!
10
Headed for land!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM