У меня сразу несколько вопросов по mvc. Спрашивать буду на примере игрушечной программки, которую специально для этого написал:
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()));
}
}
Это класс представления. Получается такая картинка:
Кнопка increment first увеличивает на единицу первое число, increment second - второе, decrement first уменьшает первое число на единицу, decrement secomd - второе. В общем, после двух нажатий на increment first и одного нажатия на increment second получаем такую картинку:
Два числа хранятся в модели. Вот она:
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();
}
}
Мой контроллер:
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();
}
}
}
Теперь вопросы.
Самый главный вопрос собственно в названии.
Чем нам помог контроллер? Код из веток case оператора switch в
контроллере мог бы с таким же успехом находится в слушателях. На
каждую ветку по слушателю. В итоге слушателей все равно приходится
создавать, что бы вызвать метод контроллера. А потом в этом методе в
операторе switch прописывать тот же самой код, что мог бы быть в
слушателях. Может быть я вообще неправильно реализовал mvc?
Подозреваю что именно так. Причем именно в части контроллера. Потому
что по нему в сети мало информации. Если это так то прошу подсказать
как правильно. Слышал что можно создавать контроллеры для каждого
элемента (в данном случае для каждой кнопки). Но ситуация не сильно
поменяется.
В моем примере все методы, которые вызывается в ветках case не имеют параметров. В реальных программах это скорее всего будет не так. Некоторые кнопки могли бы требовать вызовов методов с параметрами, причем с разным количеством параметров. Что делать в такой ситуации?
Где лучше вычислять самое правое число? В моем примере я делаю это в функции update(). Не лучше ли это делать в модели?
Ответ
Контроллер помогает структурировать код. Весь код, реагирующий на пользовательский ввод, принадлежит контроллеру.
Можно было бы всё слить в один класс, и делать всю логику в OnClick, и код получился бы наверное даже короче. Но вашей целью должен быть не самый короткий, а самый понятный и легко поддерживаемый код.
Мне кажется, ваша диспетчеризация команд по строкам — неправильна. Команды должны быть не строками, а объектами. (У нас же ООП, в конце-концов, да?) И вместо длинного свитча у вас должен быть всего лишь вызов Invoke. [Но это практика из MVVM, общепринятого паттерна в WPF, а традиции в MVC могут быть и другими.]
Вычисления принадлежат модели и только ей. Представление должно не выполнять работу модели (а сложение в реальной программе превратится в серьёзное, длинное вычисление), а лишь только отображать её.