В Clojure, как я могу лучше разработать этот код, который должен быть полиморфным?

Я пишу эту программу, которая похожа на веб-сканер для онлайн-форумов. Для каждого форума, который я сканирую, мне нужно сделать одно и то же:

  1. авторизоваться
  2. найти доски
  3. найти сообщения
  4. найти постоянную ссылку на пост
  5. найти имя пользователя, который сделал пост
  6. и т.п.

Теперь, когда такая же логика должна присутствовать для каждого форума, реализация для каждого форума различна. Например, входы каждой формы входа в систему различны для каждого форума. Один форум может иметь поле с именем "username", другой может иметь поле с именем "user". Некоторые из этих шагов могут иметь реализации по умолчанию. Например, реализация по умолчанию login это ничего не делать (потому что вам не нужно входить в некоторые форумы, чтобы сканировать его).

Я создал функцию со всеми этими шагами, которая называется crawl-forum но реализации являются абстрактными и реализованы в другом месте. Мой вопрос, каков наилучший способ получить crawl-forum использовать эти реализации?

Что я пробовал

1) Конфиг карта

Вот что я пробовал до сих пор. Я добавил новый аргумент в crawl-forum функция называется configs, Это структура данных карты, которая выглядит следующим образом:

{ :login login-function
  :find-boards find-boards-function
  ...
}

Код, который вызывает crawl-forum отвечает за заполнение этой карты. Что мне не нравится в этом, так это configs должны быть переданы на протяжении crawl-forum код. Он добавляет новый параметр везде. Кроме того, у меня есть какой-то отстойный специальный код для обработки реализаций по умолчанию.

2) Мультиметоды

Я говорил об этом на irc, и кто-то подсказал мне, что вместо этого я должен использовать мультиметоды, так как это действительно полиморфное поведение. Они выглядят так:

(defn get-site-key [& args] (first args))
(defmulti login get-site-key)
(defmethod login :default [site-key cookie-store] nil)

Затем клиентский код должен определить свои собственные мультиметоды снаружи:

(defmethod login :forum-1 [site-key cookie-store] (do-something cookie-store))

Что мне не нравится в этом, так это то, что config Я должен передать site-key в к crawl-forum функция и site-key все еще должен быть передан везде внутри. Также каждый defmethod должен быть передан свой site-key назад в качестве параметра, но никто из них никогда не будет использовать его. Это просто необходимый аргумент для отправки. Мне действительно трудно найти подробное мультиметодное учебное пособие, поэтому, если есть более разумный способ сделать это, дайте мне знать.

Есть ли третий вариант, который еще лучше? Есть ли лучший способ использовать мультиметоды? Дайте мне знать, спасибо.

2 ответа

Решение

Я бы выбрал вариант 1. Если вам мешает обойти карту, вы всегда можете использовать динамический var. Для значений по умолчанию я бы предложил использовать слияние:

(def defaults { ... })
(def site-specific (merge defaults { ...}))

Ты можешь использовать Protocols, Это преемники мультиметодов, которые поддерживают полиморфное поведение. Кроме того, когда вы определяете протокол, он может компилироваться в интерфейс Java с :gen-class директива пространства имен.

Вот несколько хороших ссылок, которые помогут вам понять:

Однако я неравнодушен к простому подходу к реализации. Вести карту URL-адресов форума к карте ваших функций, т.е.

(def config 
  {"http://forum1.com" {:login login-function1
                          :find-boards find-boards-function1 ... }
   "http://forum2.com" {:login login-function2 
                          :find-boards find-boards-function2 ... }
    ;; etc
   "http://forumN.com" {:login login-functionN
                          :find-boards find-boards-functionN ... }})

и в других местах

(crawl-forum (get config forum-url))

Я не думаю, что есть что-то не так с этим простым подходом в небольшом приложении. Интерфейсы и полиморфизм предназначены для больших проектов с членами команды, разделенными расстоянием и временем.

Другие вопросы по тегам