I'm trying to separate the logic from Jframe in a different class under the same package. But when I add functions for Jframe conponents such as a button it's added in the Jframe file. And I can't access my objects in my logic file from there. What's the correct way to separate logic and graphic?
-
Start by taking a look at [Model-View-Controller](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller), but basically you're on the right track. Instead of providing direct access to the components consider provide getters (and setters) which provide access to their state. You could use a interface as a contract between the controller and the view to describe what the controller can get/do to the view as well as provide some event notification so the view can notify the controller when something important happens – MadProgrammer Mar 01 '16 at 23:04
-
Swing itself is a type of MVC, trying to force a pure MVC onto is just asking for headaches, instead, you should focus on wrapping an MVC around it's existing framework – MadProgrammer Mar 01 '16 at 23:04
-
For [example](http://stackoverflow.com/questions/31576623/how-mvc-work-with-java-swing-gui/31576899#31576899), [example](http://stackoverflow.com/questions/26517856/java-and-gui-where-do-actionlisteners-belong-according-to-mvc-pattern/26518274#26518274), [example](http://stackoverflow.com/questions/31602113/listener-placement-adhering-to-the-traditional-non-mediator-mvc-pattern/31604919#31604919) – MadProgrammer Mar 01 '16 at 23:07
-
@MadProgrammer 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? – Siyu Mar 01 '16 at 23:15
-
Well, that describes a model, when the button is clicked it needs to call the models `add` method, which performs the physical actions. This way, when you want to change the logic you can just change the model, the rest will continue to work – MadProgrammer Mar 01 '16 at 23:17
-
@MadProgrammer I went to read some introduction about MVC. The model part seems easy to understand. 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? – Siyu Mar 01 '16 at 23:30
-
With regards to examples, I've linked three, I believe the majority of which have a working example. Normally, yes, you would also have a controller which acts as a bridge between your model and your view. The problem you will have is, Swing itself is a form of MVC (albit in the form of VC-M), which makes it harder to wrap a pure MVC around. – MadProgrammer Mar 01 '16 at 23:50
1 Answers
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?
This basically describes a model, the model is responsible for controlling the logic and providing the required functionality for other interested parties to work with it
So you might start with a simple contract...
public interface DataModel {
public void add();
public int getData();
}
You I would then create an abstract
version of the model which does most of the boiler plate work...
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;
}
}
Which then allows me to create simple concrete implementations...
public class AddByOneDataModel extends AbstractDataModel {
public AddByOneDataModel(int value) {
super(value);
}
@Override
public void add() {
add(1);
}
}
or if you wanted to be really lazy, you could just do...
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);
}
}
but, so far, the UI's not be involved in any of it, it doesn't care, it will only want an instance of DataModel
Then your UI might look something like...
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;
}
}
This would then allow you to do things like...
TestPane pane = new TestPane();
pane.setModel(new DeltaDataModel(0, 100));
which allows you to specify the actual functionality of the model you want to use (because who knows what you'll want to do in the future)
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?
As explained in How MVC work with java swing GUI, Java and GUI - Where do ActionListeners belong according to MVC pattern?, Listener Placement Adhering to the Traditional (non-mediator) MVC Pattern and verious other answers on the subject, Swing is an implementation of MVC, albit more like M-VC, where the components are self contained views and controllers and the model is dynamic
This makes it difficult to try and wrap a more traditional MVC around. Instead, we work with the concept of a view been a series of components contained with a container which then conforms to some specified contract.
In a more traditional MVC, the model and the view don't interact with each, they don't know anything about each other and instead the controller maintains the relationship.
Model...
Let's go back and update our model. In order to facilitate the MVC, we need to provide an Observer Pattern to it, so it can trigger notifications when the model is updated (because the model could be updated indepdently of the controller or view)
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);
}
}
View...
Next, let's have a look at the view. First we define the contract for the view, this ensures that the controller can only do what what the contract says it can.
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: I might also be tempted to add a getComponent
method which returns the actual JComponent
which the implementation actually uses, but that will come down to what you want to do and is demonstrated in some of the other links
And a physical implementation...
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);
}
}
}
}
Controller...
Again, starting at the lowest level and building the funcitonality up
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);
}
}
Putting it together...
And finally, you might be able to use something like...
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);
}
});
to put it all together

- 1
- 1

- 343,457
- 22
- 230
- 366