В Ruby, что эквивалентно интерфейсу в C #? - PullRequest
21 голосов
/ 17 августа 2010

В настоящее время я пытаюсь изучить Ruby и пытаюсь понять, что он предлагает с точки зрения инкапсуляции и контрактов.

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

Есть лиспособ реализовать такие вещи в Ruby?

Спасибо

Простой пример того, что я имею в виду в C #:

interface IConsole
{
    int MaxControllers {get;}
    void PlayGame(IGame game);
}

class Xbox360 : IConsole
{
   public int MaxControllers
   {
      get { return 4; }
   }

   public void PlayGame(IGame game)
   {
       InsertDisc(game);
       NavigateToMenuItem();
       Click();
   }
}

class NES : IConsole
{
    public int MaxControllers
    {
        get { return 2; }
    }

   public void PlayGame(IGame game)
   {
       InsertCartridge(game);
       TurnOn();
   }
}

Ответы [ 6 ]

25 голосов
/ 17 августа 2010

В ruby ​​нет никаких интерфейсов, так как ruby ​​является динамически типизированным языком. Интерфейсы в основном используются для взаимозаменяемости различных классов без нарушения безопасности типов. Ваш код может работать с любой консолью, пока он ведет себя как консоль, которая в C # означает реализацию IConsole. «duck typing» - это ключевое слово, которое вы можете использовать, чтобы справиться с динамическими языками для решения подобных проблем.

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

15 голосов
/ 18 августа 2010

Ruby имеет Интерфейсы , как и любой другой язык.

Обратите внимание, что вы должны быть осторожны, чтобы не связать концепцию интерфейса , которая представляет собой абстрактную спецификацию обязанностей, гарантий и протоколов устройства с концепцией interface, которая является ключевое слово в языках программирования Java, C # и VB.NET. В Ruby мы все время используем первое, но второго просто не существует.

Очень важно различать два. Что важно, это Интерфейс , а не interface. interface говорит вам почти ничего полезного. Ничто не демонстрирует это лучше, чем маркерные интерфейсы в Java, которые являются интерфейсами, которые вообще не имеют членов: просто взгляните на java.io.Serializable и java.lang.Cloneable; эти два interface s означают очень разные вещи, но они имеют точно такую ​​же подпись.

Итак, если два interface, которые означают разные вещи, имеют одинаковую подпись, то что именно interface даже гарантирует вам?

Еще один хороший пример:

interface ICollection<T>: IEnumerable<T>, IEnumerable
{
    void Add(T item);
}

Что такое Интерфейс из System.Collections.Generic.ICollection<T>.Add?

  • что длина коллекции не уменьшается
  • что все предметы, которые были в коллекции прежде, все еще там
  • что item в коллекции

А что из этого на самом деле появляется в interface? Никто! В interface нет ничего, что говорит о том, что метод Add должен даже добавлять вообще, он может удалить элемент из коллекции.

Это совершенно правильная реализация interface:

class MyCollection<T>: ICollection<T>
{
    void Add(T item)
    {
        Remove(item);
    }
}

Другой пример: где в java.util.Set<E> действительно сказано, что это, знаете ли, set ? Нигде! Точнее, в документации. На английском.

Практически во всех случаях interfaces, как из Java, так и .NET, вся релевантная информация фактически находится в документах, а не в типах. Итак, если типы не говорят вам ничего интересного, зачем вообще их хранить? Почему бы не придерживаться только документации? И это именно то, что делает Руби.

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

Итак, вкратце: у Ruby нет эквивалента Java interface. Однако имеет эквивалент Java Interface и точно такой же, как в Java: документация.

Также, как и в Java, Приемочные тесты можно использовать для указания Интерфейс с.

В частности, в Ruby интерфейс объекта определяется тем, что он может делать , а не тем, чем является class, или тем, что module он смешивает в Любой объект, который имеет метод <<, может быть добавлен. Это очень полезно в модульных тестах, где вы можете просто передать Array или String вместо более сложного Logger, даже если Array и Logger не разделяют явное interface друг от друга от того, что у них обоих есть метод с именем <<.

Другим примером является StringIO, который реализует тот же интерфейс , что и IO, и, таким образом, большую часть интерфейса из File, но без общего предка, кроме Object.

5 голосов
/ 17 августа 2010

Интерфейсы обычно вводятся в статически типизированные языки ОО, чтобы восполнить отсутствие множественного наследования.Другими словами, они являются скорее необходимым злом, чем чем-то полезным per se .

Ruby, с другой стороны:

  1. Является динамически типизированным языком с"duck typing", поэтому, если вы хотите вызвать метод foo для двух объектов, им не нужно ни наследовать один и тот же класс предков, ни реализовывать один и тот же интерфейс.
  2. Поддерживает множественное наследование через концепцию mixinsопять же, здесь не нужны интерфейсы.
4 голосов
/ 17 августа 2010

У Руби на самом деле их нет;интерфейсы и контракты обычно живут больше в статическом мире, чем в динамическом.

Существует гем под названием Рукопожатие , который может реализовывать неофициальные контракты, если вам это действительно нужно.

0 голосов
/ 30 мая 2016

У Йорга есть хорошая точка зрения, у ruby ​​есть интерфейсы, но не ключевое слово.Читая некоторые ответы, я думаю, что это негатив в динамических языках.Вместо применения интерфейса через язык, вы должны создавать модульные тесты, вместо того, чтобы не реализовывать методы перехвата компилятора.Это также усложняет понимание метода понимания, так как вам приходится выслеживать, что такое объект, когда вы пытаетесь вызвать его.

Взять в качестве примера:

def my_func(options)
  ...
end

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

Хотя есть один плюс, и это то, что динамические языки программирования БЫСТРО для написания фрагмента кода.Мне не нужно писать какое-либо объявление интерфейса, и позже я могу использовать новые методы и параметры, не обращаясь к интерфейсу, чтобы раскрыть его.Компромисс - скорость обслуживания.

0 голосов
/ 17 августа 2010

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

http://www.amazon.com/Design-Patterns-Ruby-Russ-Olsen/dp/0321490452

...