OOP Design Pattern für GUI - Datenbank Anwendung

Devox

Mitglied
Hallo zusammen,

ich habe aus Übungszwecken ein kleines GUI - Datenbank Programm geschrieben, welches zum Speichern von URL´s benutzt werden kann. Soweit funktioniert auch alles wunderbar, allerdings ist das Programm vom Code her, sehr "hässlich", da ich alles in 2 Klassen gelöst habe.

JFrame - Klasse

Java:
package iLinks;

import java.awt.Color;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.DefaultListModel;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;

public class UI_Main extends JFrame{
	
	DatabaseHandler dbHandler = new DatabaseHandler();
	
	Icon deleteIcon = new ImageIcon("./img/img_delete.png");
	Icon addIcon = new ImageIcon("./img/img_add.png");
	Icon refreshIcon = new ImageIcon("./img/img_refresh.gif");

	String[] columnNames = {"Name","Link", "Kategorie"};
	String[] kategorieNames;
	
	final DefaultTableModel model = new DefaultTableModel(columnNames, 0);	
	final DefaultListModel<String> listModelKategorie;
	
	private JLabel lLinkName = new JLabel("Name: ");
	private JLabel lLinkUrl = new JLabel("URL: ");
	private JLabel lLinkKategorie = new JLabel("Kategorie: ");
		
	private JTextField txtLinkUrl = new JTextField(64);
	private JTextField txtLinkName = new JTextField(64);
	private JTextField txtLinkKategorie = new JTextField(64);
	
	private JButton btnAddLink = new JButton(addIcon);	
	private JButton btnRemoveLink = new JButton(deleteIcon);
	private JButton btnConfirm = new JButton("hinzufügen");
	private JButton btnChangeKategorie = new JButton(refreshIcon);
	
	private JList<String> listKategorie;
	private JScrollPane spKategorie;
	
	private JTable table = new JTable(model);
	private JScrollPane spTable;
	
	JDialog dialogAddLink;
		
