我正在通过开发一个小型Web应用程序来学习Clojure 来自Java世界,我试图了解如何使用关键字设计我的数据。
我应该如何访问数据结构(模型)字段?
例如,我有一个comment
字段{。}}
我使用关键字body
来访问模型值,
但如果我想将:body
名称更改为其他名称(例如body
),将来似乎很难。
至于Java,我会用content
函数封装它,
我应该如何在Cojure中使用它?
什么是设计模式或最佳实践?
答案 0 :(得分:2)
您可以创建类似的get
函数:
; Using bloated names here since
; get and comment are built-ins
(defn get-stuff [comment-node]
(:body comment-node))
; ^ Update here!
然后您只需要更改该功能中的关键字。
当然,如果你决定改变它,智能IDE也许能够做一个合适的重构。我虽然使用了IntelliJ + Cursive,但遗憾的是他们并不聪明,无法完成这样的重构。我不得不承认这是让我对使用关键字感到沮丧的一件事。它们太远了#34;在空中"让IDE能够提供帮助。
您是否想要这样的瘦包装函数取决于用例。如果它是一个简单的POD地图,那么使用(:x point)
可能是有意义的。如果它是一个更复杂的状态,您可能想要使用一组公共API"方法"代替。
答案 1 :(得分:0)
好吧,即使在Java世界中,当您在某个类中重命名字段或方法时,并非每个IDE都可以自动重构Thole项目。你肯定需要手工清理代码。
回答您的问题,尝试为字段选择正确的名称,并且以后不要更改它们。
如果您仍然害怕重命名问题,请将访问敏感字段的功能保存在同一名称空间中。
另一个建议可能是,当您引用某个关键字时,将其放入局部变量中,如下所示:
(let [field :comment
value (get data field)
value2 (get data2 field)]
...)
在将地图解构为同名变量时,关键字是Clojure非常强大的功能,例如:
(let [{:keys [comment body title]} node-data]
... now operate on those variables)
但即使重命名了任何字段,您仍然可以使用解构语法访问它。想象一下,您的数据现在有:description
字段而不是:body
:
(let [{:keys [comment title] body :description} node-data]
... the body now points on the right field)
答案 2 :(得分:0)
SOLID principles aplies to Clojure programs as well. If your data can change then code against interface. By creating get method you will have to change only that method.
This might sound as a violation of "The Clojure Way" but in my humble opinion it is not. Having transparent data has its benefits for sure. At least you can pretty-print them in REPL. But if your data comes from a module which you do not have direct dependency on, it might not be worth creating an accidental dependency on its internal state.
I do not have Idea+Cursive because at work we develop Eclipse RCP apps. So I use Eclipse+Emacs+Cider and I can not speak about Cursive. Clj-refactor can not reliably refactor keywords in this case. You can use namespaced keywords which are easy to change just by find-replace. This is what I would use when I had data which can change over time but the change is unlikely and other parts of application could access it directly. Of course with unit tests and spec. If I had data which are likely to change (e.g. from external API) or other modules would have to access it via traversing deeply nested maps, I would create an interface (get method) in appropriate module.
答案 3 :(得分:0)
TL; DR:使用类似clojure.set / rename或clojure.set / rename-keys的内容。
苛刻的建议
这是我的印象(主要基于https://www.youtube.com/watch?v=oyLBGkS5ICk),“clojure方式”是在你前面充分考虑你的设计,你愿意坚持下去。 (参见“吊床驱动的发展”)。
如果您最终需要进行此类更改,请创建另一组使用新关键字名称的函数(可能使用不同ns中的相同名称)。现在让这些打电话给你的原件,或者你的原件打电话给新的(以更合理的为准)。更新您的文档以建议人们使用新版本。
在吊床上花更多时间。
更一般地(并希望有帮助):
尽量保持你的功能很小并且足够分离,无论如何你只需传递几个值。如果您正在调用需要:body的内容,那么该地图中其余相关值的确有多少呢?
您能否将这些部件重构为自己的功能以减少耦合?你能提供/返回高阶函数来隔离看起来需要全部使用内容处理的功能吗? (“部分”功能是你的朋友)。
全面解释(完成个人抱怨):
目前,其中很大一部分涉及使用带有命名空间关键字的地图以及规范。由于您可以为命名空间添加别名,这实际上似乎会使您解决的特定问题变得更糟:您甚至无法对项目进行简单的搜索/替换。
我现在正在经历这方面的很多痛苦,重新审视我在写这篇文章时写的一些代码。我意识到我的命名空间错了。
如果其他人实际上正在使用我的图书馆,那么真正的答案就是接受我开始时的内容是丑陋,破碎和计划不周。我可以编写比现在更具表现力的新函数(因此,在您的示例中,他们使用的地图包含:bar / content而不是:foo / body)。也许标记旧功能已被弃用,但接受我永远不会消除它们。
或者我可能只是弃用该库并将现有/潜在用户指向一个与我实际想要的更接近的新用户。
实际上,后一种选择很诱人,这已经足够了。即使我是唯一的用户。重命名这样的键看起来几乎是刻意痛苦的。
根据我对其他语言的经验,我怀疑是这种情况,因为破坏你的API是件可怕的事情。
另一方面,至少还有一件作品。
你希望能够随时改变的黑盒子,未记录的数据怎么样? (假设您有一个像句柄一样对待的值,恰好是作为地图实现的)。整个OOP私有的无证实现细节。
这部分API将具有名称为“establish-database-connection!”的函数。和“run-database-command!”。其余部分确实应该是纯函数,它们设置了一系列待处理的副作用,并传递给实际改变世界的副作用。
Pedestal库就是这种方法的一个很好的例子。它的上下文映射与我上面推荐的方法完全相反,只是为任何给定的函数调用使用尽可能少的解耦值......这些观点肯定是我自己的。
你的最终用户应该打电话给你的功能然后回来......把它称为灰盒子。他们负责向你依赖它的其他函数提供它(没有修改)(老实说,这应该是非常罕见的:这些实际上是系统边界代码导致的副作用)。如果他们看到内部,这是他们的业务,但我认为你改变这些细节是公平的。
改变这些细节会让我们回到原来的问题:这样做仍然很痛苦。
这是编写需要此类事情的代码时非常吝啬的另一个原因。
希望有所帮助!
答案 4 :(得分:0)
我会说你是在思考这个问题。大多数时候,我只是在地图中选择一个字段的名称,然后坚持下去。稍后,找到所有字段并使用简单搜索重命名将不是很难。 对我来说,Clojure的理念是让代码尽可能简单易懂,并且不要考虑前期过于灵活,因为Clojure本身简单,灵活且动态,因此您可以在以后轻松更改系统的各个部分。最小的努力。