Поддерживают ли какие-либо языки ОО механизм, гарантирующий, что переопределенный метод вызовет базу?

12

Я думаю, что это может быть полезной языковой функцией, и мне было интересно, поддерживают ли ее какие-либо языки.

Идея в том, если у вас есть:

class C
  virtual F
     statement1
     statement2

и

class D inherits C
  override F
     statement1
     statement2
     C.F()

К CF () будет применено ключевое слово, такое, что удаление последней строки кода, приведенного выше, приведет к ошибке компилятора, так как он говорит: «Этот метод может быть переопределен, но реализация должна выполняться независимо от того, что».

Аарон Анодид
источник

Ответы:

14

Да, они делают. Это называется скандинавская модель ОО, она используется, например, в Simula (другая модель ОО, которая широко распространена и считается само собой разумеющейся в настоящее время, - американская модель). В скандинавской модели вы не переопределяете, а задаете подчиненное поведение.

в методе суперкласса foo:

some-code-before
INNER // this is the actual Simula keyword
some-code-after

в подклассе 'метод foo:

some-code-in-subclass

При вызове метода Foo суперкласса экземпляры, только some-code-beforeи some-code-afterбывает ( INNERничего не делает), но если вы звоните Foo Подкласс экземпляры, он делает some-code-before, some-code-in-subclassа затем some-code-after.

Херби
источник
9

Ни один из известных мне языков не заставляет вызывать переопределенный метод. Действительно, некоторые языки допускают переопределение методов, которые не могут быть переопределены (например, использование newключевого слова в C #). Однако есть два способа приблизиться к этому.

Первый - создать не переопределяемый метод (например, метод, в котором отсутствует virtualключевое слово в C # или метод , содержащий finalключевое слово в Java), который вызывает переопределяемый метод, который нельзя вызвать извне класса (например, protectedв C #, Java или C ++).

class C
  A
     statement1
     F
     statement3

  protected virtual F
     statement2

и

class D inherits C

  protected override F
     statement4
     C.F()

Переопределение классов Cможет свободно переопределять Fи изменять его поведение, но вызывающие извне класса только получают к нему доступ A.

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

Второй способ - использовать язык, который обеспечивает предварительные условия и постусловия, указанные в базовом классе, такие как Eiffel или C # с кодовыми контрактами. Это не заставит вызывать базовый класс, но переопределенный метод может быть вынужден выполнять те же операторы. Использование аспектов может также помочь, если язык позволяет аспектам наследоваться.

Актон
источник
2
Вы даже можете сделать метод переопределенным privateв C ++ :) Херб Саттер объясняет это здесь подробно.
fredoverflow
Шаблонный шаблон имеет только один недостаток: вам нужно каждый раз переопределять его при углублении иерархии наследования. Пример с Simula более элегантный и по-прежнему учитывает шаблон шаблона.
Павел Воронин
7

На самом деле не является частью языка, но статический анализатор кода FindBugs для Java имеет аннотацию, OverrideMustInvokeкоторую разработчик может добавить к методу и которая заставит FindBugs показать ошибку, если он найдет переопределяющий метод, который не вызывает супер реализацию , Он даже позволяет указать, должен ли вызов быть первым или последним в переопределяющем методе.

Майкл Боргвардт
источник
6

Требование вызова метода суперкласса является анти-паттерном . Если он не применяется во время компиляции, он подвержен ошибкам, поэтому вы ищете языковую конструкцию, которая его проверяет.

Существует способ, который поддерживается во всех языках ОО: шаблон шаблона . Здесь вы делаете метод суперкласса не переопределяемым, и в нем вы вызываете переопределяемый метод. Затем подкласс может переопределить этот метод для добавления функциональности:

class super {
  public final void doSomething() {
    doSpecialthing();
    doMore();
  }
  public void doSpecialthing() {
  }
}

В зависимости от местоположения вызова переопределенного метода, он даже позволяет определить порядок выполнения, который при обычном супер вызове осуществляется по желанию разработчика подкласса.

Озан
источник
1

Самый близкий образец, который я могу придумать, это самоподписанные события. Это немного громоздко, и совсем не интуитивно понятно для кодера, но достигает цели.

class C
{
    public void F()
    {
        ...
        OnF()
    }

    protected event OnF
}

class D : C
{
    public D()
    {
        base.OnF += this.F
    }

    private void F
    {
        ...
    }
}
Hand-E-Food
источник
1
Это довольно распространенный шаблон проектирования, иногда с переопределением как про, так и после, например. ViewWillAppear (), ViewDidAppear (). Идеально, когда вы хотите разрешить подклассам расширять (а не изменять) поведение по умолчанию.
Крис Ван Баел
1

Машина «flavors» на Лиспе допускала методы с типом «before», «after» и «around» унаследованным основным методом.

ddyer
источник
0

Хотя это не плохая идея в теории, у нее есть отрицательный побочный эффект ограничения моих вариантов при реализации D. Например, что если (по какой-то непостижимой причине) удобнее вызывать реализацию суперкласса Fиз какого-то другого метода:

class D inherits C
    override F
        statement1
        statement2
        G()
    G
        statement3
        C.F()
        statement4

По вашему сценарию, я предполагаю, что компилятор будет отмечать реализацию Fin D, даже если он (косвенно) вызывает C.F().

По сути, вы описали возможный механизм, помогающий компилятору распознавать, когда Cнарушается контракт на классы, наследуемые от . Я хочу сказать, что, хотя это здорово, это не должно происходить за счет ограничения того, как я могу реализовать свой подкласс.

макинтош
источник
1
-1: возможность придумать ситуацию, в которой вы не захотите использовать, это не ответ на вопрос «позволяет ли это какой-либо язык».
@GrahamLee: очень верно. Моя цель состояла в том, чтобы попытаться объяснить одну причину, почему ни один язык (который я знаю) не реализует такую ​​функцию. Думаю, я так увлекся объяснением, что забыл упомянуть, почему объяснил это. -1 радостно принято. :)
Mac