Comment forcer l'appel uniquement aux méthodes de la superclasse malgré le fait qu'elles aient été remplacées (en Ocaml)

StackOverflow https://stackoverflow.com/questions/4829597

Question

Pour être honnête, je ne connais pas grand-chose au système d'objets d'OCaml.L'extrait suivant illustre mon problème :

class foo =
  object (o)

    method foo1 y =
      print_endline "foo1";
      o#foo2 (y - 1)

    method foo2 y =
      print_endline "foo2";
      if y > 0 then o#foo2 y else print_endline "end"
  end

class bar =
  object (o : 'self_type)
    inherit foo as super

    method foo2 y =
    print_endline "bar2";
    if y > 0 then super#foo1 y else print_endline "endbar"

  end

let _ = (new bar)#foo2 3

Une fois exécuté, l'extrait produit le résultat suivant :

bar2
foo1
bar2
foo1
bar2
endbar

, montrant que la méthode de superclasse foo1 (appelée via super#foo1) exécute la méthode remplacée foo2 de la sous-classe.Je m'attendais plutôt à ce qu'il appelle la méthode foo2 depuis la superclasse, comme elle est appelée via super.

Est-il possible d'obtenir ce comportement, c'est-à-direUne méthode de superclasse appelle-t-elle uniquement d'autres méthodes de superclasse même si elles sont remplacées dans une sous-classe ?

Était-ce utile?

La solution

Je ne suis pas sûr à 100%, mais je suis assez sûr que vous ne pouvez pas. Dans l'héritage et le sous-typage OCaml sont deux concepts différents. Une classe peut être un sous-type d'un autre type sans héritage. Tout l'héritage est fait tirer dans les méthodes de la classe héritée.

Polymorphisme est réalisée par la saisie de structure, de sorte que votre appel à foo2 appelle la méthode en bar parce que bar a mis en place foo2 et non pas parce inherits de bar de foo (par opposition à dire, C ++, où bar#foo2 est appelé en raison d'une table de fonction virtuelle rechercher).

Cela dit, OCaml ne vous fournit un moyen de faire la distinction entre les méthodes et les méthodes héritées redéfinis en utilisant la syntaxe de inherit...as.... Dans bar de votre exemple, o#foo1 et super#foo1 sont la même méthode (car bar ne met pas en oeuvre foo1) alors que différentes méthodes sont o#foo2 et super#foo2. Malgré cela, je ne pense pas qu'il y ait de toute façon pour la classe héritée de savoir qu'il a été hérité et la distinction entre les méthodes et les méthodes surchargées est tout. Il pourrait y avoir une syntaxe pour cela, mais je doute fortement en raison du fait que l'héritage et le polymorphisme sont des concepts indépendants.

Autres conseils

Non, ce n'est pas. Ceci est « obligatoire en retard », une idée de base de OO, pas spécifique à OCaml.

Je ne sais pas ce que vous essayez de faire, mais la programmation orientée objet est peut-être pas les bons outils, ou du moins pas utilisé de cette façon. Si vous voulez apprendre OCaml, vous devriez probablement se concentrer sur les parties non-OO, ce qui est assez intéressant pour commencer.

Comme points de GASCHE sur, c'est le comportement prévu et standard pour les langages OO.

L'appel super#foo1, puisque bar ne remplace pas foo1, est exactement équivalente à o#foo1. La construction super existe seulement pour être en mesure d'appeler la méthode foo2 de foo de méthodes bar (sinon il n'y a aucun moyen de cette méthode de référence). Dans foo1 comme appelé à partir bar#foo2, o est en fait un bar, pas foo, donc appeler la méthode foo2 appelle la méthode bar de foo2.

Je dirais que si vous souhaitez coder en dur ce comportement, vous feriez mieux de ne pas utiliser de programmation orientée objet.Implémentez-le simplement en tant que fonctions appelant d’autres fonctions.

("ce comportement" tel que je l'ai compris :si j'appelle foo2 de l'intérieur du code qui a été appelé comme super#foo1, alors exactement le foo2 de l'implémentation de la superclasse doit être appelée, et non des implémentations plus "spécifiques" des sous-classes)

C’est la manière la plus simple, la plus propre et la plus claire de procéder :des fonctions de programme qui font ce que vous voulez.

Ou vous devriez expliquer à vous-même et à nous :Pourquoi avez-vous besoin de POO ?La raison n’en est pas indiquée dans le texte de la question.Pourquoi faire foo1 et foo2 des méthodes plutôt que des fonctions indépendantes ?(À part foo1 et foo2, vous pouvez avoir des objets, des classes et des méthodes dans votre programme, le cas échéant).

Vous vous demandez si cette question vient d'une comparaison avec d'autres langues

Si vous connaissez un autre langage OO, c'est étrange que vous souhaitiez "ce comportement" de la POO :ce n'est pas le comportement attendu, par exemple, en Java ou en C++, car ils utilisent le concept d'une table de méthodes virtuelles associée à chaque objet au moment de l'exécution. Ainsi, lorsque vous appelez une méthode dans votre programme, elle est distribuée à l'exécution. temps jusqu'à la mise en œuvre de cette méthode réellement associée à l'objet.Donc en bref : chaque fois que vous utilisez une expression d'appel de méthode dans votre programme, vous vous engagez à respecter ce principe de recherche de l'implémentation de la méthode ("liaison tardive"), comme l'a souligné Gasche.Bien que cf.les différences entre OCaml et les langages implémentés avec une table de méthodes virtuelles soulignées par Niki Yoshiuchi.

Formaliser toute la discussion sur les comportements disponibles et recherchés

Bien que ce que vous souhaitez ne soit peut-être pas le comportement attendu et disponible dans de nombreux langages OO populaires, il est imaginable et pourrait être implémenté dans certains systèmes POO spécifiques, si l'on a accès aux composants internes de l'implémentation POO.

Disons, si dans une implémentation, super est une structure contenant la table des méthodes de la superclasse (à laquelle recourir lors de la résolution d'un appel de méthode pour l'objet actuel), et les méthodes sont des fonctions qui doivent recevoir l'objet (la table des méthodes) comme premier argument, puis pour effectuer ce que vous veux, on écrirait super.foo1(super, y).

(Je me demande en fait s'il existe des implémentations de POO dont les composants internes sont exposés au programmeur et qui permettent d'effectuer un tel appel.)

Alors que le comportement habituel de la POO serait exprimé dans ce système par this.foo1(this, y) (où this est la table des méthodes pour l'objet actuel.)

Votre appel OCaml super#foo1 y ou un Java super.foo1(y); se traduit par ce "mon" système par super.foo1(this, y).(Même si toujours cf.les différences soulignées par Niki Yoshiuchi entre OCaml et des langages comme Java implémentés avec une table de méthodes virtuelles.)

Vous voyez les différences entre les 3 cas.

Annexe.À la recherche de langues qui fonctionneraient de cette façon

Hmm, PHP pourrait être un langage dans lequel ce comportement serait possible, mais :

  • uniquement sur la programmation "au niveau de la classe" (méthodes statiques), pas au niveau de l'objet ;
  • seulement quand vous codez en dur des choses étranges "reliure statique tardive" aux appels de fonction :

#!/usr/bin/php
<?php
class Foo {
  public static function foo1($y) {
    echo "foo1\n";
    static::foo2($y-1);
  }

  public static function foo2($y) {
    echo "foo2\n";
    if ($y > 0)
      static::foo2($y);
    else
      echo "end\n";
  }
  }

class Bar extends Foo {
  public static function foo2($y) {
    echo "bar2\n";
    if ($y > 0)
      Foo::foo1($y);
    else
      echo "endbar\n";
  }
}

class Model extends Foo {
  public static function foo2($y) {
    echo "model2\n";
    if ($y > 0)
      static::foo1($y);
    else
      echo "endmodel\n";
  }
}

Model::foo2(3);

Bar::foo2(3);
?>

Le modèle se comporte dans un sens comme des objets OO standard avec des méthodes virtuelles, et Bar comme tu voulais:

$ ./test-force-super-binding.php | head -20
model2
foo1
model2
foo1
model2
foo1
model2
endmodel
bar2
foo1
foo2
foo2
foo2
foo2
foo2
foo2
foo2
foo2
foo2
foo2
$ 

(BTW, en utilisant parent:: au lieu de Foo:: ne nous amènerait pas à votre comportement recherché.)

Je ne comprends pas le but des spécifications de liaison insensées de PHP comme static::, qui n'ont un certain effet que pour les méthodes statiques (c'est-à-dire la programmation au niveau de la classe).

Un exemple C++ analogue ne donne pas de comportement au niveau de l'objet OO par défaut :


#include<iostream>

class Foo {
protected:
  static void foo1(int y) {
    std::cout << "foo1" << std::endl;
    foo2(y-1);
  }

public:
  static void foo2(int y) {
    std::cout << "foo2" << std::endl;
    if (y > 0)
      foo2(y);
    else
      std::cout << "end" << std::endl;
  }

};

class Bar: public Foo {
public:
  static void foo2(int y) {
    std::cout << "bar2" << std::endl;
    if (y > 0)
      foo1(y);
    else
      std::cout << "endbar" << std::endl;
  }
};

int main() {
  Bar::foo2(3);
  return 0;
}

-- cela donne votre comportement recherché :

$ ./a.out | head -10
bar2
foo1
foo2
foo2
foo2
foo2
foo2
foo2
foo2
foo2
$ 

même sans qualificatif spécial lors de l'appel de fonction dans le code de Bar::foo2(), donc pas intéressant pour nous.

Qu'en est-il des méthodes statiques de Java ?(Est-ce qu'ils diffèrent du C++ et nous donnent par défaut une résolution de type méthode virtuelle des appels de fonction ?)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top