سؤال

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.

هل كانت مفيدة؟

المحلول

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)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top