Clojure defrecord и приватные поля
Я часто реализую свой 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();
}
}
Затем интерфейс может быть передан в класс презентатора, который отвечает за обработку всех действий в представлении. Макетная версия может быть передана в презентатор для тестирования, и представление настолько простое, что теоретически его не нужно подвергать модульному тестированию.
Я пытаюсь сделать что-то подобное в Clojure, используя 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)))
Единственный способ сделать интерфейс и "класс" - это использовать defprotocol
а также defrecord
, Есть ли способ лучше? Есть ли способ сделать "поля" в defrecord
которые содержат компоненты (JButton, JLabel, JList) private? Мне не нравится раскрывать детали реализации.
1 ответ
Для таких реализаций вы, вероятно, захотите deftype
вместо defrecord
, defrecord
больше о данных, в то время как deftype
используется для реализации мелочей за некоторыми интерфейсами. Это звучит немного нечетко, я знаю, но это моя интерпретация http://clojure.org/datatypes. Я думаю, что ваш кадр попадает во вторую категорию.
Я бы не стал тратить слишком много времени на попытки спрятать вещи. Не трогайте поля типов (если только внутри интерфейсной функции). Используйте только интерфейсные функции для взаимодействия с типом. Тогда не имеет значения, является ли область технически частной или публичной. (Опять же: см. http://clojure.org/datatypes, раздел о мнениях)