在Clojure中,我怎样才能更好地设计需要多态的代码?

时间:2013-05-22 18:10:33

标签: design-patterns clojure multimethod

我正在编写这个程序,就像在线论坛的网络爬虫一样。对于我抓取的每个论坛,我都需要做同样的事情:

  1. 登录
  2. 找到董事会
  3. 找到帖子
  4. 找到帖子的永久链接
  5. 找到发布帖子的人的用户名
  6. 现在虽然每个论坛都需要同样的逻辑,但每个论坛的实施都是不同的。例如,每个登录表单的输入在每个论坛中是不同的。一个论坛可能有一个名为“username”的字段,另一个论坛可能有一个名为“user”的字段。其中一些步骤可能具有默认实现。例如,login的默认实现是不做任何事情(因为您不必登录某些论坛来抓取它)。

    我所做的是创建了一个函数,其中包含名为crawl-forum的所有这些步骤,但这些实现是抽象的并在其他地方实现。我的问题是,crawl-forum使用这些实施的最佳方式是什么?

    我尝试了什么

    1)配置地图

    这是我到目前为止所尝试的内容。我在名为crawl-forum的{​​{1}}函数中添加了一个新参数。这是一个如下所示的地图数据结构:

    configs

    调用{ :login login-function :find-boards find-boards-function ... } 的代码负责填充该地图。我不喜欢这个是crawl-forum需要在整个configs代码中传递。它随处可见新参数。另外,我有一些用于处理默认实现的蹩脚的临时代码。

    2)多方法

    我在irc上谈到了这一点,有人给了我一个想法,我应该使用多方法来代替,因为它确实是多态行为。它们看起来像这样:

    crawl-forum

    然后客户端代码必须在外部定义自己的多方法:

    (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,但它们都不会永远使用它。这只是发送的必要论据。虽然我很难找到一个彻底的多方法教程,所以如果有更聪明的方法,请告诉我。

    还有第三种选择更好吗?有没有更好的方法来使用多方法?让我知道,谢谢。

2 个答案:

答案 0 :(得分:1)

您可以使用Protocols。这些是支持多态行为的多方法的后继者。此外,在定义协议时,它可以使用:gen-class命名空间指令编译到Java接口。

以下是一些有助于您理解的好链接:

但是,我偏爱一种简单的实现方法。维护论坛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))

我认为这种简单的方法在小型应用程序中没有任何问题。接口和多态性适用于大型项目,团队成员按距离和时间分开。

答案 1 :(得分:1)

我会选择选项1.如果传递地图困扰你,你总是可以使用动态变量。对于默认值,我建议使用merge:

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