Czy jakieś języki OO obsługują mechanizm gwarantujący, że metoda zastąpiona wywoła bazę?

12

Myślę, że może to być przydatna funkcja językowa i zastanawiałem się, czy którykolwiek język już ją obsługuje.

Chodzi o to, jeśli masz:

class C
  virtual F
     statement1
     statement2

i

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

Do CF () zastosowane byłoby słowo kluczowe takie, że usunięcie ostatniego wiersza kodu powyżej spowodowałoby błąd kompilatora, ponieważ mówi on: „Metodę tę można zastąpić, ale implementacja tutaj musi działać bez względu na wszystko”.

Aaron Anodide
źródło

Odpowiedzi:

14

Tak, robią. Nazywa się to skandynawskim modelem OO, jest on używany na przykład w Simula (drugi model OO, który jest szeroko rozpowszechniony i przyjęty jako przyznany teraz, to model amerykański). W modelu skandynawskim nie zastępujesz, ale zapewniasz pod-zachowanie.

w metodzie Superclass foo:

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

w metodzie podklasy foo:

some-code-in-subclass

Jeśli wywołanie metody foo SuperClass' instancje, tylko some-code-beforei some-code-afterzdarza się ( INNERnic nie robi), ale jeśli zadzwonisz podklasy foo instancje, to robi some-code-before, some-code-in-subclassa potem some-code-after.

herby
źródło
9

Żaden znany mi język nie wymusza wywoływania metody przesłoniętej. Rzeczywiście, niektóre języki pozwalają na przesłanianie metod, których nie można zastąpić (takich jak użycie newsłowa kluczowego w języku C #). Istnieją jednak dwa sposoby podejścia do tego.

Pierwszym z nich jest stworzenie metody niemożliwej do zastąpienia (np. Takiej, w której brakuje virtualsłowa kluczowego w języku C # lub takiej, która ma finalsłowo kluczowe w Javie), która wywołuje metodę nadrzędną, której nie można wywołać spoza klasy (np. protectedW języku C #, Java lub C ++).

class C
  A
     statement1
     F
     statement3

  protected virtual F
     statement2

i

class D inherits C

  protected override F
     statement4
     C.F()

Przesłanianie klas Cmoże dowolnie zastępować Fi modyfikować jego zachowanie, ale wywołujący spoza klasy mają do niego dostęp tylko poprzez A.

Edycja: jak zauważyli inni, jest to tak zwany wzorzec metody szablonu .

Drugim sposobem jest użycie języka, który wymusza warunki wstępne i dodatkowe określone w klasie podstawowej, takie jak Eiffel lub C # z kontraktami kodowymi. Nie wymusi to wywołania klasy bazowej, ale przesłonięta metoda może zostać zmuszona do wykonania tych samych instrukcji. Korzystanie z aspektów może również pomóc, jeśli język pozwala na dziedziczenie aspektów.

akton
źródło
2
Możesz nawet ustawić metodę zastępowania privatew C ++ :) Herb Sutter wyjaśnia ją tutaj szczegółowo.
fredoverflow
Wzorzec szablonu ma tylko jedną wadę, że za każdym razem trzeba go ponownie wdrożyć, przechodząc głębiej przez hierarchię dziedziczenia. Przykład z Simula jest bardziej elegancki i nadal pozwala na wzorzec szablonu.
Pavel Voronin
7

Nie jest to część języka, ale analizator kodu statycznego FindBugs dla Javy ma adnotację, OverrideMustInvokektórą programista może dodać do metody, i która spowoduje, że FindBugs wyświetli błąd, jeśli znajdzie nadrzędną metodę, która nie wywołuje super-implementacji . Pozwala nawet określić, czy wywołanie musi być pierwsze czy ostatnie w metodzie zastępowania.

Michael Borgwardt
źródło
6

Wymaganie wywołania metody nadklasy jest anty-wzorcem . Jeśli nie jest wymuszony w czasie kompilacji, jest podatny na błędy, dlatego szukasz konstruktora języka, który to sprawdza.

Istnieje sposób obsługiwany we wszystkich językach OO: wzorzec metody szablonu . Tutaj sprawiasz, że metoda nadklasy nie jest nadpisywalna, a w niej wywołujesz metodę nadpisywalną. Następnie podklasa może zastąpić tę metodę, aby dodać funkcjonalność:

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

W zależności od lokalizacji wywołania metody zastępowanej pozwala nawet określić kolejność wykonywania, która w przypadku zwykłego super wywołania jest do woli implementatora podklasy.

Ozan
źródło
1

Najbliższy wzorzec, jaki mogę wymyślić, to subskrybowane zdarzenia. Jest to trochę kłopotliwe i wcale nie intuicyjne dla programisty, ale osiąga cel.

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

    protected event OnF
}

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

    private void F
    {
        ...
    }
}
Hand-E-Food
źródło
1
Jest to dość powszechny wzorzec projektowy, czasem z przesłonięciami zarówno pro, jak i post, np. ViewWillAppear (), ViewDidAppear (). Idealny, gdy chcesz pozwolić podklasom na rozszerzenie (zamiast modyfikacji) domyślnego zachowania.
Kris Van Bael
1

„Smaki” maszyny Lisp dopuszczały metody typu „przed”, „po” i „wokół” odziedziczonej metody głównej.

ddyer
źródło
0

Chociaż teoretycznie nie jest to zły pomysł, ma negatywny efekt uboczny, ograniczając moje opcje podczas wdrażania D. Na przykład, co jeśli (z jakiegoś niezgłębionego powodu) wygodniej jest wywołać implementację nadklasy Fz innej metody:

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

W twoim scenariuszu wyobrażam sobie, że kompilator oznaczałby implementację parametru Fin D, nawet jeśli wywołuje (pośrednio) C.F().

Zasadniczo to, co opisałeś, jest możliwym mechanizmem pomagającym kompilatorowi w rozpoznaniu, kiedy Czostanie naruszona umowa dotycząca dziedziczących klas . Chodzi mi o to, że chociaż jest to świetna rzecz, nie powinno to odbywać się kosztem ograniczenia, w jaki sposób mogę wdrożyć moją podklasę.

Prochowiec
źródło
1
-1: możliwość wymyślenia sytuacji, w której nie chciałbyś skorzystać, co nie jest odpowiedzią na pytanie „czy na to pozwala każdy język”.
@GrahamLee: bardzo prawda. Chciałem wyjaśnić jeden z powodów, dla których żaden język (którego znam) nie implementuje takiej funkcji. Wydaje mi się, że tak pochłonęło mnie to wyjaśnianie , że zapomniałem wspomnieć, dlaczego to wyjaśniam. -1 szczęśliwie przyjęty. :)
Mac