在 Netbeans 中将逻辑与 Jframe 文件分离

Separating logic from Jframe file in Netbeans

我试图在同一包下的不同 class 中将逻辑与 Jframe 分开。但是当我为 Jframe 组件添加功能时,例如按钮,它会添加到 Jframe 文件中。而且我无法从那里访问逻辑文件中的对象。逻辑和图形分离的正确方法是什么?

Could you be more specific? Let's give it a context. Say I have only one object Data containing an int, a Jframe contains a button which I want it to add 1 to Data. How should I set up the system?

这基本上描述了一个模型,该模型负责控制逻辑并为其他感兴趣的各方提供所需的功能

所以你可以从一个简单的合同开始...

public interface DataModel {
    public void add();
    public int getData();
}

然后我会创建一个 abstract 版本的模型,它完成大部分样板工作...

public abstract class AbstractDataModel implements DataModel {

    private int data;

    public AbstractDataModel(int value) {
        this.data = value;
    }

    public void add(int delta) {
        data += delta;
    }

    @Override
    public int getData() {
        return data;
    }
}

然后允许我创建简单的具体实现...

public class AddByOneDataModel extends AbstractDataModel {

    public AddByOneDataModel(int value) {
        super(value);
    }

    @Override
    public void add() {
        add(1);
    }
}

或者如果你真的想变得懒惰,你可以做...

public class DeltaDataModel extends AbstractDataModel {

    private int delta;

    public DeltaDataModel(int delta, int value) {
        super(value);
        this.delta = delta;
    }

    @Override
    public void add() {
        add(delta);
    }

}

但是,到目前为止,UI 没有参与其中的任何一个,它不关心,它只需要 DataModel

的一个实例

那么你的 UI 可能看起来像...

public class TestPane extends JPanel {

    private DataModel model;
    private JButton add;

    public TestPane() {
        //...
        add = new JButton("Add");
        add.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                getModel().add();
                int data = getModel().getData();
                // Update the UI in some meaningful way...
            }
        });
        //...
    }

    public void setModel(DataModel model) {
        this.model = model;
    }

    public DataModel getModel() {
        return model;
    }

}

这样您就可以执行诸如...

TestPane pane = new TestPane();
pane.setModel(new DeltaDataModel(0, 100));

它允许您指定要使用的模型的实际功能(因为谁知道您将来想要做什么)

But I'm still a bit confused how the View and Controller part seperate from each other. Can you explain a bit still based on the example above? Also, I saw MVC are 3 objects so the main method is in none of the 3, am I correct?

正如在 , Java and GUI - Where do ActionListeners belong according to MVC pattern?, Listener Placement Adhering to the Traditional (non-mediator) MVC Pattern 和其他有关该主题的答案中所解释的那样,Swing 是 MVC 的一个实现,尽管更像是 M-VC,其中组件是独立的视图和控制器以及模型是动态的

这使得尝试包装更传统的 MVC 变得困难。相反,我们使用视图的概念是包含在容器中的一系列组件,然后符合某些指定的合同。

在更传统的 MVC 中,模型和视图不会相互交互,它们彼此之间一无所知,而是由控制器维护这种关系。

型号...

让我们返回并更新我们的模型。为了方便MVC,我们需要给它提供一个Observer Pattern,这样它就可以在模型更新时触发通知(因为模型可以独立于控制器或视图进行更新)

public abstract class AbstractDataModel implements DataModel {

    private List<ChangeListener> changeListeners;
    private int data;

    public AbstractDataModel(int value) {
        this.data = value;
        changeListeners = new ArrayList<>(25);
    }

    @Override
    public void addChangeListener(ChangeListener listener) {
        changeListeners.add(listener);
    }

    @Override
    public void removeChangeListener(ChangeListener listener) {
        changeListeners.remove(listener);
    }

    protected void fireStateChanged() {
        if (!changeListeners.isEmpty()) {
            ChangeEvent evt = new ChangeEvent(this);
            for (ChangeListener listener : changeListeners) {
                listener.stateChanged(evt);
            }
        }
    }

    public void add(int delta) {
        data += delta;
        fireStateChanged();
    }

    @Override
    public int getData() {
        return data;
    }
}

public class DeltaDataModel extends AbstractDataModel {

    private int delta;

    public DeltaDataModel(int value, int delta) {
        super(value);
        this.delta = delta;
    }

    @Override
    public void add() {
        add(delta);
    }

}

public class AddByOneDataModel extends DeltaDataModel {

    public AddByOneDataModel(int value) {
        super(value, 1);
    }
}

查看...

接下来,让我们来看看风景吧。首先我们为视图定义契约,这确保控制器只能做契约规定的事情。

public interface AddView {
    public static final String ADD_ACTION_COMMAND = "Action.add";
    public void setData(int data);
    public void addActionListener(ActionListener listener);
    public void removeActionListener(ActionListener listener);
}

nb:我可能还想添加一个 getComponent 方法,该方法 returns 实现实际使用的实际 JComponent,但那会下降您想要做什么,并在其他一些链接中进行了演示

以及物理实现...

public class AddViewPane extends JPanel implements AddView {

    private JButton btn;
    private JLabel label;

    public AddViewPane() {
        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridwidth = GridBagConstraints.REMAINDER;

        btn = new JButton("Add");
        label = new JLabel("...");

        add(btn, gbc);
        add(label, gbc);

        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                fireAddAction();
            }
        });
    }

    @Override
    public void setData(int data) {
        label.setText(NumberFormat.getNumberInstance().format(data));
    }

    @Override
    public void addActionListener(ActionListener listener) {
        listenerList.add(ActionListener.class, listener);
    }

    @Override
    public void removeActionListener(ActionListener listener) {
        listenerList.remove(ActionListener.class, listener);
    }

    protected void fireAddAction() {
        ActionListener[] listeners = listenerList.getListeners(ActionListener.class);
        if (listeners.length > 0) {
            ActionEvent evt = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, ADD_ACTION_COMMAND);
            for (ActionListener listener : listeners) {
                listener.actionPerformed(evt);
            }
        }
    }

}

控制器...

同样,从最低层开始,向上构建功能

public interface AddController {
    public DataModel getModel();
    public AddView getView();
}

public class AbstractAddController implements AddController {

    private AddView view;
    private DataModel model;

    public AbstractAddController(AddView view, DataModel model) {
        this.view = view;
        this.model = model;

        view.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                getModel().add();
            }
        });

        model.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                getView().setData(getModel().getData());
            }
        });
    }

    @Override
    public DataModel getModel() {
        return model;
    }

    @Override
    public AddView getView() {
        return view;
    }

}

public class DefaultAddController extends AbstractAddController {

    public DefaultAddController(AddView view, DataModel model) {
        super(view, model);
    }

}

放在一起...

最后,您也许可以使用类似...

EventQueue.invokeLater(new Runnable() {
    @Override
    public void run() {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
            ex.printStackTrace();
        }

        DataModel model = new AddByOneDataModel(0);
        AddViewPane view = new AddViewPane();
        DefaultAddController controller = new DefaultAddController(view, model);

        JFrame frame = new JFrame("Testing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // This is where having a getComponent method in
        // view interface would be helpful      
        frame.add(view);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
});

把它们放在一起