我编写了一个游戏服务器,并且必须检查来自用户的消息是否正确且有效。这意味着它们必须具有正确的语法,符合参数格式并且语义正确,即符合游戏规则。
我的目标是拥有一种富有表现力的功能性方法,而不会抛出允许尽可能好的可组合性的异常。
我知道其他类似的问题,但他们要么引用我不喜欢的{:pre ..., :post ...}
,因为一旦抛出异常,只能处理字符串化的信息,或者引用一般我不喜欢的异常处理因为Clojure应该能够做这种任务,或者他们用例如Haskell的monadic风格也许Monadàla(-> [err succ])
我也不喜欢,因为Clojure应该能够在不需要Monad的情况下处理这类任务。
到目前为止,我使用cond作为前置条件检查器和错误代码,然后我将其发送回发送请求的客户端:
(defn msg-handler [client {:keys [version step game args] :as msg}]
(cond
(nil? msg) :4001
(not (valid-message? msg)) :4002
(not (valid-version? version)) :5050
(not (valid-step? step)) :4003
(not (valid-game-id? game)) :4004
(not (valid-args? args)) :4007
:else (step-handler client step game args)))
和类似......
(defn start-game [game-id client]
(let [games @*games*
game (get games game-id)
state (:state game)
players (:players game)]
(cond
(< (count players) 2) :4120
(= state :started) :4093
(= state :finished) :4100
:else ...)))
另一种方法是编写一个宏,类似于defn
和{:pre}
,但不是抛出AssertionError
抛出ex-info
一张地图,而是再次:反对例外投掷。