【问题标题】:In Clojure, how can I better design this code that needs to be polymorphic?在 Clojure 中,如何更好地设计需要多态的代码?
【发布时间】:2013-05-22 18:10:33
【问题描述】:

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

  1. 登录
  2. 查找板
  3. 查找帖子
  4. 找到帖子的固定链接
  5. 查找发帖人的用户名

现在,虽然每个论坛都需要执行相同的逻辑,但每个论坛的实现是不同的。例如,每个论坛的每个登录表单的输入都不同。一个论坛可能有一个名为“用户名”的字段,另一个可能有一个名为“用户”的字段。其中一些步骤可能具有默认实现。例如,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 作为参数传回,但它们都不会永远使用它。这只是进行调度的必要参数。不过,我真的很难找到一个完整的多方法教程,所以如果有更聪明的方法可以做到这一点,请告诉我。

有没有更好的第三个选项?有没有更好的方法来使用多方法?告诉我,谢谢。

【问题讨论】:

  • @nathanhayfield 那条评论对我来说没有意义。这是在 clojure 中。
  • 使用配置映射似乎不是一种反模式。你可以使用 get 的 not-found 选项、or 宏和 :or 指令破坏来减轻你的“用于处理默认实现的蹩脚的临时代码”吗?如果使用多方法,你也有默认值。
  • @A.Webb 这正是我所做的。我认为这有点蹩脚,因为地图是到处传递的,但:or 是在使用之前为每个函数定义的。也许我应该在一个地方完成这一切。

标签: design-patterns clojure multimethod


【解决方案1】:

我会选择选项 1。如果传递地图打扰您,您始终可以使用动态 var。对于默认值,我建议使用合并:

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

【讨论】:

  • 不错。有什么办法可以解决我遇到的那些多方法问题吗? site-key 问题很烦人,我想知道它们是否可以用于教育目的。
  • 您也可以使用存储站点的动态变量来解决它:)。
【解决方案2】:

您可以使用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))

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

【讨论】:

  • 我想你可能会自己这么说,但我看不出协议如何使这方面的任何方面变得更好。此外,它不适合这个问题,因为我没有类型的变化,只是不同的关键字/网址。另外:
  • 也许我误解了你的建议,或者你误解了我的问题,但我需要每个论坛的多个功能。所以我需要一张地图。或者,也许您是说每次我有一个要处理的新论坛时,我都应该复制并粘贴crawl-forum 代码?出于(希望)显而易见的原因,我不喜欢这个想法。
  • 由于您刚刚编写了“复制粘贴”,因此您的真正目标是 DRY 代码,这意味着您可能需要一个宏。
  • 即crawl-forum 实际上是一个宏,它获取您的地图数据并为每个论坛发出一个函数。
猜你喜欢
  • 1970-01-01
  • 2011-11-06
  • 2020-07-25
  • 2015-09-02
  • 1970-01-01
  • 1970-01-01
  • 2020-12-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多