这个秋千表模型代码设计得很糟糕吗?

发布于 2025-01-03 09:43:47 字数 1893 浏览 2 评论 0原文

上下文:我有一个基于 clojure 的填字游戏应用程序,其主 ui 是一个 JTabbedPane,带有两个选项卡、一个网格和一个线索表。线索表是线索向量的视图,但向量本身并不是数据的权威存储,而是通过 (active-cluelist) 函数从几个内部数据结构动态生成,由选择的线索选项卡触发。

这就是线索表的实现:

(def cluelist [])
(def update-cluelist)
(def model)

(defn make []
  (let [column-names ["Sq" "Word" "Clue"]
        column-widths [48 200 600]
        table-model (proxy [AbstractTableModel] []
                      (getColumnCount [] (count column-names))
                      (getRowCount [] (count cluelist))
                      (isCellEditable [row col] (= col 2))
                      (getColumnName [col] (nth column-names col))
                      (getValueAt [row col] (get-in cluelist [row col]))
                      (setValueAt [s row col]
                                  (let [word (get-in cluelist [row 1])]
                                    (add-clue word s) ; editing a cell updates the main clue data
                                    (def cluelist (assoc-in cluelist [row 2] s))
                                    (. this fireTableCellUpdated row col))))
        table (JTable. table-model)
        ]

; some pure display stuff elided

(def model table-model)
)

(defn update-cluelist []
  (def cluelist (active-cluelist))
  (.fireTableDataChanged model))

另一个讨论中有人指出,手动调用 fireTableDataChanged(update-cluelist) 的主要代码味道,因为没有任何东西TableModel 类之外应该调用该方法。但是,我认为这是从外部源动态生成表的不可避免的结果。这些文档并没有太大帮助 - 他们指出

您的自定义类只需调用以下之一 每次表数据更改时 AbstractTableModel 方法 外部来源。

它隐含地假设 CustomTableModel 类是数据的权威来源。

这里还有一点 clojure/java 阻抗不匹配 - 在 java 中,我会让 cluelistupdate-cluelist 成为我的 TableModel 的私有成员和方法,而在 clojure 中,cluelist 和表模型是 update-cluelist 可以访问的动态作用域变量。

我的主要问题是没有很多 clojure/swing 代码可供我参考以获取最佳实践。有人对执行此操作的最佳方法有任何建议吗?

Context: I have a clojure-based crossword app whose main ui is a JTabbedPane with two tabs, a grid and a clue table. The clue table is a view over a vector of clues, but the vector itself is not the authoritative store of the data, but dynamically generated from a couple of internal data structures via an (active-cluelist) function, triggered by the clue tab being selected.

So this is the implementation of the clue table:

(def cluelist [])
(def update-cluelist)
(def model)

(defn make []
  (let [column-names ["Sq" "Word" "Clue"]
        column-widths [48 200 600]
        table-model (proxy [AbstractTableModel] []
                      (getColumnCount [] (count column-names))
                      (getRowCount [] (count cluelist))
                      (isCellEditable [row col] (= col 2))
                      (getColumnName [col] (nth column-names col))
                      (getValueAt [row col] (get-in cluelist [row col]))
                      (setValueAt [s row col]
                                  (let [word (get-in cluelist [row 1])]
                                    (add-clue word s) ; editing a cell updates the main clue data
                                    (def cluelist (assoc-in cluelist [row 2] s))
                                    (. this fireTableCellUpdated row col))))
        table (JTable. table-model)
        ]

; some pure display stuff elided

(def model table-model)
)

(defn update-cluelist []
  (def cluelist (active-cluelist))
  (.fireTableDataChanged model))

Someone in another discussion noted that it is a major code smell for (update-cluelist) to be manually calling fireTableDataChanged, because nothing outside the TableModel class should ever be calling that method. However, I feel this is an unavoidable consequence of the table being dynamically generated from an external source. The docs aren't too helpful - they state that

Your custom class simply needs to invoke one the following
AbstractTableModel methods each time table data is changed by an
external source.

which implicitly assumes that the CustomTableModel class is the authoritative source of the data.

Also there is a bit of a clojure/java impedance mismatch here - in java I would have had cluelist and update-cluelist be a private member and method of my TableModel, whereas in clojure cluelist and the table model are dynamically scoped vars that update-cluelist has access to.

My main problem is that there is not a lot of clojure/swing code around that I can look to for best practices. Does anyone have any advice as to the best way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

千里故人稀 2025-01-10 09:43:47

建议:使用原子作为线索列表。不断地重新定义线索列表并不是表示可变数据的正确方法。老实说,我希望它在您第二次定义线索列表时抛出异常。

如果您使用原子作为线索列表,则可以从观察者调用 fireTableDataChanged 方法,而不是手动调用它。这意味着无论何时(何地)您更改原子,fireTableDataChanged 都会自动调用,无需显式调用。

def 的问题在于,多次调用 def 在多线程环境中效果不佳,并且 Clojure 尝试将所有内容默认为相当线程安全。据我了解,使用 var 的“正确”方法是单独保留其根绑定(即,不要再次调用 def)并使用 binding 如果您需要在本地更改它。 def 可能按照您使用它的方式工作,但是该语言被设置为在这种情况下支持原子、引用或代理,并且这些在大多数情况下可能会工作得更好(即您得到观察者)。另外,如果您稍后添加线程,则完全不需要担心它们。

Suggestion: use an atom for cluelist. Constantly redefining cluelist is not the right way to represent mutable data. Honestly, I would expect it to throw an exception the second time you define cluelist.

If you use an atom for cluelist, you can call the fireTableDataChanged method from a watcher instead of calling it manually. This would mean that anytime (and anywhere) you change the atom, fireTableDataChanged will be called automatically, without an explicit call.

The issue with def is that calling def multiple times doesn't work well in a multi-threaded environment and Clojure tries to make everything default to fairly threadsafe. As I understand it, the "proper" way to use a var is to leave its root binding alone (ie, don't call def again) and use binding if you need to locally change it. def may work the way you are using it, but the language is set up to support atoms, refs, or agents in this sort of situation and these will probably work better most of the time (ie you get watchers). Also, you don't need to worry at all about threads if you add them later.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文