18

私は、異なる名前空間間に循環依存関係があるいくつかのClojureコードに取り組んでおり、それらを解決するための最良の方法を見つけようとしています。

  • 基本的な問題は、ファイルの1つで「そのような変数はありません:名前空間/関数名」エラーが発生することです
  • 関数を「宣言」しようとしましたが、「存在しない修飾変数を参照できません」と文句を言います。
  • もちろん、コードベース全体をリファクタリングすることもできますが、解決する依存関係があるたびにそれを行うのは非現実的です.....循環依存関係の特定のネットワークでは非常に醜くなる可能性があります
  • たくさんのインターフェース/プロトコル/宣言を別のファイルに分けて、すべてにそれを参照させることができます....しかし、それは厄介になり、関連する機能をグループ化した現在の素晴らしいモジュラー構造を台無しにするようです一緒

何かご意見は?Clojureでこの種の循環依存を処理するための最良の方法は何ですか?

4

5 に答える 5

25

Clojure の名前空間に関する多くの議論 (メーリング リストやその他の場所) を覚えています。コンセンサス (そして、AFAICT、Clojure の設計の現在の方向性) は、循環依存関係が設計の叫びであるということです。リファクタリング。回避策は時折可能かもしれませんが、見苦しく、パフォーマンスに問題がある可能性があり (不必要に「動的」にすると)、永久に動作する保証はありません。

あなたは、循環型のプロジェクト構造が素晴らしく、モジュール化されていると言います。しかし、すべてがすべてに依存している場合、なぜそれを呼ぶのでしょうか...? また、事前にツリーのような依存関係構造を計画している場合、「解決する必要がある依存関係があるたびに」ということはあまりないはずです。そして、いくつかの基本的なプロトコルなどを独自の名前空間に入れるというあなたの考えに対処するために、プロジェクトが正確にそれを行うことを何度も望んでいたと言わざるを得ません。コードベースにざっと目を通して、どのような種類の抽象化が使用されているかをすばやく把握できることは、非常に役に立ちます。

要約すると、私の投票はリファクタリングに行きます。

于 2010-06-21T13:11:13.773 に答える
15

私はいくつかのGUIコードで同様の問題を抱えていました、私がやったことは、

(defn- frame [args]
  ((resolve 'project.gui/frame) args))

これにより、実行時に呼び出しを解決できました。これはフレーム内のメニュー項目から呼び出されるため、フレーム自体から呼び出されていたため、フレームが定義されていることを100%確信していました。resolveがnilを返す場合があることに注意してください。

于 2010-06-21T12:52:23.020 に答える
13

私は常にこの同じ問題を抱えています。多くの開発者がそれを認めたくないのと同様に、これは言語の重大な設計上の欠陥です。循環依存は、実際のオブジェクトの通常の状態です。体は心がなければ生きられず、心も体がなければ生きられません。

通話時に解決できる場合もありますが、最適ではありません。そのAPIの一部はエラー報告メソッドですが、APIは独自のメソッドを持つオブジェクトを作成するため、APIがある場合を考えてみましょう。これらのオブジェクトにはエラー報告が必要であり、循環依存があります。エラー チェックおよびレポート機能は頻繁に呼び出されるため、呼び出された時点で解決することはできません。

この場合の解決策は、ほとんどの場合、依存関係を持たないコードを別の (util) 名前空間に移動して、自由に共有できるようにすることです。この手法で問題を解決できないケースにはまだ遭遇していません。これにより、完全で機能的なビジネス オブジェクトを維持することはほぼ不可能になりますが、これが唯一の選択肢のようです。Clojure が現実世界を正確にモデル化できる成熟した言語になるには、まだ長い道のりがあります。それまでは、非論理的な方法でコードを分割することが、これらの依存関係を排除する唯一の方法です。

Aa() が Ba() に依存し、Bb() が Ab() に依存している場合、唯一の解決策は Ba() を Ca() に移動するか、Ab() を Cb() に移動することですが、技術的には C は存在しません。現実の世界。

于 2014-04-10T11:01:46.630 に答える
0

外部依存関係がないようにすべてを 1 つの巨大なソース ファイルに移動するか、リファクタリングします。個人的にはリファクタリングを使いたいと思いますが、実際に取り掛かると、美学がすべてです。KLOCS やスパゲッティ コードが好きな人もいるので、好みは関係ありません。

于 2011-12-08T05:11:50.467 に答える
0

デザインをよく考えると良いです。循環依存関係は、重要なことについて混乱していることを示している可能性があります。

1 つまたは 2 つのケースで、循環依存関係を回避するために私が使用したトリックを次に示します。

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; example/a.cljc

(ns example.a
  (:require [example.b :as b]))

(defn foo []
  (println "foo"))

#?(

   :clj
   (alter-var-root #'b/foo (constantly foo))                ; <- in clojure do this

   :cljs
   (set! b/foo foo)                                         ; <- in clojurescript do this

   )

(defn barfoo []
  (b/bar)
  (foo))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; example/b.cljc

(ns example.b)

;; Avoid circular dependency.  This gets set by example.a
(defonce foo nil)

(defn bar []
  (println "bar"))

(defn foobar []
  (foo)
  (bar))

このトリックは、 Reagent の Dan Holmsand のコードから学びました。

于 2017-09-10T23:22:03.053 に答える