Как заставить вызывать только методы суперкласса, несмотря на то, что они были переопределены (в Ocaml) - PullRequest
4 голосов
/ 28 января 2011

Если честно, я мало что знаю об объектной системе OCaml.Следующий фрагмент иллюстрирует мою проблему:

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

При выполнении этот фрагмент выводит следующий вывод:

bar2
foo1
bar2
foo1
bar2
endbar

, показывая, что выполняется метод суперкласса foo1 (вызываемый через super # foo1)переопределенный метод foo2 из подкласса.Вместо этого я бы ожидал, что он вызовет метод foo2 из суперкласса, так как он вызывается через super.

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

Ответы [ 4 ]

4 голосов
/ 28 января 2011

Нет, это не так.Это «позднее связывание», базовая идея ОО, не специфичная для OCaml.

Я не знаю, что вы пытаетесь сделать, но объектно-ориентированное программирование, возможно, не правильные инструменты илипо крайней мере, не использовать таким образом.Если вы хотите изучать OCaml, вам, вероятно, следует сосредоточиться на деталях, не относящихся к OO, что достаточно интересно для начала.

1 голос
/ 28 января 2011

Как указывает gasche, это стандартное поведение для языков OO.

Вызов super#foo1, поскольку bar не переопределяет foo1, в точности эквивалентен o#foo1.Конструкция super существует только для возможности вызова метода foo2 для foo из методов в bar (в противном случае нет ссылки на этот метод).В foo1, вызванном из bar#foo2, o на самом деле bar, а не foo, поэтому вызов метода foo2 вызывает метод bar foo2.

1 голос
/ 28 января 2011

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

(«такое поведение», как я понял: если вызов foo2 изнутри кода, который был вызван как super#foo1, то должен вызываться именно foo2 из реализации суперкласса, а не из более «конкретные» реализации из подклассов)

Это самый простой и понятный и понятный способ продолжить: программные функции, которые делают то, что вы хотите.

Или вы должны объяснить себе и нам: зачем вам ООП? Причина этого не указана в тексте вопроса. Зачем создавать foo1 и foo2 методы, а не независимые функции? (Помимо foo1 и foo2, у вас могут быть некоторые объекты, классы и методы в вашей программе, где это уместно).

Интересно, возникает ли этот вопрос в сравнении с другими языками? 1016 * Если вы знаете другой язык ОО, странно, что вы хотите «такое поведение» от ООП: это не поведение, ожидаемое, скажем, в Java или C ++, потому что они используют концепцию таблицы виртуальных методов, которая связана с каждый объект во время выполнения, поэтому при вызове метода в вашей программе он отправляется во время выполнения для реализации этого метода, фактически связанного с объектом. Итак, вкратце: всякий раз, когда вы используете выражение вызова метода в своей программе, вы обязуетесь придерживаться этого принципа нахождения реализации метода («позднее связывание») , на что указывает gasche. Хотя все еще ср. различия между OCaml и языками, реализованными с помощью таблицы виртуальных методов, на которую указывает Ники Йошиучи. Формализация всего обсуждения доступных и желаемых поведений

Хотя то, что вам нужно, может не соответствовать ожидаемому и доступному поведению во многих популярных языках ОО, это возможно и может быть реализовано в некоторых конкретных системах ООП, если кто-то имеет доступ к внутренним компонентам реализации ООП.

Скажем, если в какой-то реализации, super - это структура, содержащая таблицу методов суперкласса (к которой можно вернуться при разрешении вызова метода для текущего объекта), а методы - это функции, которые должны получать объект ( таблица методов) в качестве 1-го аргумента, затем, чтобы выполнить то, что вы хотите, можно написать super.foo1(super, y).

(Мне действительно интересно, существуют ли реализации ООП, внутренние компоненты которых доступны программисту и которые позволяют выполнять такой вызов.)

Принимая во внимание, что обычное поведение ООП будет выражаться в этой системе как this.foo1(this, y) (где this - таблица методов для текущего объекта.)

Ваш вызов OCaml super#foo1 y или Java super.foo1(y); переводится в эту "мою" систему как super.foo1(this, y). (Хотя по-прежнему сравниваем различия, указанные Ники Йошиучи между OCaml и такими языками, как Java, реализованными с помощью таблицы виртуальных методов.)

Вы видите разницу между 3 случаями.

Приложение. Ищете языки, которые бы работали таким образом

Хм, PHP мог бы быть языком, где такое поведение было бы возможно, но:

  • только при программировании на уровне класса (статические методы), но не на уровне объекта;
  • только когда вы жестко кодируете какое-то странное «позднее статическое связывание» при вызовах функции:

#!/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);
?>

Модель ведет себя в некотором смысле как стандартные объекты OO с виртуальными методами, а Bar как вы хотели :

$ ./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
$ 

(Кстати, использование parent:: вместо Foo:: не приведет нас к желаемому поведению.)

Я не понимаю смысла безумных спецификаций связывания PHP, таких как static::, которые имеют некоторый эффект только для статических методов (то есть, программирования на уровне класса).

Аналогичный пример C ++ по умолчанию не дает поведения на уровне объектов OO:


#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;
}

- дает желаемое поведение:

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

даже без какого-либо специального спецификатора при вызове функции в коде для Bar::foo2(), поэтому нам это не интересно.

А как насчет статических методов Java? .. (Они отличаются от C ++ и дают намвиртуальное методоподобное разрешение вызовов функций по умолчанию?)

1 голос
/ 28 января 2011

Я не уверен на 100% в этом, но я уверен, что вы не можете.В OCaml наследование и подтипирование - это две разные концепции.Класс может быть подтипом другого типа без наследования.Все, что делает наследование, это извлекает методы из унаследованного класса.

Полиморфизм достигается посредством структурной типизации, поэтому ваш вызов foo2 вызывает метод в bar, потому что bar реализовал foo2 иНЕ потому, что bar наследуется от foo (как говорится, C ++, где bar#foo2 вызывается из-за поиска в таблице виртуальных функций).

Тем не менее, OCaml предоставляет вам способразличать переопределенные методы и унаследованные методы, используя синтаксис inherit...as...bar из вашего примера o#foo1 и super#foo1 - это один и тот же метод (поскольку bar не реализует foo1), тогда как o#foo2 и super#foo2 - это разные методы.Несмотря на это, я не думаю, что унаследованный класс в любом случае должен знать, что он унаследован, и различать его методы и переопределенные методы.Для этого может быть некоторый синтаксис, но я сильно сомневаюсь в этом из-за того, что наследование и полиморфизм являются независимыми понятиями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...