можно ли предотвратить переопределение метода путем понижения до суперкласса?

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

Предположим, у меня есть класс A, который определяет методы act и jump; метод act вызывает метод jump. Подкласс B A переопределяет метод jump (т. е. соответствующий синтаксис используется для гарантии того, что всякий раз, когда вызывается jump, используется реализация класса B).

У меня есть объект b класса B. Я хочу, чтобы он вел себя точно так же, как если бы он был класса A. Другими словами, я хочу, чтобы jump выполнялось с использованием реализации в A. Какие у меня варианты на разных языках?

Например, могу ли я добиться этого с помощью какой-либо формы понижения? Или, возможно, создав прокси-объект, который знает, какие методы вызывать?

Я бы хотел избежать создания совершенно нового объекта класса A и тщательной настройки совместного использования внутреннего состояния между a и b, потому что это явно не перспективно и сложно. Я также хотел бы избежать копирования состояния b в совершенно новый объект класса A, потому что может быть много данных для копирования.

ОБНОВИТЬ

Я задал этот вопрос конкретно о Python, но кажется, что этого невозможно добиться в Python, и технически это можно сделать... типа..

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


person max    schedule 16.01.2012    source источник
comment
Действительно — в Python даже не существует понятия кастинга. Можно взломать большую часть самоанализа, чтобы он работал, но я полагаю, что и в других динамических языках это невозможно.   -  person jsbueno    schedule 17.01.2012
comment
Делегируйте реализацию действия и перехода какому-то другому классу (возможно, Стратегии), а затем не беспокойтесь о B. Как правило, если ваш существующий дизайн заставляет вас искать способы обойти хороший объектно-ориентированный дизайн, пришло время сделать шаг назад и спросить Почему.   -  person Terry Wilcox    schedule 17.01.2012
comment
@TerryWilcox: но не может ли эта проблема возникнуть потенциально при любом наследовании с динамической привязкой? В этом случае я (потенциально) должен быть готов к рефакторингу любого кода от наследования к какому-то другому подходу всякий раз, когда возникает эта проблема. Это сделало бы перспективу использования полиморфизма очень непривлекательной. Что мне не хватает?   -  person max    schedule 17.01.2012
comment
Создание подклассов связано с изменением поведения. Если вы хотите, чтобы ваш экземпляр b действовал так, как если бы он был A, то он не должен быть B в первую очередь. Если бы я был в вашей ситуации, я бы признал, что наследование было плохим дизайном, и переключился бы на композицию. например, есть только A, который использует шаблон стратегии для реализации различных моделей поведения.   -  person Terry Wilcox    schedule 17.01.2012
comment
@TerryWilcox: так вы бы сказали, что маловероятно, что кому-то может понадобиться подавить полиморфизм; но если в моем приложении я вижу это как отдельную возможность, я должен вместо этого попытаться использовать шаблон стратегии, который строго более мощный, но требует немного более высоких затрат на кодирование?   -  person max    schedule 17.01.2012
comment
Вы не подавляете полиморфизм, вы подавляете наследование. И если вам нужно подавить его, вы не должны использовать его с самого начала. Предпочитать композицию наследованию — это хорошая философия дизайна. Наследование не всегда уместно, и попытка «взломать» лежащие в его основе ООП-механизмы, чтобы заставить проект работать, — серьезный предупреждающий знак.   -  person Terry Wilcox    schedule 17.01.2012
comment
@TerryWilcox, не могли бы вы поместить свои комментарии в ответ, пожалуйста?   -  person max    schedule 17.01.2012


Ответы (4)


Комментарии повторили: Предпочитайте композицию наследованию.

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

Состав обычно является лучшим решением. Делегирование различного поведения вашего объекта другому объекту (или объектам) может уменьшить или устранить потребность в подклассах.

В вашем случае поведенческие различия между классом A и классом B могут быть инкапсулированы в шаблон стратегии. Затем вы можете изменить поведение класса A (и класса B, если все еще требуется) на уровне экземпляра, просто назначив новую стратегию.

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

person Terry Wilcox    schedule 17.01.2012

То, что вы спрашиваете, совершенно не связано/не поддерживается программированием ООП.

Если вы создаете подкласс объекта A с классом B и переопределяете его методы, когда создается конкретный экземпляр B, с ним связаны все переопределенные/новые реализации базовых методов (будь то мы говорим о Java или C++ с виртуальными таблицами и т. д.) .

