首页 > 解决方案 > clojure 中比使用实例更惯用的模式?发送类型

问题描述

我尝试实现以下模式:我有一个 AST 和 2 个生成器,一个用于 C#,一个用于 VB。生成器将提供的 AST 作为输入,并以所选语言生成源代码。首先,我为生成器定义了一个抽象:

(defprotocol Generator
  (generate-code [this ast]))

然后我给出一个实现,一个用于 C#,一个用于 VB(我将只给出 C# 实现,因为 VB 几乎完全相同):

(defn ^:private gen-csharp-class [ast]
  (str "class " (:id ast) " {  }"))

(defn ^:private gen-csharp [ast]
  (cond
    (instance? AstClass ast) (gen-csharp-class ast)))

(defrecord AGenerator []
  Generator
  (generate-code [this ast] (gen-csharp ast)))

我不喜欢instance?gen-csharp函数中使用。在 Clojure 中编写这种类型的调度是否有更惯用的方式?

标签: clojurepolymorphism

解决方案


您可以反转抽象:

(defprotocol CodeGenerator
  (generate-class [this ast])
  ;; + other generator methods
  )

(defrecord VBGenerator []
  CodeGenerator
  (generate-class [this ast] (gn-vb-class ast)))

(defrecord CSharpGenerator []
  CodeGenerator
  (generate-class [this ast] (gn-chsharp-class ast)))

然后instance?只编写一次调用:

(defn gen-code [generator ast]
  (cond (instance? AstClass ast) (generate-class generator ast)
         ;; + other generator cases...
         ))

或者您可以在 gen-class 的第二个参数上使用多方法。每个多方法定义都会从以下位置调用正确的方法CodeGenerator

(defmulti gen-class (fn [_ ast] (type ast)))

(defmethod gen-class AstClass [generator ast]
  (generate-class generator ast))

推荐阅读