I have several questions on mvc at once. I will ask on the example of a toy program, which I specifically wrote for this:

public class View implements Observer { private Model model; private IController controller; private JLabel firstNumber = new JLabel(); private JLabel secondNumber = new JLabel(); private JLabel resultLabel = new JLabel(); public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Model model = new Model(); IController controller = new Controller(); View view = new View(); view.setModel(model); view.setController(controller); view.createAndShowGUI(); } }); } public void setModel(Model model) { this.model = model; model.addObserver(this); } public void setController(IController controller) { this.controller = controller; } public void createAndShowGUI() { JFrame frame = new JFrame("mvc train"); frame.setSize(400, 400); frame.getContentPane().add(createMainPanel()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } private Component createMainPanel() { JPanel mainPanel = new JPanel(); mainPanel.add(firstNumber); mainPanel.add(secondNumber); mainPanel.add(resultLabel); JButton inc1 = new JButton("increment first"); inc1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { controller.execute("inc1", model); } }); JButton dec1 = new JButton("decrement first"); dec1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { controller.execute("dec1", model); } }); mainPanel.add(inc1); mainPanel.add(dec1); JButton inc2 = new JButton("increment second"); inc2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { controller.execute("inc2", model); } }); JButton dec2 = new JButton("decrement second"); dec2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { controller.execute("dec2", model); } }); mainPanel.add(inc2); mainPanel.add(dec2); return mainPanel; } @Override public void update(Observable arg0, Object arg1) { firstNumber.setText("" + model.getA()); secondNumber.setText("" + model.getB()); resultLabel.setText("" + (model.getA() + model.getB())); } } 

This is a presentation class. It turns out this picture: enter image description here

The increment first button increments the first number, increment second - the second, decrement first decrements the first number by one, decrement secomd - the second. In general, after two clicks on the increment first and one click on the increment second we get the following picture:

enter image description here

Two numbers are stored in the model. Here she is:

 public class Model extends Observable { private int a; private int b; public void incrementA() { a++; setChanged(); notifyObservers(); } public void incrementB() { b++; setChanged(); notifyObservers(); } public void decrementA() { a--; setChanged(); notifyObservers(); } public void decrementB() { b--; setChanged(); notifyObservers(); } public int getA() { return a; } public int getB() { return b; } @Override public void addObserver(Observer observer) { super.addObserver(observer); setChanged(); notifyObservers(); } } 

My controller:

 public class Controller implements IController { @Override public void execute(String action, Model model) { switch (action) { case "inc1": model.incrementA(); break; case "inc2": model.incrementB(); break; case "dec1": model.decrementA(); break; case "dec2": model.decrementB(); } } } 

Now questions. The main question is actually in the title.

  1. How did the controller help us? The code from the case branches of the switch statement in the controller might as well be in the listeners. For each branch on the listener. As a result, listeners still have to be created to call the controller method. And then in this method in the switch statement to prescribe the same code that could be in the listeners. Maybe I generally implemented mvc incorrectly? I suspect that this is so. And it is in the part of the controller. Because there is little information on it in the network. If this is the case, please suggest how it is correct. I heard that you can create controllers for each element (in this case for each button). But the situation does not change much.
  2. In my example, all the methods that are called in the case branches have no parameters. In real programs, this is likely to be wrong. Some buttons could require method calls with parameters, and with a different number of parameters. What to do in this situation?
  3. Where it is better to calculate the rightmost number? In my example, I do this in the update () function. Isn't it better to do this in a model?

    1 answer 1

    1. The controller helps to structure the code. All code that reacts to user input belongs to the controller.

      It would be possible to merge everything into one class, and do all the logic in OnClick , and the code would probably turn out to be even shorter. But your goal should not be the shortest, but the most understandable and easily supported code.

    2. It seems to me that your dispatch of commands in rows is incorrect. Commands should not be lines, but objects. (We have the same OOP, after all, right?) And instead of a long switch, you should have just a call to Invoke . [But this is a practice from MVVM, a generally accepted pattern in WPF, and the traditions in MVC may be different.]

    3. The calculations belong to the model and only to her. The representation should not perform the work of the model (and the addition in the real program will turn into a serious, long calculation), but only to display it.

    • thank you very much, very interesting. Can the second paragraph be clarified? We need to call functions. But can we wrap them up in classes? They will implement the MyListener interface, which will have one invoke () method. That is, there will be four classes that implement this interface: FirstIncrementer, SecondIncrementer, etc. And the execute method in the controller will accept a parameter of type MyListener. And just call its invoke () method. Then each listener will push the desired MyListener implementation to the controller. But what should be written in the invoke () methods of specific classes? In theory, there should be work with the model - Alexander Elizarov
    • But work with the model should be in the controller. - Alexander Elizarov
    • it turns out that there will be only one call in the controller, and the whole processing code is again not in it .. Although we aimed at transferring the input processing code to the controller. I do not understand ( - Alexander Elizarov
    • @Alexander Elizarov: So, these classes (commands) should be part of the controller. So all the processing code is still in the controller. - VladD
    • one
      @Aleksandr Elizarov: Well, during the creation of the UI, you need to bind commands. That is, the button will know its team, but not know what's inside. And it will pull for it by pressing. Or not, the controller will have the Map buttons in the command, and by the button find the desired command and run it. I do not know yet how to correct. [Well, probably ICommand , rather than IController .] - VladD