В статических языках, таких как Java, вам нужны интерфейсы, потому что иначе система типов просто не позволит вам делать определенные вещи. Но в динамических языках, таких как PHP и Python, вы просто используете утиную печать .
PHP поддерживает интерфейсы. Ruby и Python их не имеют. Таким образом, вы можете спокойно жить без них.
Я в основном делал свою работу на PHP и никогда не использовал возможность определять интерфейсы. Когда мне нужен набор классов для реализации определенного общего интерфейса, я просто опишу его в документации.
Так что ты думаешь? Не лучше ли вам без использования интерфейсов в динамических языках?
Я думаю об этом больше как о удобстве. Если у вас есть функция, которая берет «файл-подобный» объект и вызывает только метод read (), то неудобно – даже ограничение – заставить пользователя реализовать какой-то интерфейс File. Также легко проверить, имеет ли объект метод чтения.
Но если ваша функция ожидает большой набор методов, проще проверить, поддерживает ли объект интерфейс, а затем проверять поддержку каждого отдельного метода.
Да, есть точка
Если вы явно не используете интерфейсы, ваш код по-прежнему использует объект, как если бы он реализовал определенные методы, просто неясно, что такое невысказанный интерфейс.
Если вы определите функцию для принятия интерфейса (скажем, в PHP), то это произойдет раньше, и проблема будет связана с вызывающим не с методом, выполняющим работу. Обычно неудача раньше – хорошее правильное правило.
Интерфейсы фактически добавляют некоторую степень динамической языковой гибкости к статическим языкам, которые имеют их, например Java. Они предлагают способ запроса объекта, для которого он реализуется во время выполнения .
Эта концепция хорошо переносится на динамические языки. В зависимости от вашего определения слова «динамический», конечно, это даже включает Objective-C, который довольно широко использует протоколы в Cocoa.
В Ruby вы можете спросить, отвечает ли объект имени метода. Но это довольно слабая гарантия того, что он будет делать то, что вы хотите, особенно учитывая, как несколько слов используются снова и снова, что полная подпись метода не учитывается и т. Д.
В Ruby я мог бы спросить
object.respond_to? :sync
Итак, да, у него есть метод под названием «sync», что бы это ни значило.
В Objective-C я мог бы спросить что-то подобное, т. Е. Выглядит ли это look / walk / quack как-то, что синхронизируется? »:
[myObject respondsToSelector:@selector(sync)]
Еще лучше, ценой некоторой многословности, я могу спросить что-то более конкретное, т. Е. «Выглядит ли это / walk / quack как что-то, что синхронизируется с MobileMe?»:
[myObject respondsToSelector:@selector(sync:withMobileMeAccount:)]
Это утка, типичная для вида.
Но чтобы действительно спросить объект, обещает ли он реализовать синхронизацию с MobileMe …
[receiver conformsToProtocol:@protocol(MobileMeSynchronization)]
Конечно, вы можете реализовать протоколы, просто проверяя наличие селекторов, которые вы считаете определением протокола / утки, и если они достаточно конкретны. В какой момент протокол является просто аббревиатурой для большого куска уродливых ответов? запросов и очень полезного синтаксического сахара для использования компилятора / IDE.
Интерфейсы / протоколы – это еще одно измерение метаданных объектов, которые могут использоваться для реализации динамического поведения при обработке этих объектов. В Java компилятор просто требует такого рода вещей для обычного вызова метода. Но даже динамические языки, такие как Ruby, Python, Perl и т. Д., Реализуют понятие типа, которое выходит за рамки только «каких методов реагирует объект». Следовательно, ключевое слово class. Javascript является единственным действительно часто используемым языком без этой концепции. Если у вас есть классы, то интерфейсы также имеют смысл.
Это, по общему признанию, более полезно для более сложных библиотек или иерархий классов, чем в большинстве прикладных программ, но я думаю, что эта концепция полезна на любом языке.
Кроме того, кто-то еще упоминал миксины. Ruby mixins – это способ совместного использования кода – например, они относятся к реализации класса. Интерфейсы / протоколы относятся к интерфейсу класса или объекта. Они могут фактически дополнять друг друга. У вас может быть интерфейс, который определяет поведение и один или несколько микшинов, которые помогают объекту реализовать это поведение.
Конечно, я не могу думать о каких-либо языках, которые действительно имеют как отличные первоклассные языковые функции. У тех, у кого есть миксины, включая mixin, обычно подразумевается интерфейс, который он реализует.
Если у вас нет ограничений безопасности (поэтому никто не будет обращаться к вашим данным так, как вы этого не хотите), и у вас есть хорошая документация или хорошо обученные кодеры (поэтому им не нужен интерпретатор / компилятор, чтобы сообщить им, что do), то нет, это бесполезно.
Для большинства проектов среднего размера, утиная печать – это все, что вам нужно.
У меня создалось впечатление, что у Python нет интерфейсов . Насколько я знаю в Python, вы не можете применять метод, который должен быть реализован во время компиляции именно потому, что он является динамическим языком.
Есть библиотеки интерфейсов для Python, но я не использовал их.
У Python также есть Mixins, поэтому вы могли бы создать класс интерфейса, указав Mixin, имеющий pass
для каждой реализации метода, но это не дает вам большой ценности.
Я думаю, что использование интерфейсов определяется больше тем, сколько людей будет использовать вашу библиотеку. Если это только вы, или небольшая команда, тогда документация и соглашение будут прекрасными, а интерфейсы будут препятствием. Если это публичная библиотека, то интерфейсы намного полезнее, потому что они ограничивают людей предоставлением правильных методов, а не просто намеком. Таким образом, интерфейсы, безусловно, являются ценной функцией для написания публичных библиотек, и я полагаю, что недостаток (или, по крайней мере, де-акцент) является одной из многих причин, по которым динамические языки больше используются для приложений, а сильно типизированные языки используются для больших библиотек.
Рене, пожалуйста, прочитайте мой ответ на вопрос «Лучшая практика для архивирования больших систем в динамическом языке» здесь, в StackOverflow. Я обсуждаю некоторые преимущества предоставления свободы динамических языков для экономии усилий на развитие и облегчения внедрения новых программистов в проект. Интерфейсы при правильном использовании значительно способствуют написанию надежного программного обеспечения.
На языке, таком как PHP, где вызов метода, который не существует, приводит к фатальной ошибке и приводит к отключению всего приложения, тогда да интерфейс имеет смысл.
На языке, таком как Python, где вы можете поймать и обрабатывать недействительные вызовы методов, это не так.
Python 3000 будет иметь базовые базовые классы . Стоит прочитать.
Одно использование Java-интерфейса – это позволить строго типизированные mixins в Java. Вы смешиваете правильный суперкласс и любые дополнительные методы, реализованные для поддержки интерфейса.
Python имеет множественное наследование, поэтому на самом деле не нужно использовать интерфейс, чтобы позволить использовать методы из нескольких суперклассов.
Я, однако, как некоторые из преимуществ сильной типизации – в первую очередь, я поклонник раннего обнаружения ошибок. Я пытаюсь использовать абстрактное определение суперкласса, похожее на интерфейс.
class InterfaceLikeThing( object ): def __init__( self, arg ): self.attr= None self.otherAttr= arg def aMethod( self ): raise NotImplementedError def anotherMethod( self ): return NotImplemented
Это формализует интерфейс – в некотором роде. Он не дает абсолютных доказательств того, что подкласс соответствует ожиданиям. Однако, если подкласс не реализует требуемый метод, мои юнит-тесты потерпят неудачу с очевидным значением NotImplemented
return или NotImplementedError
.
Ну, во-первых, правильно, что Ruby не имеет интерфейса как есть, но у них есть mixin, который берет как-то лучшее из обоих интерфейсов и абстрактных классов с других языков.
Основная цель интерфейса – обеспечить, чтобы ваш объект выполнял ВСЕ методы, присутствующие в самом интерфейсе.
Конечно, интерфейс никогда не является обязательным, даже в Java, который вы могли бы представить, чтобы работать только с классами и использовать методы отражения для вызова, когда вы не знаете, какой тип объекта вы манипулируете, но он подвержен ошибкам и должен быть обескуражен много способов.
Ну, конечно, было бы легче проверить, поддерживает ли данный объект весь интерфейс, а не просто не сбой, когда вы вызываете один или два метода, которые вы используете в исходном методе, например, чтобы добавить объект во внутренний список.
Утиная печать имеет некоторые преимущества интерфейсов, то есть их легко использовать везде, но механизм обнаружения все еще отсутствует.
Это похоже на то, что вам не нужны явные типы на динамически типизированном языке. Почему бы вам не сделать все «вар» и документировать свои типы в другом месте?
Это ограничение наложено программистом программистом. Это затрудняет вам стрелять в ногу; дает вам меньше места для ошибок.
как программист PHP, как я его вижу, интерфейс в основном используется в качестве контракта. Это позволяет вам сказать, что все, что использует этот интерфейс, ДОЛЖНО реализовать данный набор функций.
Я не знаю, все ли это полезно, но я нашел это немного камнем преткновения, пытаясь понять, что такое интерфейсы.
Если вы считаете, что вам нужно, вы можете реализовать какой-то интерфейс с функцией, которая сравнивает методы / атрибуты объекта с данной сигнатурой. Вот очень простой пример:
file_interface = ('read', 'readline', 'seek') class InterfaceException(Exception): pass def implements_interface(obj, interface): d = dir(obj) for item in interface: if item not in d: raise InterfaceException("%s not implemented." % item) return True >>> import StringIO >>> s = StringIO.StringIO() >>> implements_interface(s, file_interface) True >>> >>> fp = open('/tmp/123456.temp', 'a') >>> implements_interface(fp, file_interface) True >>> fp.close() >>> >>> d = {} >>> implements_interface(d, file_interface) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in implements_interface __main__.InterfaceException: read not implemented.
Конечно, это не гарантирует очень многое.
В дополнение к другим ответам я просто хочу указать, что Javascript имеет ключевое слово instanceof, которое вернет true, если данный экземпляр находится где угодно в цепочке прототипов данного объекта.
Это означает, что если вы используете свой «объект интерфейса» в цепочке прототипов для ваших «объектов реализации» (оба являются просто объектами JS), вы можете использовать instanceof, чтобы определить, «он» реализует его. Это не помогает аспекту правоприменения, но это помогает в аспекте полиморфизма – это одно общее применение для интерфейсов.
Ссылка MDN instanceof
Не пытайтесь писать Java на динамическом языке.