For example I have class A which implements class B
---class A----
implements B
public sub B_do()
end sub
--class B----
public sub do()
end sub
How can I call do() from A? (super.do()) So, how I can define some common variable for both classes? Now I can inherit only functions, sub and properties...
added: same question http://social.msdn.microsoft.com/Forums/en-US/vbgeneral/thread/5a83d794-3da1-466a-83d3-5d2eb0a054b2
added: It is not possible to share variable across hierarhy of classes. You should implements property (same way as functions).
The usual way to do this in VBA is to have A contain an instance of B as well as having A implement B's interface, and then delegate calls to the B interface of A to the internal B.
This is old stuff, but see the Visual Studio 6.0 Programmer's Guide:
http://msdn.microsoft.com/en-us/library/aa716285(VS.60).aspx
There is a chapter on "The Many (Inter)Faces of Code Reuse" that describes this convention:
http://msdn.microsoft.com/en-us/library/aa240846(v=VS.60).aspx
The way MS describes it is:
In addition to implementing abstract
interfaces, you can reuse your code by
implementing the interface of an
ordinary class, and then selectively
delegating to a hidden instance of the
class.
This means that implementation inheritance requires lots of explicit delegation methods. There's even a chapter subheading: "Doesn't This Get Tedious?". Yet another reason why OOP in VBA is a PITA (TM)...
EDIT THAT WON'T FIT IN A COMMENT:
To answer the question you posed in your comment, well, an A is a B. When you make A implement B's interface, you are essentially saying that you can treat an instance of A as if it is actually of type B. In VBA, the way you do that is by declaring a variable of type B, and then setting it to an instance of A. VBA will know what to do when you call it like a B:
Dim usedAsB as B
Dim anA as A
Set anA = New A
Set usedAsB = anA 'fine since A implements B
usedAsB.something() 'will call B_something() defined in class A
As far as what you see in the debug window, I don't why it appears that way. And as far as forced delegation, I'm not sure what you mean. VBA automatically dispatches calls to the B interface to the right methods in the A class. If you mean automatically generating the code to inherit B's implementation in the manner described above, there's nothing like that I know of for VBA. I think the various "professional" versions of VB6 could do that, but I've never used VB6 so I don't know.
This is the way I have used it a long time to simulate an abstract class through the interface.
'class module: IBase
'We define a base interface
'
Sub go(): End Sub
Sub gogo(): End Sub
Now let's define the other classes, beginning with the abstract class 'B'.
'
'class module: B
'
Implements IBase
'Daughter classes should implement 'go'
'Note that the method is 'Public'
Public Sub go(): End Sub
'Let's agree that every daughter class implements the method
'abstract 'super' that returns the IBase interface
Public Property Get super() As IBase: End Property
'
'The signature of other abstract methods can be stated here
'
'Public Sub goGoChild(): End Sub
'Public Function goGoGoChild2(): End Function
'
'
'Note that the methods are accessible through the IBase interface
'
Private Sub IBase_go()
Debug.Print "B: super.go()"
End Sub
Private Sub IBase_gogo()
Debug.Print "B: super.gogo()"
End Sub
Let's create class 'A' which implements the abstract class 'B'
'
'class module: 'A'
'
'We started implementing the abstract class B
Implements B
'we define a private type 'myType'
Private Type myType
'variable that references an instance of 'B'
objB As B
'variable that references the interface of 'B'
objIB As IBase
End Type
'VBA version of 'this'
Private this As myType
'
'Every class that implements 'B' (abstract class)
'you must initialize in your constructor some variables
'of instance.
'
Private Sub Class_Initialize()
With this
'we create an instance of object B
Set .objB = New B
'the variable 'objIB' refers to the IBase interface, implemented by class B
Set .objIB = .objB
End With
End Sub
'Visible only for those who reference interface B
Private Property Get B_super() As IBase
'returns the methods implemented by 'B', through the interface IBase
Set B_super = this.objIB
End Property
Private Sub B_go()
Debug.Print "A: go()"
End Sub
'==================================================
'Class 'A' local method
Sub localMethod1()
Debug.Print "A: Local method 1"
End Sub
And finally, let's create the 'main' module.
Sub testA()
'reference to class 'A'
Dim objA As A
'reference to interface 'B'
Dim objIA As B
'we create an instance of 'A'
Set objA = New A
'we access the local methods of instance 'A'
objA.localMethod1
'we obtain the reference to interface B (abstract class) implemented by 'A'
Set objIA = objA
'we access the 'go' method, implemented by interface 'B'
objIA.go
'we go to the 'go' method of the super class
objIA.super.go
'we access the 'gogo' method of the super class
objIA.super.gogo
End Sub
And the output, in the verification window, will be:
A: Local method 1
A: go()
B: super.go()
B: super.gogo()
One can pull a trick to mimic inheritance. It works by using the default member property.
If you give your derived class a property called Super whose type is the superclass then make that the default member (by exporting and editing file to include Attribute Item.VB_UserMemId = 0, re-importing) then you can reach the superclass by just a pair of round brackets (which resolves to the default member).
This blog post gives the full details but author (disclosure, me) there uses 'Base' instead of 'Super'
Hopefully that syntax is tight enough for you.
I also point out that this does not expose all the base class's internal guts like C# does. This means my method does not suffer from the fragile base class problem. I.e. my method retains encapsulation making it better IMHO.
Related
I heard a lot of that VBA is lacking inheritance. I did a little workaround and now it seems to me that it is exactly what inheritance does. I'm far from pro =) and may be missing something. So I would really appreciate your thoughts about possible downsides.
I was very surprised when found that you still can make a full realization of a function in an interface class (not only a signature), which led me to the following below. I saw that some people did similar with help of composition, but they used only a signature in the interface.
IBird - class
Public Sub SayFromInterface()
Debug.Print "Tweet from IBird"
End Sub
Public Sub SayFromInstance()
End Sub
Crow - class
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
Test - module
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
Output:
Tweet from IBird
Tweet from Crow
Crow
True
That's composition, not inheritance - and yes, with composition you can essentially emulate inheritance. And if the class implements the interface of the encapsulated object, then things start looking like some kind of a decorator pattern.
Except you wouldn't be having any implementation code in IBird. An interface should be purely abstract. Making a New instance of what's supposed to be an interface, makes the class no longer be an interface: now it's just another class exposing a default interface that any other class can implement, and the I prefix becomes rather confusing:
Set pBird = New IBird
It's rather weird that the client code now needs to wonder whether they want that bird to chirp FromInstance or FromInterface - these are very "meta" identifiers that make things not work like inheritance.
If we had a Crow : Bird where Bird had this implementation for IBird.Chirp:
public virtual string Chirp() => "Chirp!";
...and then Crow had this:
public override string Chirp() => "Craaaw!";
Then which method is invoked depends on what the runtime type is - this should feel pretty obvious:
IBird bird1 = new Bird();
bird1.Chirp(); // "Chirp!"
IBird bird2 = new Crow();
bird2.Chirp(); // "Craaaw!"
However picture a method that receives an IBird parameter:
public void DoSomething(IBird bird)
{
Debug.Print(bird.Chirp());
}
If bird is a Bird, it prints "Chirp!"; if bird is a Crow, it prints "Craaaw!": the method that gets to run, is the most derived override, which isn't necessarily defined on the most derived type.
Inheritance would allow a GiantCrow : Crow to inherit the Chirp method from Crow and not necessarily override it. And that is what you can't simulate with VBA classes: you are forced to write the equivalent of...
public override string Chirp() => base.Chirp();
...which is technically redundant, and gets very repetitive if you have to do it every time just to get the "base" members visible on your default interface.
Instead of inheriting base members, we actually wrap calls to the encapsulated object. The decorator pattern does exactly that, and gives you a non-intrusive way of extending a VBA class or interface.
A decorator implements the interface it's extending, and encapsulates a private instance of that type. So basically with a decorator the "crow inheritance hierarchy" setup looks like this:
Dim bird As IBird
Set bird = Crow.Create(New BaseBird)
A perhaps more suitable decorator pattern example might be:
Dim repository As IRepository
Set repository = LoggingRepository.Create(BirdRepository.Create(connectionString), New DebugLogger)
Where a BirdRepository is responsible for abstracting the database operations relating to some Birds table (BirdRepository implements IRepository), and where LoggingRepository is a decorator that also implements IRepository, but also wraps an IRepository instance (in this case a BirdRepository) to add its own functionality - something that might look like this:
'#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
'...
A method that is given a IRepository object cannot (and absolutely should not) know whether it's given a plain BirdRepository, a LoggingRepository wrapping a BirdRepository, or a FakeRepository that encapsulates a Collection instead of access to a database table - and this polymorphism is the entire point.
It's one way to extend a type without using inheritance, that VBA can absolutely leverage without bastardizing the pattern too much. But it's not inheritance.
Expanding a bit on this question VBA inheritance pattern
I'm reproducing a basic inheritance pattern in VBA,
but I would like to understand If there is a more efficient and terse way to accomplish that.
Consider this little test-case.
IAnimal.cls
'declaration
Public Sub eat()
End Sub
Public Sub breathe()
End Sub
Animal.cls : the superclass
Implements IAnimal
' method implementation
Private Sub IAnimal_eat()
Debug.Print "I'm eating something..."
End Sub
Private Sub IAnimal_breathe()
Debug.Print "I'm brething..."
End Sub
Cat.cls : a subclass of Animal
Private super As IAnimal
Implements IAnimal
Private Sub Class_Initialize()
Set super = New Animal
End Sub
'#------------------------------------------------
' methods called when object is accessed as an IAnimal implementor.
' I HAVE TO re-implement all of them also here in the subclass (in java I don't need to. It's sufficient to implement them in the superclass)
Private Sub IAnimal_eat()
Me.eat
End Sub
Private Sub IAnimal_breathe()
Me.breathe
End Sub
'#--------------------------------------------------
' subclass-only methods
' To access those methods I MUST DECLARE the object as Cat (Dim tom as Cat)
Public Sub meow()
Debug.Print "meow..."
End Sub
'#------------------------------------------------
' superclass methods
' Since I need to declare the cat object as a Cat (see above)
' I'm FORCED TO explicitly re-implement all of the superclass methods,
' even those that I don't need to override
' otherwise I can't access them
'#Override
Public Sub eat()
Debug.print "I'm eating a fish!"
End Sub
'I'm forced to re-implement also this method, in order to use it directly on a *Cat* object
'#Unnecessary-Override
Public Sub breathe()
super.breathe
End Sub
Test.bas
Sub Main()
Dim snowball As IAnimal
Set snowball = New Cat
Dim tom As Cat
Set tom = New Cat
snowball.meow 'ERROR Method or data member not found <---- cannot access the Cat-only method "meow"
tom.meow '<--- prints "meow"
' creates a Dog, another subclass of Animal
Dim pluto As Dog
Set pluto = New Dog
'create a collection for all my animals
Dim myPets As Collection
Set myPets = New Collection
myPets.Add tom
myPets.Add pluto
Call feed(myPets) '<---- prints
'I 'm eating a fish
'meow...
'I 'm eating a bone
'woof...
End Sub
' a routine for showing how to manage an heterogeneous collection of animals
Sub feed(animals As Collection)
For Each a In animals
a.eat
If TypeOf a Is Cat Then
a.meow
End If
If TypeOf a Is Dog Then
a.woof
End If
Next
End Sub
By doing this, I can:
call the eat() method on the Cat and Dog objects and have the specific Overridden behavior
call the subclass-only methods (like meow())
pass an heterogeneous collection of Animals to a feed subroutine, that can "safely" call the Animal superclass methods and also trigger specific code based on the Animal subclass
This seems to work but it's cumbersome: imagine you need to implement many Animal subclasses (Dog, Bird, Armadillo, Platypus, Demogorgon,...).
The pattern above FORCES YOU TO:
re-implement all the methods of the IAnimal interface on ALL THE SUBCLASSES
re-implement (again) all the methods, to expose them from the subclass, even when override is not necessary. This is required expecially if you want to access ALSO the subclass-only methods.
So the question is: is there a more efficient/concise way to implement this example (and limit code rewriting for every subclass) ?
tom shouldn't be declared As Cat in the first place; the feed procedure is superfluous:
Sub Main()
Dim tom As IAnimal
Set tom = New Cat
tom.eat
End Sub
Now, in the Cat class, these members shouldn't need to exist:
'Superclass methods --- I have to explicitly override all methods :(
Public Sub eat()
super.eat
Debug.print "...a fish!"
End Sub
In SOLID/OOP, you code against the interface, not the concrete type - that's why tom is an IAnimal and not a Cat. Being accessed through its IAnimal interface, Cat.eat is entirely redundant, and suggests that a Cat does something an IAnimal doesn't do, which violates SOLID principles: suddenly it becomes relevant that an IAnimal is a Cat, and it shouldn't be, because polymorphism allows IAnimal to be anything, and the Liskov Substitution Principle (LSP - the "L" of "SOLID") says any code that works with an IAnimal should be able to work identically regardless of what implementation of that interface it's given.
Abiding by these principles means that no IAnimal implementation should have a copy of IAnimal members on its default interface (e.g. Cat.eat, vs IAnimal.eat), and that entirely removes your point #2:
re-implements (again) all the methods, to expose them from the subclass, even when override is not necessary.
As for point 1...
re-implement all the methods of the IAnimal interface
That's a compiler requirement, and isn't a VBA quirk: be it in Java, C#, or VBA, you can't say "I'm implementing an interface" ...without implementing its members. Of course Java & C# allow for class inheritance, so your base class could say "I'm implementing an interface", implement all the members, and the derived classes would happily inherit them - but then, that's inheritance, not composition anymore.
I have two objects that need to interact with each other one is called Collateral the other is called Model. Model is an abstract Class is implemented by Model_A, Model_B, Model_AB. Each Collateral object has a collection of models as one of its properties. In order to initialize each Model I will need to use information from Collateral(and still another object lets call it User_Input), that information will vary with implementation of Model.
My question is it possible to use a constructor that will be aware of what object is creating it(in this case Model Constructor that knows what Collateral instantiated it)?
If not I assume that someone will suggest for me to use abstract factory pattern, if so is so how would it look like(I'm afraid I'm still green when it comes to OOP)?
For Simplicity's sake assume following:
Collateral has properties A, B, C , Models_Collection
Collateral Calls procedure Run for Each of Models it created( has in Models_Collection)
Model has a public Sub called Run which is implemented in all classes bellow
Procedure Run Manipulates Collateral
Model_A requires property A to initialize
Model_B requires property B to initialize
Model_AB requires property A, B to initialize
Here is a Simplified Code of how I assume this should look like:
Collateral
Dim A, B, C as Variant
Dim Model_Collection as Collection
Sub New_Model( Model_Type as String)
Model_Collection.Add(Model_Implementation)
End Sub
Sub Execute_Models()
For Each Model in Model_Collection
Model.Run(Me)
Next Model
End Sub
Model
Sub Run()
End
Model_A
Implements Model
Sub Class_Initialize()
'Some code that takes property A from Collateral that Created this object
Sub Run(Collateral as Collateral)
'Some Code
End Sub
Model_B
Implements Model
Sub Class_Initialize()
'Some code that takes property B from Collateral that Created this object
Sub Run(Collateral as Collateral)
'Some Code
End Sub
Model_AB
Implements Model
Sub Class_Initialize()
'Some code that takes property A, and B from Collateral that Created this object
Sub Run(Collateral as Collateral)
'Some Code
End Sub
First, lets answer your question. How can you dynamically create instances of different class that all implement the same interface? As was pointed out, VBA doesn't have any constructors, so you're correct. A Factory Pattern is called for here.
How I tend to go about this is define a public enum in the Interface class that keeps track of what classes have been implemented. Any time you implement a new one, you'll need to add it to your enum and Factory. It's a bit more maintenance then I like, but without proper reflection, there's not much we can do about that.
So, the IModel interface:
Public Enum EModel
ModelA
ModelB
ModelC
End Enum
Public Sub Run
End Sub
Your models themselves remain unchanged. Then back in your Collateral implement your New_Model like this.
private models as Collection
Public Sub New_Model(ByVal type As EModel) As IModel
dim model As IModel
Select Case type
Case EModel.ModelA: Set model = New ModelA
Case EModel.ModelB: Set model = New ModelB
Case EModel.ModelC: Set model = New ModelC
End Select
models.Add model
End Sub
Note that it's better to use the enum than a string as in your example so it gets compile time checked for errors instead of runtime. (This removes the chances of misspelling something.)
If it was me implementing this, I would create an actual separate class ModelFactory. Then Collateral would call on the model factory to get what it needs. It makes a nice separation of concerns I think.
An implementation would look something like this, based on your requirements.
Public Function CreateModel(Optional A As Variant, Optional B As Variant, Optional C As Variant)
If Not A Is Nothing Then
If B Is Nothing Then
Set CreateModel = New ModelA
Exit Function
Else
Set CreateModel = New ModelC
Exit Function
End If
End If
If Not B Is Nothing Then
Set CreateModel = New ModelB
Exit Function
End If
End Function
Note that this entirely does away with the enum and the need to specify the type. The factory knows what to create based on which arguments are available to it.
Then your Collateral class simply calls on the factory and gives it whatever it has.
Private A,B,C
Private models As Collection
Private factory As ModelFactory
Private Sub Class_Initialize()
Set factory = New ModelFactory
End Sub
Public Sub New_Model()
models.Add factory.CreateModel(A,B,C)
End Sub
Now, I'm going to pre-emptively answer your next question, because I feel like you're on the verge of asking it already.
How can I tell exactly what type of model I have?
Well, for that you have a few options that are detailed a bit in this code review Q & A. It depends on your use case, but here they are.
TypeName(arg) - Returns the string name of the object. For example:
Dim model As IModel
Set model = New ModelA
Debug.Print TypeName(model) '=> "ModelA"
TypeOf and Is - Checks the type of a variable a bit more strongly. Details are in the question I linked to, but here is an example.
Dim model as IModel
Set model = SomeFunctionThatReturnsAnIModel()
If TypeOf model Is ModelA Then
' take some specific action for ModelA types
Else If TypeOf model Is ModelB Then
' ModelB type specific action
Else If ...
I'm using Excel VBA (Excel 2010) and I've run into a problem when attempting to use inheritance. Basically, I have an interface MyInterface and an implementing class MyImplementation. In the VBA code, when I refer to a Dim of type MyInterface I can only access members defined on that interface - this is expected. When I refer to a Dim of type MyImplementation I cannot access members defined on the interface it implements - not expected.
Why can I not call the interface property directly on the implementation class?
MyInterface
Option Explicit
Public Property Get Text() As String
End Property
MyImplementation
Option Explicit
Implements MyInterface
'The implementation of the interface method'
Private Property Get MyInterface_Text() As String
MyInterface_Text = "Some Text"
End Property
Public Property Get MoreText() As String
MoreText = "Yes, some more text!"
End Property
MainModule - usage examples
Function Stuff()
Dim impl As New MyImplementation
Dim myInt As MyInterface: Set myInt = impl
'The following line is fine - displays "Yes, some more text!"
MsgBox impl.MoreText
'This is also fine - displays "Some text"
MsgBox DownCast(impl).Text
'This is also fine - displays "Some text"
MsgBox myInt.Text
'This is *not* fine - why??
MsgBox impl.Text
End Function
Function DownCast(ByRef interface As MyInterface) As MyInterface
Set DownCast = interface
End Function
The main question is how can I avoid down-casting?
Note - the example above is intentionally contrived. I realize it's generally bad practice to refer directly to implementation classes.
When I refer to a Dim of type MyImplementation, I cannot access members defined on the interface it implements - not expected.
The solution is to change your expectation. That's the way things work in VBA: VBA classes implement COM interfaces (such as IUnknown) without exposing them publicly.
If you want to expose your interface's members from the class, you have to do so explicitly:
Option Explicit
Implements MyInterface
'The implementation of the interface method'
Private Property Get MyInterface_Text() As String
MyInterface_Text = "Some Text"
End Property
Public Property Get MoreText() As String
MoreText = "Yes, some more text!"
End Property
Public Property Get Text() As String
Text = MyInterface_Text
End Property
Simply declare the implementing methods as Public instead of Private will do:
Option Explicit
' Class MyImpl
Implements MyInterface
'The implementation of the interface method'
'Notice the Public here instead of private'
Public Property Get MyInterface_Text() As String
MyInterface_Text = "Some Text"
End Property
The only thing to keep in mind is that to invoke the methods on the implementation you'll need to use a longer name:
Dim instance as MyImpl
' initialize your instance
instance.MyInterface_Text
' instead of instance.Text
That's all.
I recently noticed the CallByName keyword in VB6.
Since this takes a object, procedure name, "call type" and arguments array, can this be used to "fake" some types of polymorphic behavior?
I can make 2 classes, class A and B, each with the same method Foo, and do:
Dim list As New Collection
Dim instanceA As New ClassA
Dim instanceB As New ClassB
Dim current As Object
Call list.Add(instanceA)
Call list.Add(instanceB)
For Each current in list
Call CallByName(current, "methodName", vbMethod)
Next
Anyone done this before? Problems? Horrible idea or genius idea? Implications? Unintended consequences?
Why fake polymorphism? VB6 has real polymorphism in the form of interfaces:
' Interface1.cls '
Sub Foo()
End Sub
' --------------------------------------------- '
' Class1.cls '
Implements Interface1
Private Sub Interface1_Foo()
? "Hello from class 1"
End Sub
' --------------------------------------------- '
' Class2.cls '
Implements Interface1
Private Sub Interface1_Foo()
? "Hello from class 2"
End Sub
' --------------------------------------------- '
' Module1.mod '
Dim x As Interface1
Set x = New Class1
Call x.Foo()
Set x = New Class2
Call x.Foo()
Although I agree with Mr. unicorn, I can't help but point out that CallByName is also unnecessary (in this case) because you can call the method using the standard method syntax and it will result in a late-bound (i.e. not resolved at compile-time) call:
...
For Each current In list
Call current.methodName
Next
The real use of CallByName is to reference method names/properties where the name comes from a (possibly calculated) string value...an absolute abomination, in my opinion.
If you are in a situation where you inherited a huge project with not a single interface in it (it sounds like you did), then CallByName is an awesome tool to fake polymorphism. I use it all the time - never had any issues whatsoever.