Вы создали экземпляр объекта B.
Почему вы ожидаете, что сможете/будете/должны иметь возможность вызывать метод суперкласса, если вы переопределили этот метод?

Конечно, вы можете назвать это явно, например. вызовом super внутри метода, но вы не можете сделать это автоматически, и кастинг вам в этом тоже не поможет.

Я не могу представить, зачем вам это нужно.
Если вам нужно использовать класс A, используйте класс A.
Если вам нужно переопределить его функциональность, используйте его подкласс B.

person Cratylus    schedule 16.01.2012
comment
Я не могу сказать вам, почему такой язык, как C++, поддерживает невиртуальные методы, но это так. - person toddsundsted; 17.01.2012
comment
virtual используется в C++ для объявления того, что функция может быть переопределена. Как только функция объявлена ​​virtual, метод подкласса запускается во время выполнения. полиморфизм не работает по умолчанию, как в Java. Но это не относится к вопросу ОП - person Cratylus; 17.01.2012
comment
@ user384706: да, мой вопрос в том, можно ли (и нужно ли) что-то сделать после того, как переопределение уже включено в определении класса (в случае C++ используется ключевое слово virtual). - person max; 17.01.2012

Большинство языков программирования прилагают некоторые усилия для поддержки динамической диспетчеризации виртуальных функций (случай вызова переопределенного метода jump в подклассе вместо реализации родительского класса) - до такой степени, что обойти или избежать этого сложно. В общем, специализация/полиморфизм является желательной функцией - возможно, в первую очередь целью ООП.

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

Таким образом, ответ на ваш вопрос: «Нет, поведение не одинаково на всех языках программирования». Кроме того, не существует независимого от языка решения. C++ может быть вашим лучшим выбором, если вам нужно такое поведение.

person toddsundsted    schedule 16.01.2012
comment
Без virtual полиформизм не работает в С++. Это не то, о чем спрашивает ОП. Это то же самое, что подкласс никогда не определял метод - person Cratylus; 17.01.2012
comment
Звучит точно так же, как OP запрашивает не виртуальный вызов метода (а-ля С++). - person toddsundsted; 17.01.2012
comment
Вы упускаете суть. Если метод не является виртуальным, это то же самое, что никогда не переопределяться. т.е. вызывается только метод класса A. Никогда не класса B. OP хочет вызвать метод класса B и когда-нибудь, если он захочет, также вызвать метод класса A. - person Cratylus; 17.01.2012
comment
Прошу прощения за путаницу. @ user384706: вы правы, я не хочу изменять определение класса. Даже если бы я мог заменить виртуальные методы на невиртуальные в классе A, это не сработает, поскольку тогда многие другие пользователи класса B, переданные как объект класса A, будут сильно удивлены, когда класс B начнет вести себя как класс A. - person max; 17.01.2012
comment
@max: Итак, у вас есть вариант использования? Это не теоретический вопрос? Что именно вы пытаетесь сделать? - person Cratylus; 17.01.2012
comment
Да, я не мог сделать этот вопрос более подробным, не сбивая всех с толку. Поэтому я задал здесь отдельный вопрос: stackoverflow.com/questions/8886568/ - person max; 17.01.2012

На самом деле вы можете сделать это с помощью Python (вроде) с некоторыми ужасными хаками. Это требует, чтобы вы реализовали что-то вроде оболочек, которые мы обсуждали в вашем первом вопросе о Python, но как подкласс B. Затем вам также необходимо реализовать проксирование записи (объект-оболочка не должен содержать никакого состояния, обычно связанного с иерархией классов, он должен перенаправлять весь доступ к атрибутам на базовый экземпляр B.

Но вместо того, чтобы перенаправлять поиск метода на A и затем вызывать метод с обернутым экземпляром, вы должны вызвать метод, передав объект-оболочку как self. Это допустимо, поскольку класс-оболочка является подклассом B, поэтому экземпляр оболочки является экземпляром классов, методы которых вы вызываете.

Это был бы очень странный код, требующий от вас динамического создания классов с использованием отношений IS-A и HAS-A одновременно. Вероятно, он также оказался бы довольно хрупким и имел бы причудливые результаты во многих крайних случаях (обычно вы не можете написать 100% идеальные классы-оболочки на Python именно потому, что возможны такие странные вещи).

Я полностью оставляю в стороне, хорошая это идея или нет.

person Ben    schedule 16.01.2012