How to deal with a variable in a library that needs to be set outside of it?

爷,独闯天下 提交于 2019-12-10 03:48:57

问题


I'm using Datomic in several projects and it's time to move all the common code to a small utilities library.

One challenge is to deal with a shared database uri, on which most operations depend, but must be set by the project using the library. I wonder if there is a well-established way to do this. Here are some alternatives I've thought about:

  • Dropping the uri symbol in the library and adding the uri as an argument to every function that accesses the database

  • Altering it via alter-var-root, or similar mechanism, in an init function

  • Keeping it in the library as a dynamic var *uri* and overriding the value in a hopefully small adapter layer like

    (def my-url ...bla...)

    (defn my-fun [args] (with-datomic-uri my-uri (apply library/my-fun args))

  • Keeping uri as an atom in the library


回答1:


There was a presentation from Stuart Sierra last Clojure/West, called Clojure in the Large, dealing with design patterns for larger Clojure applications.

One of those was the problem you describe.

To summarize tips regarding the problem at hand:

1 Clear constructor

So you have a well defined initial state.

  (defn make-connection [uri]
      {:uri uri
       ...}

2 Make dependencies clear

  (defn update-db [connection] 
     ...

3 It's easier to test

(deftest t-update
  (let [conn (make-connection)]
    (is (= ... (update-db conn)))))

4 Safer to reload

 (require ... :reload)

Keeping uri in a variable to be bound later is pretty common, but introduces hidden dependencies, also assumes body starts and ends on a single thread.

Watch the talk, many more tips on design.




回答2:


My feeling is to keep most datomic code as free of implicit state as possible.

Have query functions take a database value. Have write functions (transact) take a database connection. That maximizes potential reuse and avoids implicit assumptions like only ever talking to one database connection or inadvertently implicitly hardcoding query functions to only work on the current database value - as opposed to past (as-of) or "future" (with) database values.

Coordinating a single common connection for the standard use case of the library then becomes the job of small additional namespace. Using an atom makes sense here to hold the uri or connection. A few convenience macros, perhaps called with-connection, and with-current-db could then wrap the main library functions if manually coding for and passing connection and database values is a nuisance.



来源:https://stackoverflow.com/questions/20683724/how-to-deal-with-a-variable-in-a-library-that-needs-to-be-set-outside-of-it

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!