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 declaredAs Cat
in the first place; thefeed
procedure is superfluous:Now, in the
Cat
class, these members shouldn't need to exist:In SOLID/OOP, you code against the interface, not the concrete type - that's why
tom
is anIAnimal
and not aCat
. Being accessed through itsIAnimal
interface,Cat.eat
is entirely redundant, and suggests that aCat
does something anIAnimal
doesn't do, which violates SOLID principles: suddenly it becomes relevant that anIAnimal
is aCat
, and it shouldn't be, because polymorphism allowsIAnimal
to be anything, and the Liskov Substitution Principle (LSP - the "L" of "SOLID") says any code that works with anIAnimal
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 ofIAnimal
members on its default interface (e.g.Cat.eat
, vsIAnimal.eat
), and that entirely removes your point #2:As for point 1...
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.