Чи підтримують якісь мови OO механізм гарантування переохопленого методу виклику бази?


12

Я думаю, що це може бути корисною мовною функцією, і мені було цікаво, чи вже підтримують якісь мови.

Ідея полягає в тому, якщо у вас є:

class C
  virtual F
     statement1
     statement2

і

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

Існує ключове слово, застосоване до CF (), таким чином, що видалення останнього рядка коду вище призведе до помилки компілятора, оскільки він говорить "Цей метод може бути замінений, але реалізація тут повинна запускатися незалежно від того".


Відповіді:


14

Так, вони роблять. Його називають скандинавською моделлю ОО, вона використовується, наприклад, в Simula (інша модель ОО, яка широко поширена і прийнята як даний, - американська модель). У скандинавській моделі ви не переважаєте, а надаєте субповедінку.

у методі Superclass foo:

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

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

some-code-in-subclass

Якщо ви називаєте метод foo "екземпляри" Superclass, тільки 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.

Редагувати: Як вже вказували інші, це називається шаблоном методу Шаблон .

Другий спосіб - використовувати мову, яка виконує передумови та постумови, визначені в базовому класі, наприклад, Ейфелева або 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
    {
        ...
    }
}

1
Це досить розповсюджена модель дизайну, іноді як з промішками, так і після зміни, наприклад. ViewWillAppear (), ViewDidAppear (). Ідеально, коли ви хочете дозволити підкласам розширювати (а не змінювати) поведінку за замовчуванням.
Kris Van Bael

1

Машини Lisp "ароматизатори" дозволяли методи типу "до" "після" та "навколо" успадкованого основного методу.


0

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

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

Згідно з вашим сценарієм, я думаю, що компілятор позначив би реалізацію Fв D, хоча він (побічно) викликає C.F().

В основному те, що ви описали, - це можливий механізм, який допоможе компілятору визнати, коли Cпорушується контракт про класи, що передаються у спадок . Моя думка полягає в тому, що, хоча це чудова річ, це не повинно впливати на обмеження того, як я можу реалізувати свій підклас.


1
-1: вміння думати про ситуацію, коли ви не хотіли б користуватися, що не є відповіддю на запитання "чи дозволяє яка-небудь мова".

@GrahamLee: дуже правда. Моя думка полягала в тому, щоб спробувати пояснити одну з причин, чому жодна мова (про яку я знаю) не реалізує таку особливість. Я здогадуюсь, що я так захопився поясненням цього, що забув згадати, чому я це пояснював. -1 щасливо прийнятий. :)
Mac
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.