	public UI_Main(){
		
		kategorieNames = dbHandler.getKategorie();
		dbHandler.getAllLinks(model);
		
		setLayout(null);
		
		listModelKategorie = new DefaultListModel<String>();
		for (int i = 0; i < kategorieNames.length; i++){
			listModelKategorie.addElement(kategorieNames[i]);
		}
		
		listKategorie = new JList<String>(listModelKategorie);
		spKategorie = new JScrollPane(listKategorie);
		spKategorie.setBounds(10,10,150,20);
		add(spKategorie);
			
		btnChangeKategorie.setBounds(410, 40, 30, 30);
		add(btnChangeKategorie);
		
		btnAddLink.setBounds(410, 70, 30, 30);
		add(btnAddLink);
		
		btnRemoveLink.setBounds(410, 100, 30, 30);
		btnRemoveLink.setBackground(Color.BLACK);
		add(btnRemoveLink);
				
		spTable = new JScrollPane(table);
		spTable.setBounds(10, 40, 400, 200);
		add(spTable);
				
		//Dialog
		dialogAddLink = new JDialog(UI_Main.this);
		Container pane = dialogAddLink.getContentPane();
		pane.setLayout(null);
		
		lLinkName.setBounds(10, 10, 80, 20);
		pane.add(lLinkName);
		
		lLinkUrl.setBounds(10, 30, 80, 20);
		pane.add(lLinkUrl);
		
		lLinkKategorie.setBounds(10, 50, 80, 20);
		pane.add(lLinkKategorie);
		
		txtLinkName.setBounds(100, 10, 100, 20);
		pane.add(txtLinkName);
		
		txtLinkUrl.setBounds(100, 30, 100, 20);
		pane.add(txtLinkUrl);
		
		txtLinkKategorie.setBounds(100, 50, 100, 20);
		pane.add(txtLinkKategorie);
		
		btnConfirm.setBounds(100, 80, 100, 20);
		pane.add(btnConfirm);
		
		dialogAddLink.setTitle("Link hinzufügen");
		dialogAddLink.setSize(250,150);
		dialogAddLink.setLocation(400,300);
		dialogAddLink.setResizable(false);
		//Dialog Ende
		
		setSize(450,280);
		setLocation(300,200);
		setResizable(false);
		setTitle("iLinks");
		setVisible(true);
		
		//ON_CLOSE Operation
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e){
			 	dbHandler.closeConnection();	//Datenbank Verbindung schließen
			 	System.exit(0);
            }
		});
		
		buttonListener bl = new buttonListener();
		btnAddLink.addActionListener(bl);
		btnRemoveLink.addActionListener(bl);
		btnConfirm.addActionListener(bl);
		btnChangeKategorie.addActionListener(bl);
		
		keyListener kl = new keyListener();
		txtLinkKategorie.addKeyListener(kl);
		
	}
	
	class keyListener implements KeyListener{
		
		String kategorieInput;
		String kategorieSuggestion;
		
		public void keyPressed(KeyEvent ke) {
		}

		public void keyReleased(KeyEvent ke) {
			if (ke.getKeyCode() == KeyEvent.VK_SHIFT){
				kategorieInput = txtLinkKategorie.getText().toLowerCase();
			}else{
				kategorieInput = txtLinkKategorie.getText();
				kategorieSuggestion = dbHandler.getKategorieByChar(kategorieInput);
				
				if (!kategorieSuggestion.equals("")){
					if(ke.getKeyCode() == KeyEvent.VK_BACK_SPACE){
						
					}else{
						int inputLength = kategorieInput.length();
						int suggenstionLength = kategorieSuggestion.length();
						txtLinkKategorie.setText(kategorieSuggestion);					
						txtLinkKategorie.select(inputLength, suggenstionLength);
						
					}
					
				}
			}
		}

		public void keyTyped(KeyEvent ke) {				
		}
		
	}
	
	class buttonListener implements ActionListener{
		public void actionPerformed(ActionEvent e){
			//addLink
			if (e.getSource() == btnAddLink){
				txtLinkName.setText("");
				txtLinkUrl.setText("");
				txtLinkKategorie.setText("");
				dialogAddLink.setVisible(true);
			}
			
			//selectedRow
			if(e.getSource() == btnRemoveLink){
				int selectedRow = table.getSelectedRow();
				String rowToRemove = model.getValueAt(selectedRow, 1).toString(); //1 für Url-Feld
				model.removeRow(selectedRow);
				dbHandler.removeLink(rowToRemove);
				kategorieNames = dbHandler.getKategorie();
				listModelKategorie.removeAllElements();
				for (int i = 0; i < kategorieNames.length; i++){
					listModelKategorie.addElement(kategorieNames[i]);
				}
			}
			
			if(e.getSource() == btnChangeKategorie){
				String selectedKategorie;
				selectedKategorie = listKategorie.getSelectedValue();	
				if (selectedKategorie == null){
					listKategorie.setSelectedIndex(0);
					selectedKategorie = listKategorie.getSelectedValue();	
				}
				model.setRowCount(0);	//Alle Rows löschen
				if (selectedKategorie == "All"){
					dbHandler.getAllLinks(model);
				}else{
					dbHandler.getLinkByKategorie(model, selectedKategorie);
				}
			}
			
			if(e.getSource() == btnConfirm){
				if(!txtLinkName.getText().equals("") && !txtLinkUrl.getText().equals("") && !txtLinkKategorie.getText().equals("")){
					String name = txtLinkName.getText();
					String url = txtLinkUrl.getText();
					String kategorie = txtLinkKategorie.getText();
					dbHandler.addLink(name, url, kategorie);
					txtLinkName.setText("");
					txtLinkUrl.setText("");
					txtLinkKategorie.setText("");
					dialogAddLink.dispose();
					model.setRowCount(0);
					dbHandler.getAllLinks(model);
					kategorieNames = dbHandler.getKategorie();
					listModelKategorie.removeAllElements();
					for (int i = 0; i < kategorieNames.length; i++){
						listModelKategorie.addElement(kategorieNames[i]);
					}
					
				}else{
					JOptionPane.showMessageDialog(null, "Die Felder dürfen nicht leer sein!");
				}
			}
		}
	}
}

