5

http://mindbat.com/2013/03/clojurewest-2013-day-one-notes/には、次のようなメモがあります

  • トップレベルで参照とアトムを定義することは、基本的にシングルトンを介したグローバルな可変状態です。避けてください
  • コンストラクター関数を使用して、使用する状態変数を返すことをお勧めします。次に、その状態を各関数に渡します

これは良いアドバイスだと思いますが、Ring/Compojure アプリケーションでこれを実装する方法が完全にはわかりません。これがどのように機能するかの具体的な例を誰か挙げてもらえますか?

とをこのように組み合わせてdefroutes、そのスコープ内のグローバル変数を取り除く方法に特に興味があります。initapp

4

2 に答える 2

5

スチュアートの話から私が理解していることは、次のようなものです。

(ns state.core)

(defn create-user-module [] (atom []))

(defn add-user [module user]
  (swap! module conj user))

(defn get-users [module]
  @module)

状態を操作する関数はパラメーターとしてそれを取得することを期待しているため、「コア」にグローバル状態はありません。これにより、テストごとに「ユーザー モジュール」の新しいインスタンスを作成できるため、テストが容易になります。また、このモジュールのクライアントは、create-user-module 関数で取得したものを気にする必要はありません。検査せずに渡すだけでよいため、必要なときにいつでもユーザー モジュールの実装を変更できます。Stuart は、複数の実装を行う場合に、これらのモジュールのプロトコルを作成することについても話します。

あなたの質問に答えようとすると、リング アダプターは単なる 1 つのパラメーターの関数であり、compojure は単なるルーティング ライブラリであるため、次のようなクロージャーを使用して Web アプリを作成できます。

(ns state.web
  (:use compojure.core)
  (:require [state.core :as core]))

(defn web-module [user-module]
  (routes
   (GET "/all" [] (core/get-users user-module))))

これで、web-module を呼び出して webapp を作成し、必要な依存関係をパラメーターとして渡すことができます。もちろん、正しいユーザー モジュールを使用して Web アプリを作成する人がまだ必要なので、必要なのはすべてを結び付ける「メイン」関数だけです。

(ns state.main
  (:require state.core
            state.web)
  (:use ring.adapter.jetty))

(defn start []
  (let [user-module (state.core/create-user-module)
        web-module (state.web/web-module user-module)]
    (run-jetty web-module {:port 3000 :join? false})))

(defn stop [app]
    (.stop app))

startmainアプリのメソッドから呼び出されます。これは、lein-run プラグインに切り替える必要があることを意味します。

さて、あなたがinit(私が推測する lein ring プラグインから) について質問していることを考えると、Web アプリケーションをコンテナーにデプロイすることを計画していると思います。lein ring プラグインは Java サーブレット fw 制約内で動作する必要があり、ハンドラーは最終的に Java サーブレットにコンパイルされるため、おそらく実行できる最善の方法は次のようなものです。

(ns state.web
  (:use compojure.core)
  (:require [state.core :as core]))

(def module-deps (atom {})

(defn init-app [] (swap! module-deps conj [:user-module (core/create-user-module)]))

(defroutes web-module []
   (GET "/all" [] (core/get-users (:user-module @module-deps))))

これは、コアの名前空間がテストしやすいことを意味しますが、Web 名前空間にはまだグローバルな状態がありますが、それは「適切に」カプセル化されており、Java コンテナーを使用する必要がある場合はおそらく十分であると思います。

これは、ライブラリがフレームワークよりも「優れている」理由のもう 1 つの議論です :)

于 2013-03-27T01:10:11.987 に答える
2

グローバルな状態が必要な場合が多いため、それを避けることはできません。できることはそれを適切に管理することであり、それが次の 2 つのポイントで説明されていることだと思います。

良くない方法:

(ns data)
(def users (atom []))

(ns pages)
(defn home []
    (do-something data/@users)

(defn save []
    (let [u @users]
       (swap! data/users ....)

良い方法:

(ns data)
(def- users (atom []))
(defn get-users [] @users)
(defn update-user [user] (swap! @users ...))

(ns pages)
; use the functions exposed by data ns to interact with data rather then poking the atom directly.

基本的に、あらゆるタイプの状態へのすべてのアクセスは、アプリケーションの他の部分から抽象化する必要があります。すべてのビジネス ロジック関数は、状態自体を選択して直接更新するのではなく、状態をパラメーターとして受け取り、新しい状態を返す必要があります。

于 2013-03-25T04:22:45.130 に答える