Clojure defrecord 和私有字段

发布于 2024-11-03 18:05:05 字数 3462 浏览 5 评论 0原文

我经常使用 Martin Fowler 的 演示模型 模式来实现 Java swing GUI。

下面是一个示例:

import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListModel;

interface MainView {
    void configurationButtonAddActionListener(ActionListener actionListener);

    void directoryLabelSetText(String text);

    ListModel fileListGetModel();

    void setVisible(final boolean visible);
}

class MainFrame
        extends JFrame
        implements MainView {
    private final JButton configurationButton = new JButton("Configuration...");
    private final JLabel directoryLabel = new JLabel();
    private final JList fileList = new JList();

    public MainFrame(final String title) {
        super(title);

        final JPanel mainPanel = new JPanel(new BorderLayout());
        add(mainPanel);
        mainPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));

        mainPanel.add(directoryLabel, BorderLayout.NORTH);
        mainPanel.add(new JScrollPane(fileList));
        mainPanel.add(configurationButton, BorderLayout.SOUTH);

        setSize(800, 600);
        setLocationRelativeTo(null);
    }

    @Override
    public void configurationButtonAddActionListener(final ActionListener actionListener) {
        configurationButton.addActionListener(actionListener);
    }

    @Override
    public void directoryLabelSetText(final String text) {
        directoryLabel.setText(text);
    }

    @Override
    public ListModel fileListGetModel() {
        return fileList.getModel();
    }
}

然后可以将该接口传递给负责处理视图上的所有操作的演示者类。可以将模拟版本传递到演示器中进行测试,并且视图非常简单,理论上不需要进行单元测试。

我正在尝试使用 defrecord 在 Clojure 中做类似的事情:

(ns mainframe
 (:gen-class)
 (:import
   [java.awt BorderLayout]
   [javax.swing JButton JFrame JLabel JList JPanel JScrollPane]))

(if *compile-files*
  (set! *warn-on-reflection* true))

(defprotocol MainView
  (directory-label-set-text [this text])
  (set-visible [this visible]))

(defrecord mainframe [^JFrame frame
                      directory-label
                      file-list
                      configuration-button]
  MainView
  (directory-label-set-text [this text]
    (.setText directory-label text))
  (set-visible [this visible]
    (.setVisible frame visible)))

(defn create-main-frame
  [title]
  (let [directory-label (JLabel.)

        file-list (JList.)

        configuration-button (JButton. "Configuration...")

        main-panel (doto (JPanel. (BorderLayout.))
                     (.add directory-label BorderLayout/NORTH)
                     (.add (JScrollPane. file-list))
                     (.add configuration-button BorderLayout/SOUTH))

        frame (doto (JFrame.)
                (.setTitle title)
                (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
                (.add main-panel)
                (.setSize 800 600)
                (.setLocationRelativeTo nil))]
    (mainframe. frame directory-label file-list configuration-button)))

我可以完成接口和“类”的唯一方法是使用 defprotocoldefrecord< /代码>。有更好的办法吗?有没有办法使包含组件(JButton、JLabel、JList)的 defrecord 中的“字段”私有?我不喜欢暴露实施细节。

I frequently implement my Java swing GUIs using Martin Fowler's Presentation Model pattern.

Here is an example:

import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListModel;

interface MainView {
    void configurationButtonAddActionListener(ActionListener actionListener);

    void directoryLabelSetText(String text);

    ListModel fileListGetModel();

    void setVisible(final boolean visible);
}

class MainFrame
        extends JFrame
        implements MainView {
    private final JButton configurationButton = new JButton("Configuration...");
    private final JLabel directoryLabel = new JLabel();
    private final JList fileList = new JList();

    public MainFrame(final String title) {
        super(title);

        final JPanel mainPanel = new JPanel(new BorderLayout());
        add(mainPanel);
        mainPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));

        mainPanel.add(directoryLabel, BorderLayout.NORTH);
        mainPanel.add(new JScrollPane(fileList));
        mainPanel.add(configurationButton, BorderLayout.SOUTH);

        setSize(800, 600);
        setLocationRelativeTo(null);
    }

    @Override
    public void configurationButtonAddActionListener(final ActionListener actionListener) {
        configurationButton.addActionListener(actionListener);
    }

    @Override
    public void directoryLabelSetText(final String text) {
        directoryLabel.setText(text);
    }

    @Override
    public ListModel fileListGetModel() {
        return fileList.getModel();
    }
}

The interface can then be passed to a presenter class that is responsible for handling all actions on the view. A mock version can be passed into the presenter for testing and the view is so simple that, in theory, it does not need to be unit tested.

I am trying to do something similar in Clojure using defrecord:

(ns mainframe
 (:gen-class)
 (:import
   [java.awt BorderLayout]
   [javax.swing JButton JFrame JLabel JList JPanel JScrollPane]))

(if *compile-files*
  (set! *warn-on-reflection* true))

(defprotocol MainView
  (directory-label-set-text [this text])
  (set-visible [this visible]))

(defrecord mainframe [^JFrame frame
                      directory-label
                      file-list
                      configuration-button]
  MainView
  (directory-label-set-text [this text]
    (.setText directory-label text))
  (set-visible [this visible]
    (.setVisible frame visible)))

(defn create-main-frame
  [title]
  (let [directory-label (JLabel.)

        file-list (JList.)

        configuration-button (JButton. "Configuration...")

        main-panel (doto (JPanel. (BorderLayout.))
                     (.add directory-label BorderLayout/NORTH)
                     (.add (JScrollPane. file-list))
                     (.add configuration-button BorderLayout/SOUTH))

        frame (doto (JFrame.)
                (.setTitle title)
                (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
                (.add main-panel)
                (.setSize 800 600)
                (.setLocationRelativeTo nil))]
    (mainframe. frame directory-label file-list configuration-button)))

The only way I can up with to do the interface and "class" are using defprotocol and defrecord. Is there a better way? Is there any way to make the "fields" in the defrecord that contain the components (JButton, JLabel, JList) private? I dislike exposing the implementation details.

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

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

发布评论

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

评论(1

自此以后,行同陌路 2024-11-10 18:05:05

对于这些实现类的东西,您可能需要 deftype 而不是 defrecorddefrecord 更多的是关于数据,而 deftype 用于实现一些接口背后的细节。我知道,这听起来有点模糊,但这是我对 http://clojure.org/datatypes 的解释。我认为你的框架属于第二类。

我不会花太多时间试图隐藏事情。不要触摸类型字段(除非在接口函数内部)。仅使用接口函数与类型交互。那么该领域在技术上是私有的还是公共的并不重要。 (再次:参见 http://clojure.org/datatypes,有关意见的部分)

For these implementation kind of things you probably want deftype instead of defrecord. defrecord is more about data, while deftype is used to implement the nitty-gritty behind some interfaces. This sounds a bit fuzzy, I know, but it is my interpretation of http://clojure.org/datatypes. I think your frame falls into the second category.

I wouldn't spend too much time on trying to hide things. Don't touch a types fields (unless inside of an interface function). Use only interface functions to interact with the type. Then it doesn't matter whether the field is technically private or public. (Again: cf. http://clojure.org/datatypes, section about opinions)

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