und die "Datenbank - Klasse" (Connection + Datenbank abfragen, inserts, etc.)

Java:
package iLinks;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Vector;

import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.table.DefaultTableModel;

public class DatabaseHandler {

	static Connection con = null;
		
	public DatabaseHandler(){
		try {
			Class.forName("com.mysql.jdbc.Driver");
			con = DriverManager.getConnection ("jdbc:mysql://localhost:3306/ilinks", "root", "");
			System.out.println("Datenbankverbindung hergestellt!");
		} catch (ClassNotFoundException | SQLException e) {
			JOptionPane.showMessageDialog(null, e.getMessage());
			e.printStackTrace();
		}
	}
	
	//Close Connection
	public void closeConnection(){
		try {
			con.close();
			System.out.println("Datenbankverbindung geschlossen!");
		} catch (SQLException e) {
			JOptionPane.showMessageDialog(null, e.getMessage());
			e.printStackTrace();
		}
	}
	
	public String[] getKategorie(){
		
		
		int rowCount = 0;
		
		try {
			String sql = "Select Count(Distinct Kategorie) from links";
			PreparedStatement ps1 = con.prepareStatement(sql);
			ResultSet rs1 = ps1.executeQuery();
			
			while (rs1.next()){
				rowCount = rs1.getInt(1);
			}
			rs1.close();
			ps1.close();
		} catch (SQLException e) {
			JOptionPane.showMessageDialog(null, e.getMessage());
			e.printStackTrace();
		}
		
		PreparedStatement ps2;
		ResultSet rs2;
		rowCount++; 	//Um 1 erhöhen, da Kategorie All in 0
		String[] data = new String[rowCount];
		data[0] = "All";
		int counter = 1;
		try {
			String sql = "Select Distinct Kategorie from links Order By Kategorie";
			ps2 = con.prepareStatement(sql);
			rs2 = ps2.executeQuery();
			
			while(rs2.next()){
				data[counter] = rs2.getString("Kategorie");
				counter++;
			}
			
		} catch (SQLException e) {
			JOptionPane.showMessageDialog(null, e.getMessage());
			e.printStackTrace();
		}
		return data;
	}
	
	//addLink
	public void addLink(String name, String url, String kategorie){
		String sql = "INSERT INTO links (Name, Url, Kategorie) VALUES (?,?,?)";
		try {
			PreparedStatement ps = con.prepareStatement(sql);
			ps.setString(1, name);
			ps.setString(2, url);
			ps.setString(3, kategorie);
			ps.executeUpdate();
			ps.close();
		} catch (SQLException e) {
			JOptionPane.showMessageDialog(null, e.getMessage());
			e.printStackTrace();
		}
	}//end addLink
	
	//getLink	
	public void getAllLinks(DefaultTableModel model){
		
		String sql = "Select * from links Order By Kategorie";
		
		PreparedStatement ps;
		ResultSet rs = null;
		
		try {			
			ps = con.prepareStatement(sql);
			rs = ps.executeQuery();
			
			while(rs.next()){
				Vector<String> data = new Vector<String>();
				data.add(rs.getString("Name"));
				data.add(rs.getString("Url"));
				data.add(rs.getString("Kategorie"));
				model.addRow(data);
			}			
			ps.close();
			rs.close();			
		} catch (SQLException e) {
			JOptionPane.showMessageDialog(null, e.getMessage());
			e.printStackTrace();
		}
	}//end getLink
	
	//getLinkByKategorie
	public void getLinkByKategorie(DefaultTableModel model, String kategorie){
		
		String sql = "Select * from links Where Kategorie = (?)";
		
		try {
			PreparedStatement ps = con.prepareStatement(sql);
			ps.setString(1, kategorie);
			ResultSet rs = ps.executeQuery();
			
			while(rs.next()){
				Vector<String> data = new Vector<String>();
				data.add(rs.getString("Url"));
				data.add(rs.getString("Name"));
				data.add(rs.getString("Kategorie"));
				model.addRow(data);
			}
			
			ps.close();
			rs.close();		
		} catch (SQLException e) {
			JOptionPane.showMessageDialog(null, e.getMessage());
			e.printStackTrace();
		}
		
	}//end getLinkByKategorie
	public void removeLink(String rowToRemove){
		String sql = "Delete From links Where Url = (?)";
		PreparedStatement ps;
		
		try {
			ps = con.prepareStatement(sql);
			ps.setString(1, rowToRemove);
			ps.executeUpdate();
			ps.close();
		} catch (SQLException e) {
			JOptionPane.showMessageDialog(null, e.getMessage());
			e.printStackTrace();
		}
	}
	
