Можем ли мы предоставить интерфейсы в Ruby, как мы это делаем в java, и заставить модули или классы Ruby реализовать методы, определенные интерфейсом.
Один из способов - использовать наследование и method_missing для достижения того же, но есть ли другой более подходящий подход?
Ответы:
У Ruby есть интерфейсы, как и у любого другого языка.
Обратите внимание, что вы должны быть осторожны, чтобы не объединить концепцию интерфейса , которая является абстрактной спецификацией обязанностей, гарантий и протоколов модуля, с концепцией,
interface
которая является ключевым словом в программировании на Java, C # и VB.NET. языков. В Ruby мы все время используем первое, а второго просто не существует.Это очень важно различать. Важен интерфейс , а не файл
interface
.interface
Не говорит вам , довольно много ничего полезного. Ничто не демонстрирует это лучше, чем интерфейсы маркеров в Java, которые являются интерфейсами, не имеющими вообще никаких членов: просто взгляните наjava.io.Serializable
иjava.lang.Cloneable
; эти дваinterface
символа означают очень разные вещи, но имеют одинаковую подпись.Так, если два
interface
s , что означают разные вещи, имеющие ту же сигнатуру, то , что именно этоinterface
даже гарантирует вам?Еще один хороший пример:
Что такое интерфейс из
java.util.List<E>.add
?element
есть в коллекцииИ какой из них на самом деле появляется в
interface
? Никто! В элементе нет ничего,interface
что говорило бы о том, чтоAdd
метод должен вообще добавлять , он может с таким же успехом удалить элемент из коллекции.Это вполне допустимая реализация этого
interface
:Другой пример: где на
java.util.Set<E>
самом деле говорится, что это, знаете ли, набор ? Никуда! А точнее в документации. По-английски.Практически во всех случаях
interfaces
, как на Java, так и на .NET, вся соответствующая информация находится в документации, а не в типах. Итак, если типы все равно ничего интересного вам не говорят, зачем их вообще оставлять? Почему бы не придерживаться только документации? И это именно то, что делает Руби.Обратите внимание, что есть и другие языки, на которых интерфейс может быть описан содержательно. Однако эти языки обычно не вызывают конструкцию, описывающую интерфейс "
interface
", они называют ееtype
. В языке программирования с зависимой типизацией вы можете, например, выразить свойства, которыеsort
функция возвращает коллекцию той же длины, что и оригинал, что каждый элемент, который находится в оригинале, также находится в отсортированной коллекции и что нет большего элемента появляется перед меньшим элементом.Итак, вкратце: Ruby не имеет эквивалента Java
interface
. Однако у него есть эквивалент Java интерфейса , и он точно такой же, как в документации Java :.Также, как и в Java, приемочные тесты можно использовать для указания интерфейсов. .
В частности, в Ruby интерфейс объекта определяется тем, что он может делать , а не тем, что
class
есть или чтоmodule
он смешивает. К любому объекту, имеющему<<
метод, можно присоединить. Это очень полезно в модульных тестах, где вы можете просто передатьArray
илиString
вместо более сложногоLogger
, хотяArray
иLogger
не делиться явным,interface
кроме того факта, что у них обоих есть вызываемый метод<<
.Другой пример может служить
StringIO
, который реализует тот же интерфейс , какIO
и , следовательно , большая часть интерфейса изFile
, но без какого - либо совместного общего предка , кромеObject
.источник
interface
она бесполезна, без смысла ее использования. Было бы проще сказать, что Ruby динамически типизирован и имеет другой фокус и делает такие концепции, как IOC, ненужными / нежелательными. Это тяжелый сдвиг, если вы привыкли к дизайну по контракту. Кое-что может принести пользу Rails, что основная команда осознала, как вы можете видеть в последних версиях.interface
может не предоставлять всю необходимую информацию, но дает очевидное место для размещения документации. Я написал класс на Ruby, который реализует (достаточное количество) ввода-вывода, но я сделал это методом проб и ошибок и был не очень доволен процессом. Я также написал несколько реализаций собственного интерфейса, но документирование того, какие методы требуются и что они должны делать, чтобы другие члены моей команды могли создавать реализации, оказалось сложной задачей.interface
конструкция действительно нужна только для обработки разных типов как одного и того же в статически типизированных языках с одним наследованием (например, рассматриватьLinkedHashSet
иArrayList
оба как aCollection
), она практически не имеет ничего общего с интерфейсом, как показывает этот ответ. Ruby не имеет статической типизации, поэтому в конструкции нет необходимости .Попробуйте "общие примеры" rspec:
https://www.relishapp.com/rspec/rspec-core/v/3-5/docs/example-groups/shared-examples
Вы пишете спецификацию для своего интерфейса, а затем помещаете одну строку в спецификацию каждого разработчика, например.
Полный пример:
Обновление : восемь лет спустя (2020) ruby теперь поддерживает статически типизированные интерфейсы через sorbet. См. Абстрактные классы и интерфейсы в документации по сорбету.
источник
Ruby не имеет такой функциональности. В принципе, они ему не нужны, поскольку Ruby использует так называемую утиную типизацию .
Есть несколько подходов, которые вы можете использовать.
Напишите реализации, вызывающие исключения; если подкласс пытается использовать нереализованный метод, он потерпит неудачу
Наряду с вышеизложенным, вы должны написать тестовый код, который обеспечивает выполнение ваших контрактов (какой другой пост здесь неправильно называет Интерфейс )
Если вы все время пишете методы void, как указано выше, напишите вспомогательный модуль, который фиксирует это
Теперь объедините вышеперечисленное с модулями Ruby, и вы близки к тому, что хотите ...
И тогда вы можете сделать
Позвольте мне еще раз подчеркнуть: это элементарная задача, поскольку все в Ruby происходит во время выполнения; нет проверки времени компиляции. Если вы объедините это с тестированием, вы сможете выявить ошибки. Более того, если вы пойдете дальше, вы, вероятно, сможете написать интерфейс, который выполняет проверку класса в первый раз, когда объект этого класса создается; сделать ваши тесты такими же простыми, как вызов
MyCollection.new
... да, сверх меры :)источник
Как все здесь сказали, для рубина нет интерфейсной системы. Но с помощью самоанализа вы можете довольно легко реализовать это самостоятельно. Вот простой пример, который можно улучшить разными способами, чтобы помочь вам начать работу:
Удаление одного из методов, объявленных для Person, или изменение его количества аргументов приведет к возникновению файла
NotImplementedError
.источник
В Java не существует таких вещей, как интерфейсы. Но есть и другие вещи, которыми вы можете наслаждаться в рубине.
Если вы хотите реализовать какие-то типы и интерфейс - чтобы можно было проверить объекты, есть ли у них какие-то методы / сообщения, которые вам требуются, - вы можете взглянуть на rubycontracts . Он определяет механизм, аналогичный PyProtocols . Блог о проверке типов в Ruby находится здесь .
Упомянутые подходы не являются живыми проектами, хотя цель поначалу кажется хорошей, кажется, что большинство разработчиков Ruby могут жить без строгой проверки типов. Но гибкость ruby позволяет реализовать проверку типов.
Если вы хотите расширить объекты или классы (то же самое в ruby) определенным поведением или иметь какой-то рубиновый способ множественного наследования, используйте механизм
include
илиextend
. Сinclude
его помощью вы можете включать в объект методы из другого класса или модуля. С участиемextend
вы можете добавить поведение к классу, чтобы его экземпляры имели добавленные методы. Однако это было очень короткое объяснение.Я считаю, что лучший способ решить потребность в интерфейсе Java - это понять объектную модель Ruby (например, см. Лекции Дэйва Томаса ). Возможно, вы забудете об интерфейсах Java. Или у вас есть исключительное приложение в вашем расписании.
источник
Как показывают многие ответы, в Ruby нет способа заставить класс реализовать определенный метод путем наследования от класса, включая модуль или что-то подобное. Причиной этого, вероятно, является преобладание TDD в сообществе Ruby, который представляет собой другой способ определения интерфейса - тесты не только определяют сигнатуры методов, но и поведение. Таким образом, если вы хотите реализовать другой класс, реализующий уже определенный интерфейс, вы должны убедиться, что все тесты проходят.
Обычно тесты определяются изолированно с использованием имитаторов и заглушек. Но есть также такие инструменты, как Bogus , позволяющие определять тесты контрактов. Такие тесты не только определяют поведение «основного» класса, но также проверяют, существуют ли заглушенные методы в взаимодействующих классах.
Если вас действительно интересуют интерфейсы в Ruby, я бы рекомендовал использовать среду тестирования, которая реализует тестирование контрактов.
источник
Все примеры здесь интересны, но в них отсутствует проверка контракта интерфейса, я имею в виду, если вы хотите, чтобы ваш объект реализовал все определения методов интерфейса, и только эти, вы не можете. Поэтому я предлагаю вам простой простой пример (который наверняка можно улучшить), чтобы убедиться, что у вас есть именно то, что вы ожидаете получить через свой интерфейс (контракт).
рассмотрите свой интерфейс с помощью таких определенных методов
Затем вы можете написать объект хотя бы с контрактом интерфейса:
Вы можете безопасно вызывать свой объект через свой интерфейс, чтобы убедиться, что вы в точности то, что определяет интерфейс.
И вы также можете убедиться, что ваш объект реализует все ваши определения методов интерфейса
источник
Я немного расширил ответ Карлосаяма для моих дополнительных потребностей. Это добавляет пару дополнительных принудительных мер и параметров к классу Interface:
required_variable
иoptional_variable
который поддерживает значение по умолчанию.Я не уверен, что вы захотите использовать это метапрограммирование с чем-то слишком большим.
Как указывалось в других ответах, вам лучше всего писать тесты, которые правильно обеспечивают соблюдение того, что вы ищете, особенно если вы хотите начать принудительное соблюдение параметров и возвращаемых значений.
Обратите внимание: этот метод вызывает ошибку только при вызове кода. Перед запуском все равно потребуются тесты для надлежащего применения.
Пример кода
interface.rb
plugin.rb
Я использовал одноэлементную библиотеку для данного шаблона, который использую. Таким образом, любые подклассы наследуют одиночную библиотеку при реализации этого «интерфейса».
my_plugin.rb
Для моих нужд это требует, чтобы класс, реализующий «интерфейс», был подклассом.
источник
Сам Ruby не имеет точного эквивалента интерфейсов в Java.
Однако, поскольку такой интерфейс иногда может быть очень полезным, я сам разработал гем для Ruby, который очень просто имитирует интерфейсы Java.
Это называется
class_interface
.Работает довольно просто. Сначала установите гем
gem install class_interface
или добавьте его в свой Gemfile и rundbundle install
.Определение интерфейса:
Реализация этого интерфейса:
Если вы не реализуете определенную константу или метод или номер параметра не совпадает, перед выполнением программы Ruby возникнет соответствующая ошибка. Вы даже можете определить тип констант, назначив тип в интерфейсе. Если nil, разрешен любой тип.
Метод «реализует» должен вызываться в последней строке класса, потому что это позиция кода, где уже проверены реализованные выше методы.
Подробнее на: https://github.com/magynhard/class_interface
источник
Я понял, что слишком часто использую шаблон «Не реализованная ошибка» для проверок безопасности объектов, для которых мне нужно определенное поведение. Закончил написанием гема, который в основном позволяет использовать такой интерфейс:
Он не проверяет аргументы метода. Это соответствует версии0.2.0
. Более подробный пример на https://github.com/bluegod/rintисточник