Я знаю, что это кажется странным вопросом, поскольку смысл в том, что два или более объектов совместно используют один и тот же класс, состоит в том, что их поведение одинаково, то есть их методы идентичны.
Однако мне любопытно, существуют ли какие-либо ООП-языки, которые позволяют вам переопределять методы объектов таким же образом, как вы можете назначать разные значения для их полей. Результатом будут объекты, построенные из одного и того же класса, уже не демонстрирующие одинаковое поведение.
Если я не ошибаюсь, вы можете сделать этот JavaScript? Наряду с этим вопросом я спрашиваю: зачем кому-то это делать?
object-oriented
programming-languages
Нико Беллик
источник
источник
setClickHandler()
метод и заставить разные экземпляры одного и того же класса делать совершенно разные вещи. В языках, в которых нет удобных лямбда-выражений, проще создать новый анонимный подкласс только для нового обработчика. Традиционно, переопределение методов считалось отличительной чертой нового класса, в то время как установка значений атрибутов не была, но особенно с функциями-обработчиками, эти два эффекта очень похожи, поэтому различие становится войной за слова.Ответы:
Методы в большинстве языков ООП (на основе классов) фиксируются по типу.
JavaScript основан на прототипах, а не на классах, и поэтому вы можете переопределять методы для каждого экземпляра, потому что нет четкого различия между «классом» и объектом; в действительности «класс» в JavaScript - это объект, который походит на шаблон для того, как экземпляры должны работать.
Любой язык, который допускает первоклассные функции, Scala, Java 8, C # (через делегаты) и т. Д., Может действовать так, как если бы вы переопределяли методы для каждого экземпляра; вам нужно будет определить поле с типом функции, а затем переопределить его в каждом экземпляре.
Скала имеет другую возможность; В Scala вы можете создавать синглеты объектов (используя ключевое слово object вместо ключевого слова class), поэтому вы можете расширить свой класс и переопределить методы, что приведет к созданию нового экземпляра этого базового класса с переопределениями.
Зачем кому-то это делать? Там могут быть десятки причин. Может случиться так, что поведение должно быть более четко определено, чем использование различных комбинаций полей. Кроме того, код может быть лучше отделен и организован. Однако в целом я считаю эти случаи более редкими, и зачастую существует более простое решение с использованием значений полей.
источник
Трудно угадать мотивацию вашего вопроса, и поэтому некоторые возможные ответы могут или не могут соответствовать вашим реальным интересам.
Даже в некоторых непрототипных языках этот эффект можно аппроксимировать.
Например, в Java анонимный внутренний класс очень близок к тому, что вы описываете - вы можете создать и создать экземпляр подкласса оригинала, переопределяя только тот метод или методы, которые вы хотите. Полученный класс будет
instanceof
исходным классом, но не будет тем же классом.Относительно того, почему вы хотите это сделать? Я думаю, что с лямбда-выражениями Java 8 многие из лучших вариантов использования уходят. По крайней мере, в более ранних версиях Java это может избежать распространения тривиальных, узко используемых классов. То есть, когда у вас есть большое количество связанных вариантов использования, отличающихся лишь незначительным функциональным способом, вы можете создавать их практически на лету (почти), с поведенческими различиями, вводимыми в то время, когда вам это нужно.
Тем не менее, даже до J8, это часто может быть реорганизовано, чтобы сдвинуть разницу в поле или три, и вставить их в конструктор. С J8, конечно, сам метод может быть введен в класс, хотя может возникнуть соблазн сделать это, когда другой рефакторинг может быть чище (если не так круто).
источник
Вы запросили любой язык, который предоставляет методы для каждого экземпляра. Ответ на Javascript уже есть, поэтому давайте посмотрим, как это делается в Common Lisp, где вы можете использовать EQL-специализаторы:
Почему?
Специалисты по EQL полезны, когда предполагается, что аргумент, подлежащий диспетчеризации, имеет тип, для которого
eql
имеет смысл: число, символ и т. Д. Вообще говоря, он вам не нужен, и вам просто нужно определить столько подклассов, сколько нужна ваша проблема. Но иногда вам нужно только выполнить диспетчеризацию в соответствии с параметром, который является, например, символом:case
выражение будет ограничено известными случаями в функции диспетчеризации, тогда как методы можно добавлять и удалять в любое время.Кроме того, специализация на экземплярах полезна для целей отладки, когда вы хотите временно проверить, что происходит с конкретным объектом в вашем запущенном приложении.
источник
Вы также можете сделать это в Ruby, используя одноэлементные объекты:
Производит:
Что касается использования, то именно так Ruby выполняет методы класса и модуля. Например:
Фактически определяет одноэлементный метод
hello
наClass
объектеSomeClass
.источник
extend
самом деле просто создает класс singleton для объекта, а затем импортирует модуль в класс singleton.Вы можете думать о методах для каждого экземпляра как о том, что они позволяют вам собирать свой собственный класс во время выполнения. Это может устранить много склеивающего кода, того кода, который не имеет никакой другой цели, кроме как соединить два класса, чтобы общаться друг с другом. Миксины - это несколько более структурированное решение проблем такого же типа.
Вы немного страдаете от парадокса blub , по сути, из-за того , что трудно увидеть значение языковой функции, пока вы не используете эту функцию в реальной программе. Так что ищите возможности, где вы думаете, что это может работать, попробуйте и посмотрите, что произойдет.
Посмотрите в своем коде группы классов, которые отличаются только одним методом. Ищите классы, единственной целью которых является объединение двух других классов в разных комбинациях. Ищите методы, которые ничего не делают, кроме как передать вызов другому объекту. Ищите классы, которые создаются с использованием сложных шаблонов создания . Это все потенциальные кандидаты, которые должны быть заменены методами для каждого экземпляра.
источник
Другие ответы показали, как это является общей особенностью динамических объектно-ориентированных языков, и как это можно тривиально эмулировать в статическом языке, который имеет функциональные объекты первого класса (например, делегаты в c #, объекты, которые переопределяют operator () в c ++) , В статических языках, в которых такая функция отсутствует, это сложнее, но все же может быть достигнуто путем использования комбинации шаблона стратегии и метода, который просто делегирует его реализацию стратегии. По сути, это то же самое, что вы делаете в c # с делегатами, но синтаксис немного сложнее.
источник
Вы можете сделать что-то подобное в C # и большинстве других подобных языков.
источник
Концептуально, даже если в языке, подобном Java, все экземпляры класса должны иметь одинаковые методы, можно сделать так, чтобы они выглядели так, как будто их нет, добавив дополнительный уровень косвенности, возможно, в сочетании с вложенными классами.
Например, если класс
Foo
может определять статический абстрактный вложенный класс,QuackerBase
который содержит методquack(Foo)
, а также несколько других статических вложенных классов, производных отQuackerBase
каждого, каждый со своим собственным определениемquack(Foo)
, тогда, если внешний класс имеет полеquacker
типаQuackerBase
, то он может установите это поле для идентификации (возможно, одноэлементного) экземпляра любого из его вложенных классов. После того, как это было сделано так, ссылающеесяquacker.quack(this)
выполнитquack
метод класса , чей экземпляр был присвоен этой области.Поскольку это довольно распространенный шаблон, Java включает в себя механизмы для автоматического объявления соответствующих типов. Такие механизмы на самом деле не делают ничего, что нельзя было бы сделать, просто используя виртуальные методы и необязательно вложенные статические классы, но они значительно сокращают количество шаблонов, необходимых для создания класса, единственной целью которого является запуск одного метода от имени другой класс.
источник
Я считаю, что это определение «динамического» языка, такого как ruby, groovy & Javascript (и многие другие). Динамический относится (по крайней мере частично) к способности динамически определять, как экземпляр класса может вести себя на лету.
Это не очень хорошая практика ОО в целом, но для многих программистов с динамическим языком принципы ОО не являются их главным приоритетом.
Это упрощает некоторые хитрые операции, такие как Monkey-Patching, где вы можете настроить экземпляр класса, чтобы позволить вам взаимодействовать с закрытой библиотекой так, как они этого не ожидали.
источник
Я не говорю, что это хорошо, но в Python это возможно. Я не могу придумать хороший пример использования, но я уверен, что они существуют.
источник