Бесплатный курс по Clojure. Зарегистрируйтесь для отслеживания прогресса →

Clojure: О протоколах

Есть еще один вариант, для получения полиморфного поведения, используется он в основном, когда скорость полиморфных функций начинает бить по рукам (так бывает крайне редко), когда хочется избежать expression problem (возможность расширять набор операций над типами данных без изменений определения типов данных), либо необходимо выстроить высокоуровневые абстракции для сложных типов и операций над ними (главное не переусердствовать). Для создания протокола нам потребуется две функции — defprotocol, которая отвечает за генерацию протокола и defrecord, которая создает класс JVM.

Рассмотрим пример:

; Определим протокол, обратите внимание, что
; реализовывать протокол нет необходимости, достаточно определения
(defprotocol Fly
  (fly [this] "Method to fly"))

; Теперь создадим класс Bird, в котором определим поведение протокола
(defrecord Bird [name]
  Fly
  (fly [this] (str (:name this) " flies...")))

; Проверим, наследует ли запись протокол
(extends? Fly Bird)
; => true

; Создадим инстанс класса и свяжем его с идентификатором
(def crow (Bird. "Crow"))

; И вызовем метод, который определили
(fly crow)
; => "Crow flies..."

; Определим еще один класс со своим методом, для закрепления
(defrecord Plane [name]
  Fly
  (fly [this] (str (:name this) " flew away!")))

(def plane (Plane. "Hawker Hurricane"))

(fly plane)
; => "Hawker Hurricane flew away!"

Clojure действительно гибкий язык, в котором можно комбинировать различные подходы, однако некоторые из них (речь про протоколы), вносят усложнения в код, но при правильном применении можно получить абстракции, с которыми удобно работать. В случае с протоколами, выбор в их сторону должен быть обоснован, так как Clojure в целом пропагандирует подход к стремлению делать как можно проще (опять же, сам создатель языка выступал с докладом Simple Made Easy).

Задание

Создайте протокол SaysSomething, затем определите три класса Human, Dog и Cat (такой пример подобран специально :)). В каждом классе определите метод say-something с выводом в консоль (с помощью println) следующей строки:

  • Для Human Hello, World!
  • Для Cat Meow, World!
  • Для Dog Bark, World!

Поочередно создайте инстанс каждого класса и вызовите у них метод say-something.

Упражнение не проходит проверку — что делать? 😶

Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:

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

Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.

Мой код отличается от решения учителя 🤔

Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.

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

Прочитал урок — ничего не понятно 🙄

Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.

Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.

Полезное


Нашли ошибку? Есть что добавить? Пулреквесты приветствуются https://github.com/hexlet-basics
Если вы столкнулись с трудностями и не знаете, что делать, задайте вопрос в нашем большом и дружном сообществе