[[oktatas:programozás:java|< Java]]
====== Java MVC ======
* **Szerző:** Sallai András
* Copyright (c) 2014, Sallai András
* Szerkesztve: 2014, 2015, 2019, 2020
* Licenc: [[https://creativecommons.org/licenses/by-sa/4.0/|CC BY-SA 4.0]]
* Web: https://szit.hu
===== Az MVC =====
A Model View Controller szavakból alkotott betűszó.
A szoftverfejlesztésben használatos **szerkezeti minta**.
Azt jelenti, szétválasztjuk a megjelenésérét felelős kódrészeket,
az adatokért és a vezérlésért felelős kódrészektől.
{{:oktatas:programozás:java:mvc_felepitese.png|}}
* Model - üzleti logika, tárolás
* View - megjelenés, Swing komponensek
* Controller - vezérlés, eseménykezelés
===== Egyszerű MVC =====
A következő példában egyetlen állományban valósítjuk meg az MVC-t.
A View név helyett a MainWindow nevet használom osztálynévnek.
De lehetne akár View osztály is.
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JTextField;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.FlowLayout;
class MainWindow extends JFrame {
private JButton calcButton = new JButton("Számít");
public JTextField baseField = new JTextField(10);
public JTextField heightField = new JTextField(10);
public JTextField resultField = new JTextField(10);
private JLabel baseLabel = new JLabel("alap:");
private JLabel heightLabel = new JLabel("magasság:");
MainWindow() {
resultField.setVisible(false);
add(baseLabel);
add(baseField);
add(heightLabel);
add(heightField);
add(resultField);
add(calcButton);
setLayout(new FlowLayout());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
centerWindow(this);
setVisible(true);
}
public void addCalcButtonActionListener(ActionListener listener) {
calcButton.addActionListener(listener);
}
public static void centerWindow(java.awt.Window frame) {
java.awt.Dimension dimension =
java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = (int)((dimension.getWidth() - frame.getWidth()) / 2);
int y = (int)((dimension.getHeight() - frame.getHeight()) / 2);
frame.setLocation(x, y);
}
}
class Controller {
MainWindow mainWindow = new MainWindow();
Model model = new Model();
Controller() {
mainWindow.addCalcButtonActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event) {
String baseStr = mainWindow.baseField.getText();
String heightStr = mainWindow.heightField.getText();
double base = Double.parseDouble(baseStr);
double height = Double.parseDouble(heightStr);
Double area = model.calcArea(base, height);
mainWindow.resultField.setText(area.toString());
mainWindow.resultField.setVisible(true);
mainWindow.pack();
}
});
}
}
class Model {
public double calcArea(double base, double height) {
if(base <= 0 || height <=0) {
throw new IllegalArgumentException();
}
return (base * height) /2;
}
}
class Program01 {
public static void main(String[]args) {
new Controller();
}
}
===== Külön könyvtárakban, vázlat =====
Nagyobb projektekben a modell, nézet és kontroller számára is külön könyvtárat késztünk.
A következő könyvtárszerkezetet alakítjuk ki:
projektKonyvtar/
|--views/
| `--MainFrame.java
|--models/
| `--Model.java
|--controllers/
| `--Controller.java
|--Makefile
`--Haromszog.java
package view;
import javax.swing.JFrame;
public class MainFrame extends JFrame {
public MainFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 300);
setVisible(true);
}
}
package model;
public class Model {
}
package controller;
import view.MainFrame;
import model.Model;
public class Controller {
MainFrame mainFrame = new MainFrame();
Model model = new Model();
public Controller() {
}
}
import controller.Controller;
class Haromszog {
public static void main(String[] args) {
new Controller();
}
}
SOURCE=view/MainFrame.java \
model/Model.java \
controller/Controller.java \
Haromszog.java
all:
javac -cp . $(SOURCE)
run:
xterm -e "java -cp . Haromszog; read"
===== Külön könyvtárakban, megvalósítva =====
A következőben a háromszög területszámító program komplett megvalósítását látjuk,
egységteszttel együtt. Tesztelni csak a model részt teszteljük, ahol maga
a számítás történik.
projektKonyvtar/
|--views/
| `--MainFrame.java
|--models/
| `--Model.java
|--controllers/
| `--Controller.java
|--Makefile
|--Haromszog.java
`--test/
`--ModelTest.java
package view;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import java.awt.FlowLayout;
import java.awt.event.ActionListener;
public class MainFrame extends JFrame {
JPanel topPanel = new JPanel();
JLabel titleLabel = new JLabel("Háromszög területszámító");
JPanel bottomPanel = new JPanel();
JLabel baseLabel = new JLabel("alap:");
public JTextField baseField = new JTextField(5);
JLabel heightLabel = new JLabel("magasság:");
public JTextField heightField = new JTextField(5);
public JLabel resultLabel = new JLabel("eredmény:");
public JTextField resultField = new JTextField(5);
JButton calcButton = new JButton("Számít");
public MainFrame() {
topPanel.setLayout(new FlowLayout());
topPanel.add(titleLabel);
bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.X_AXIS));
bottomPanel.add(baseLabel);
bottomPanel.add(baseField);
bottomPanel.add(heightLabel);
bottomPanel.add(heightField);
bottomPanel.add(resultLabel);
bottomPanel.add(resultField);
bottomPanel.add(calcButton);
resultLabel.setVisible(false);
resultField.setVisible(false);
setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
add(topPanel);
add(bottomPanel);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public void addCalcButtonActionListener(ActionListener listener) {
calcButton.addActionListener(listener);
}
}
package test;
import org.junit.Test;
import static org.junit.Assert.*;
import model.Model;
public class ModelTest {
Model model = new Model();
@Test
public void testCalcArea() {
assertEquals(525, model.calcArea(30, 35), 0);
assertEquals(900, model.calcArea(40, 45), 0);
}
@Test(expected = IllegalArgumentException.class)
public void tesztExceptionSzamitTerulet() {
model.calcArea(0, 35);
}
}
package model;
public class Model {
public static double calcArea(double base, double height) {
if (base <= 0 || height <= 0) {
throw new IllegalArgumentException("Nem megfelelő paraméterek");
}
return (base * height) / 2;
}
}
package controller;
import view.MainFrame;
import model.Model;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class Controller {
MainFrame mainFrame = new MainFrame();
Model model = new Model();
public Controller() {
mainFrame.addCalcButtonActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event) {
String baseStr = mainFrame.baseField.getText();
double base = Double.parseDouble(baseStr);
String heightStr = mainFrame.heightField.getText();
double height = Double.parseDouble(heightStr);
Double result = model.calcArea(base, height);
mainFrame.resultField.setText(result.toString());
mainFrame.resultField.setVisible(true);
mainFrame.resultLabel.setVisible(true);
mainFrame.pack();
}
});
}
}
import controller.Controller;
class Haromszog {
public static void main(String[] args) {
new Controller();
}
}
SOURCE=view/MainFrame.java \
model/Model.java \
controller/Controller.java \
Haromszog.java
all:
javac -cp . $(SOURCE)
run:
xterm -e "java -cp . Haromszog; read"
LINJUNIT=/usr/share/java/junit4.jar
testl:
javac -cp .:$(LINJUNIT) test/ModelTest.java
java -cp .:$(LINJUNIT) org.junit.runner.JUnitCore test.ModelTest
WINJUNIT=c:\bin\SWScite\javalibs\junit-4.11.jar
WINHAMCREST=c:\bin\SWScite\javalibs\hamcrest-core-1.3.jar
testw:
javac -cp .;$(WINJUNIT);$(WINHAMCREST) test/ModelTest.java
java -cp .;$(WINJUNIT);$(HAMCREST) org.junit.runner.JUnitCore test.ModelTest
===== MVC NetBeansben =====
* view.Mainwindow osztályt hozzuk létre JFrame-ből
* controller.Controller osztályt hozunk létre
* model.Model osztályt hozunk létre
package view;
import controller.Controller;
public class Mainwindow extends javax.swing.JFrame {
Controller controller = new Controller(this);
public Mainwindow() {
initComponents();
}
//...
private void aboutButtonActionPerformed(java.awt.event.ActionEvent evt) {
controller.aboutButtonActionListener(evt);
}
public static void main(String args[]) {
//...
}
//..
private javax.swing.JButton aboutButton;
}
package controller;
import java.awt.event.ActionEvent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import model.Model;
public class Controller {
Model model = new Model();
Mainwindow jframe;
public Controller(Mainwindow jframe) {
this.jframe = jframe;
}
public void aboutButtonActionListener(ActionEvent ev ) {
JOptionPane.showMessageDialog(jframe, "Működik");
}
}
package model;
public class Model {
}
===== Swing GUI program MVC-ben =====
app01/
|-lib/
`-src/
|-controllers/
| `-MainController.java
|-models/
| `-MainModel.java
|-views/
| `-MainFrame.java
`-App.java
import controllers.MainController;
public class App {
public static void main(String[] args) throws Exception {
new MainController();
}
}
package views;
import javax.swing.JFrame;
public class MainFrame extends JFrame {
public MainFrame() {
this.setTitle("App");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(400, 300);
this.setVisible(true);
}
}
package models;
public class MainModel {
}
package controllers;
import models.MainModel;
import views.MainFrame;
public class MainController {
MainFrame mainFrame;
MainModel mainModel;
public MainController() {
this.mainFrame = new MainFrame();
this.mainModel = new MainModel();
}
}
===== Linkek =====
* https://www.geeksforgeeks.org/java-util-observable-class-java/ (2019)
* https://www.concretepage.com/java/example-observer-observable-java (2019)
* https://www.baeldung.com/java-observer-pattern (2019)
* http://www.austintek.com/mvc/ (2019)
* https://www.ssaurel.com/blog/learn-to-make-a-mvc-application-with-swing-and-java-8/ (2019)
* https://examples.javacodegeeks.com/core-java/java-swing-mvc-example/ (2019)
* https://web.archive.org/web/20131228060247/http://www.cs.wcupa.edu/~rkline/java/mvc-example.html (2019)