Есть еще один вариант, для получения полиморфного поведения, используется он в основном, когда скорость полиморфных функций начинает бить по рукам (так бывает крайне редко), когда хочется избежать 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
) следующей строки:
Hello, World!
Meow, World!
Bark, World!
Поочередно создайте инстанс каждого класса и вызовите у них метод say-something
.
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.
Ваше упражнение проверяется по этим тестам
1(ns protocols-test
2 (:require [test-helper :refer [assert-output]]))
3
4(assert-output "Hello, World!\nMeow, World!\nBark, World!")
5
Решение учителя откроется через: