对于我来说,有两种情况:要么函数描述了其输入,调用者必须按正确形状提供它们,要么函数利用现有的应用程序实体(数据模型),并应通过名称引用其定义。
在函数没有操作实体的情况下,我确保有好的参数名称,在文档字符串中描述形状,并在接受映射或元组时使用解构,为每个元素命名。有时我也可能会有规范,尽管这只有在我想使用它进行生成测试或验证时才会发生。
在函数操作实体的情况下,我让参数名称与实体定义的名称相同。
我通过构造函数定义实体,或者通过名为 make-foo 的函数创建一个概念类型 foo 的实体实例。该函数将记录其创建的形状,或者你可以从其代码中很容易地推断出来。或者我使用元组定义它们,其中元组定义描述了形状。或者我使用 Spec,其中规范名称是实体的名称。
(defn make-item
"Makes an item which is a map of key
:id UUID of the todo-list
:name - the user readable string name of the item
:content - the user provided string content of the item"
[name content]
{:id (java.util.UUID/randomUUID)
:name name
:content content})
(defn make-todo-list
"Makes a todo-list which is a map of key
:id - UUID of the todo-list
:name - the user readable string name of the list
:items - an ordered vector of item"
[name & items]
{:id (java.util.UUID/randomUUID)
:name name
:items (vec items)})
(defn insert-item
"Inserts an item in a todo-list at given 0-based position"
[todo-list item position]
(apply conj
(subvec todo-list 0 position)
item
(subvec todo-list position (count todo-list))))
(defn sanitize-input
"Given an untrusted input string, return a sanitized version.
Takes an optional options map with options:
:extra-denylist - a seq of string words to delete on top of default sanitization."
[input-string & {:keys [extra-denylist] :as options}]
(some-lib/sanitize input-string extra-denylist)
所以正如你在我的小示例中所看到的,sanitize-input 是一个不操作应用程序域模型的函数示例,因此它只是使用好的参数名称、解构和很好的文档字符串来描述其输入。
另一方面,insert-item 是一个操作应用程序域模型的函数,因此它只使用了作为输入的域实体的名称,其额外的 position 参数仅通过良好的名称和文档字符串进行描述。
最后,域实体通过各自名称的 make- 函数来描述。我可以使用记录代替它们,或者也可以使用 Spec。