2013-06-09 34 views
6

Tôi đang cố gắng triển khai ý tưởng từ http://thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded vào cơ sở mã của tôi.Tránh trạng thái toàn cầu trong lớp DAO của ứng dụng Clojure

Tôi có một lớp dao, nơi tôi cần phải vượt qua trong cơ sở dữ liệu để tránh tình trạng toàn cầu. Có một điều khiến tôi phát điên là cụm từ:

Bất kỳ chức năng nào cần một trong các thành phần này đều phải lấy tham số . Đây không phải là gánh nặng như nó có vẻ: mỗi chức năng được, nhiều nhất, một đối số thêm cung cấp "bối cảnh" trong đó nó hoạt động. Ngữ cảnh đó có thể là toàn bộ đối tượng hệ thống, nhưng nhiều hơn thường sẽ là một số tập con. Với việc sử dụng khéo léo các đóng cửa từ vựng, các đối số thêm biến mất khỏi hầu hết các mã.

Tôi nên sử dụng bao đóng ở đâu để tránh chuyển trạng thái toàn cầu cho mọi cuộc gọi? Một ví dụ sẽ là để tạo ra một hàm init trong lớp dao, một cái gì đó như thế này:

(defprotocol Persistable 
    (collection-name [this])) 

(def save nil) 

(defn init [{:keys [db]}] 
    (alter-var-root #'save (fn [_] (fn [obj] (mc/insert-and-return db (collection-name obj) obj WriteConcern/SAFE))))) 

Bằng cách này tôi có thể bắt đầu lớp dao của tôi từ hệ thống/khởi động chức năng như thế này:

(defn start 
    [{:keys [db] :as system}] 
    (let [d (-> db 
       (mc/connect) 
       (mc/get-db "my-test"))] 
    (dao/init d) 
    (assoc system :db d))) 

này hoạt động, nhưng nó cảm thấy một chút icky. Có cách nào tốt hơn? Nếu có thể tôi muốn tránh buộc khách hàng của lớp dao của tôi phải vượt qua một cơ sở dữ liệu mỗi khi nó sử dụng một hàm.

Trả lời

8

Bạn có thể sử dụng hàm bậc cao hơn để đại diện cho lớp DAO của bạn - đó là mấu chốt của lập trình hàm, sử dụng các hàm đại diện cho các phần nhỏ đến lớn trong hệ thống của bạn. Vì vậy, bạn có một chức năng thứ tự cao hơn mà mất trong kết nối DB như param và trả lại cho bạn một chức năng mà bạn có thể sử dụng để gọi các hoạt động khác nhau như lưu, xóa vv trên cơ sở dữ liệu. Dưới đây là một ví dụ:

(defn db-layer [db-connection] 
    (let [db-operations {:save (fn [obj] (save db-connection obj)) 
         :delete (fn [obj] (delete db-connection obj)) 
         :query (fn [query] (query db-connection query))}] 
    (fn [operation & params] 
     (-> (db-operations operation) (apply params))))) 

Sử dụng lớp DB:

(let [my-db (create-database) 
     db-layer-fn (db-layer my-db)] 
    (db-layer-fn :save "abc") 
    (db-layer-fn :delete "abc")) 

Đây chỉ là một ví dụ về cách các chức năng bậc cao có thể cho phép bạn loại tạo ra một bối cảnh cho một tập hợp các chức năng. Bạn có thể sử dụng khái niệm này hơn nữa bằng cách kết hợp nó với các tính năng khác của Clojure như giao thức.