	public String getKategorieByChar(String givenChars){
		String sql = "Select Kategorie from links Where Kategorie Like (?) Order By Kategorie Limit 1";
		String value = "";
		PreparedStatement ps;
		
		givenChars = givenChars + "%";
		
		try {
			ps = con.prepareStatement(sql);
			ps.setString(1, givenChars);
			ResultSet rs = ps.executeQuery();
			
				while (rs.next()){
					value = rs.getString(1);
				}
	
			ps.close();
			rs.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return value;
	}
	
}

Habe jetzt nen bissel rum gegoogelt, in Bezug auf GUI Programmierung ist mir oft das MVC design Pattern begegnet, und in Bezug auf Datenbank Anwendungen das DAO. Bin jetzt allerdings ein wenig verwirrt und weiß nich so recht wo ich Anfangen soll. Am liebsten wäre mir ein Beispiel Projekt, welches das GUI - Datenbank "Problem" sinnvoll löst, habe allerdings nichts gefunden was meine Fragen bzw. Verwirrtheit klärt, außer ne PDF auf indisch oder so, und diese hat noch mehr Fragen aufgeworfen 🙂 Kann natürlich gut sein, das ich durch meine Unwissenheit, google nicht mit den richten Fragen gefüttert habe 😉
Mit welchen Design Pattern würdet den Ihr Profis das oben gezeigt Programm entwickeln? Hat jemand evtl. nen Link zu nem kleinen Projekt, welches mir helfen könnte das Thema besser zu verstehen?

Bin für jede Hilfe dankbar 🙂

lg

EDIT: War mir mit dem Unterforum etwas unsicher, hoffe ich bin hier richtig 😉
 
Zuletzt bearbeitet:
DAO kenn ich leider nicht bzw. hab dieses auch nie verwendet. Im Bezug zur Verbindung der GUI mit der eigentlichen Anwendungslogik solltest du allerdings wie MVC vorgehen. Das erleichtert dir bei Änderungen die Anpassung der GUI bzw. den Austausch dieser.

Wie das Model sagt, gibt es einmal das Model (M - die eigentliche Anwendungslogik), die View (V - die GUI) und den Controler (C).

Perfekt gekapselt weiß die View nichts vom Model. Deswegen erreichst du hier die einfache Austauschbarkeit. Bidirektional wohl gemerkt, du könntest schließlich auch eine Änderung in deinem Model haben. Wenn eine Anwendung des DAO-Patterns deine Anwendungslogik in viele Einzelklassen aufteilt, fässt du die Logik in einer ModelFacade zusammen (auch ein Pattern), von dem aus die ganze NUTZBARE bzw. GEBRAUCHTE Logik ausgeht. Als Beispiel: Du hast eine Klasse "Addition" und eine Klasse "Multiplikation". Eine Zusammenfassung wäre dann "Rechenarten" und von diesen "Rechenarten" wird der Controler gefüttert -> Du gibst dem Controler ein Objekt der Modelfacade mit auf den Weg. Außerdem bekommt der Controler auch ein Objekt der View mitgeliefert. Die Listener, die bei dir direkt implementiert in der View liegen, werden erst im Controler implementiert. In der GUI baust du den Listener und der Controler macht die Logik hinter dem Listener.

Wie genau du dabei vorgehen musst, kannst du zu Hauf in Deutsch und in Englisch im Internet finden.

Hoffe dein Interesse an der Recherche und eine Problemlösung geweckt zu haben 😉
 

Zurück
Oben