Этот вопрос стал действительно длинным; Я приветствую комментарии, предлагающие лучшие форумы по этому вопросу.
Я моделирую поведение роящихся птиц . Чтобы помочь мне организовать свои мысли, я создал три протокола, представляющих основные концепции предметной области, которые я видел: Boid
, Flock
(набор boids) и Vector
.
Когда я больше думал об этом, я понял, что создаю новые типы для представления Boid
и Flock
, когда их можно очень четко смоделировать с помощью специальных карт: boid - это простая карта положения и скорости. (оба вектора), и стая представляет собой набор карт boid. Чистые, лаконичные, простые и исключенные мои пользовательские типы в пользу всей мощи карт и clojure.spec
.
(s/def ::position ::v/vector)
(s/def ::velocity ::v/vector)
(s/def ::boid (s/keys ::position
::velocity))
(s/def ::boids (s/coll-of ::boid))
Но хотя boids легко представить в виде пары векторов (а стадо можно представить в виде набора boids), я озадачен тем, как моделировать векторы. Я не знаю, хочу ли я представлять свои векторы с помощью декартовых или полярных координат, поэтому мне нужно представление, которое позволит мне абстрагироваться от этой детали. Мне нужна базовая алгебра векторных функций независимо от того, как я храню векторные компоненты под капотом.
(defprotocol Vector
"A representation of a simple vector. Up/down vector? Who cares!"
(magnitude [vector] "Returns the magnitude of the vector")
(angle [vector] "Returns the angle of the vector (in radians? from what
zero?).")
(x [vector] "Returns the x component of the vector, assuming 'x' means
something useful.")
(y [vector] "Returns the y component of the vector, assuming 'y' means
something useful.")
(add [vector other] "Returns a new vector that is the sum of vector and
other.")
(scale [vector scaler] "Returns a new vector that is a scaled version of
vector."))
(s/def ::vector #(satisfies? Vector %))
Помимо эстетики согласованности, самая большая причина, по которой меня беспокоит это несоответствие, - это генеративное тестирование: я еще этого не делал, но я рад учиться, потому что он позволит мне тестировать функции более высокого уровня, как только я определю мои низкоуровневые примитивы. Проблема в том, что я не знаю, как создать генератор для спецификации ::vector
без привязки абстрактного протокола / спецификации к конкретной записи, которая определяет функциональность. Я имею в виду, что мой генератор должен создать экземпляр Vector
, верно? Либо я proxy
что-то прямо в генераторе, и поэтому создаю ненужную Vector
реализацию только для тестирования, либо я связываю свой красиво абстрактный протокол / спецификацию с конкретной реализацией.
Вопрос: Как я могу смоделировать вектор - сущность, где набор поведений более важен, чем конкретное представление данных - со спецификацией? Или как создать генератор тестов для моей спецификации на основе протокола, не привязывая спецификацию к конкретной реализации?
Обновление # 1: Чтобы объяснить это по-другому, я создал многоуровневую модель данных, в которой определенный слой записывается только в терминах слоя под ним. (Ничего нового здесь.)
Flock (functions dealing with collections of boids)
----------------------------------------------------
Boid (functions dealing with a single boid)
----------------------------------------------------
Vector
Из-за этой модели удаление всех высших абстракций превратит мою программу в не что иное, как в векторные манипуляции. Желательное следствие этого факта: если я могу найти генератор для Векторов, я могу бесплатно протестировать все свои высшие абстракции. Итак, как мне настроить Vector и создать соответствующий тестовый генератор?
Очевидный, но неадекватный ответ: создайте спецификацию ::vector
, которая представляет карту пары координат, скажем (s/keys ::x ::y)
. Но почему (x, y)
? Некоторые вычисления были бы проще, если бы у меня был доступ к (angle, magnitude)
. Я мог бы создать ::vector
для представления некоторой пары координат, но тогда те функции, которым нужно представление other , должны знать и заботиться о том, как вектор хранится внутри, и поэтому должны знать, как достичь функции внешнего преобразования , (Да, я мог бы реализовать это, используя multispec / conform
/ multimethods, но использование этих инструментов пахнет излишне дырявой абстракцией; я не хочу, чтобы более высокие абстракции знали или заботились о том, чтобы Векторы могли быть представлены несколькими способами.)
Еще более фундаментально, что вектор не (x, y)
или (angle, magnitude)
, это просто проекции «реального» вектора, однако вы хотите это определить. (Я говорю о моделировании предметной области, а не о математической строгости.) Поэтому создание спецификации, представляющей вектор в виде пары координат, в данном случае является не только плохой абстракцией, но и не представляет сущность предметной области.
Лучшим вариантом будет протокол, который я определил выше.Все более высокие абстракции могут быть написаны в терминах протокола Vector
, что дает мне чистый уровень абстракции.Однако я не могу создать хороший тестовый генератор Vector
без привязки моей абстракции к конкретной реализации.Может быть, это компромисс, который я должен сделать, но есть ли лучший способ смоделировать это?