Композиция VBA для java-подобного наследования

Немного расширяя этот вопрос Шаблон наследования VBA

Я воспроизводю базовый шаблон наследования в VBA, но я хотел бы понять, есть ли более эффективный и краткий способ сделать это.

Рассмотрим этот небольшой тестовый пример.

IAnimal.cls

'declaration
Public Sub eat()
End Sub

Public Sub breathe()
End Sub

Animal.cls: суперкласс

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: подкласс 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

Таким образом я могу:

  • вызвать метод eat() для объектов Cat и Dog и иметь особое поведение Overridden
  • вызовите методы только для подкласса (например, meow())
  • передать неоднородная коллекция животных подпрограмме feed, которая может «безопасно» вызывать методы суперкласса Animal, а также запускать определенный код на основе подкласса Animal

Кажется, это работает, но это громоздко: представьте, что вам нужно реализовать множество подклассов Animal (Dog, Bird, Armadillo, Platypus, Demogorgon, ...). Вышеупомянутый шаблон ВЫНУЖДАЕТ ВАС:

  1. повторно реализовать все методы интерфейса IAnimal на ВСЕХ ПОДКЛАССАХ
  2. повторно реализовать (снова) все методы, чтобы выставить их из подкласса, даже если переопределение не требуется. Это необходимо, особенно если вы хотите получить доступ ТАКЖЕ к методам только подкласса.

Итак, вопрос: есть ли более эффективный / краткий способ реализовать этот пример (и ограничить переписывание кода для каждого подкласса)?

Вы не можете реализовать интерфейс, не реализовав его членов, нет никакого способа обойти это. Пункт 2 поднимает флаг: если подкласс не может обрабатываться вызывающим кодом точно так же, как любой другой подкласс, то вы нарушаете принцип подстановки Лискова - нет необходимости повторно реализовывать все методы снова. и выставляем их из подкласса.

Mathieu Guindon 20.12.2018 18:45

Короче говоря, вы должны реализовать каждый член интерфейса. Вы можете узнать больше об этом здесь.

JohnyL 20.12.2018 18:46

спасибо @MathieuGuindon за указание на принцип замены Лискова. Пытался исправить код соответствующим образом

Federico Destefanis 20.12.2018 19:58
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
3
476
1

Ответы 1

tom не следует в первую очередь объявлять As Cat; процедура feed лишняя:

Sub Main()    
    Dim tom As IAnimal
    Set tom = New Cat    
    tom.eat    
End Sub

Теперь в классе Cat эти члены не должны существовать:

'Superclass methods --- I have to explicitly override all methods :(
Public Sub eat()
    super.eat 
    Debug.print "...a fish!"
End Sub

В SOLID / OOP вы кодируете интерфейс, а не конкретный тип - вот почему tom - это IAnimal, а не Cat. Доступ к IAnimal осуществляется через интерфейс Cat.eat, Cat полностью избыточен и предполагает, что IAnimal делает то, чего не делает IAnimal, что нарушает принципы SOLID: внезапно становится актуальным, что Cat является IAnimal, а этого не должно быть, потому что полиморфизм позволяет IAnimal быть чем угодно, а Принцип замены Лискова (LSP - «L» от «SOLID») говорит, что любой код, работающий с IAnimal, должен иметь возможность работать идентично независимо от того, какая реализация этого интерфейса ему предоставлена.

Соблюдение этих принципов означает, что ни одна реализация IAnimal не должна иметь копию членов Cat.eat в интерфейсе по умолчанию (например, IAnimal.eat или tom), и это полностью устраняет ваш пункт №2:

  1. re-implements (again) all the methods, to expose them from the subclass, even when override is not necessary.

Что касается пункта 1 ...

  1. re-implement all the methods of the IAnimal interface

Это требование компилятора, а не причуда VBA: будь то в Java, C# или VBA, вы не можете сказать «Я реализую интерфейс» ... без реализации его членов. Конечно, Java и C# допускают наследование классов, поэтому ваш базовый класс может сказать: «Я реализую интерфейс», реализовать все члены, и производные классы с радостью унаследуют их - но тогда это наследование, а не сочинение.

Другие вопросы по теме