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:

Kaibear

Aktives Mitglied
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 ;)
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
J Meinung zum verwendeten Design Pattern Allgemeine Java-Themen 4
M OOP Design Pattern - "extends Observable implements Observer" Allgemeine Java-Themen 0
perlenfischer1984 Welches Design Pattern ist geegneit. Allgemeine Java-Themen 7
perlenfischer1984 Hilfe bei Design (Pattern) Allgemeine Java-Themen 5
F Welches Design Pattern? Allgemeine Java-Themen 3
D [Drag&Drop] Design-Pattern-Frage Allgemeine Java-Themen 4
ruutaiokwu welches design pattern? frage an die oo-experten unter euch... Allgemeine Java-Themen 3
G Accordion Design Pattern Frage Allgemeine Java-Themen 2
hdi Hilfe beim Design (Stichwort OO, Pattern, ...) Allgemeine Java-Themen 11
N Welches design pattern? Allgemeine Java-Themen 8
G Composite, Design Pattern, printTree Allgemeine Java-Themen 42
M MVC Design Pattern - Verständniss Fragen Allgemeine Java-Themen 3
F Design Pattern zur Realisierung von Mehrfachvererbung? Allgemeine Java-Themen 8
D Design Pattern: Singleton Allgemeine Java-Themen 4
S Noch eine Design-Frage zu Setter Allgemeine Java-Themen 6
S ArrayList Design Allgemeine Java-Themen 4
S Interface Design von HookUp oder Callback Methoden für eigenes Framework Allgemeine Java-Themen 9
Kirby.exe Framework für Game Design Allgemeine Java-Themen 8
C WindowBuilder Design funktioniert nicht Allgemeine Java-Themen 0
M Diverse Design-Fragen Allgemeine Java-Themen 6
rentasad Design-Frage - Interfaces, Klassen, statische Methoden Allgemeine Java-Themen 3
T OOP Fehler im Design Allgemeine Java-Themen 9
N Vererbung Design-Problem mit vorhandenen, von der Klasse unabhängigen Methoden Allgemeine Java-Themen 12
R Parameter Adapter - Design Allgemeine Java-Themen 1
D Bezüglich Design meines Codes Allgemeine Java-Themen 1
S Java Design Frage Allgemeine Java-Themen 10
L OOP Klassen-Design (static oder nicht?) Allgemeine Java-Themen 3
P Auf die Anzahl der Joins achten beim WS design Allgemeine Java-Themen 1
M OOP Design Frage Allgemeine Java-Themen 2
J Domain Driven Design - Modellierungsfrage Allgemeine Java-Themen 3
H MVC Design Allgemeine Java-Themen 9
J Swing Eigenes Button-design Allgemeine Java-Themen 2
Q Kapselung Allgemeine Design- Frage Allgemeine Java-Themen 8
Z Design um boolsche ausdrücke zu speichern & auszuwerten Allgemeine Java-Themen 3
A Sinnvolles Software Design bei Eigenschaftsänderungen von Objekten Allgemeine Java-Themen 7
C Gutes Code Design (3 Schichten Modell) Allgemeine Java-Themen 19
D Design Stations-Gitter Allgemeine Java-Themen 4
M Public Static importRunning -> Bad Design oder ok ? Allgemeine Java-Themen 5
L Software-Design: Kommunikation mit SerialPort (RXTX) Allgemeine Java-Themen 2
G Design Patterns für Programm Allgemeine Java-Themen 3
I Wie populär ist Design by Contract in Java und was haltet ihr davon? Allgemeine Java-Themen 5
Landei Design-Problem Formel-Parser Allgemeine Java-Themen 10
J Aktionen im State-Design-Modell Allgemeine Java-Themen 3
S Design Oberfläche Allgemeine Java-Themen 2
L Design-Frage: Platzierung der Save-Methode Allgemeine Java-Themen 3
G Domain Driven Design Model Allgemeine Java-Themen 14
G konkretes Domain Driven Design Aggregate Allgemeine Java-Themen 2
B Design Problem Allgemeine Java-Themen 8
faulelotte Verständnisproblem Domain Driven Design Allgemeine Java-Themen 3
S Frage zum Design der Datenstruktur Allgemeine Java-Themen 10
D design gesucht - Angabe von zu ersetzenden substrings Allgemeine Java-Themen 2
D Design ohne Getter und Setter Allgemeine Java-Themen 8
D Design: on-the-fly-Parsing + Datenstrukturen Allgemeine Java-Themen 5
D design client server Allgemeine Java-Themen 10
T Design-Frage Allgemeine Java-Themen 14
S XML-Parsing / public-Member-Variablen / Design-Frage Allgemeine Java-Themen 8
S JToolBar Design Allgemeine Java-Themen 3
M Bildersyncronisierung - Design Patterns? Allgemeine Java-Themen 2
T Design - Exception in Thread Allgemeine Java-Themen 3
N Design-Frage: persistent machen per Reflection Allgemeine Java-Themen 3
M Frage zum Design :: allgemein Allgemeine Java-Themen 6
U Frage zu DB Design Allgemeine Java-Themen 3
K Design / Implementierung Allgemeine Java-Themen 5
N Checkstyle - Design for Extension Allgemeine Java-Themen 4
E Was ist ein gutes Design fuer meine Programm? Allgemeine Java-Themen 3
F Paket und Software Design Fragen. Allgemeine Java-Themen 5
P Apple Design Allgemeine Java-Themen 5
S design frage Allgemeine Java-Themen 10
T Design-Tipp gesucht Allgemeine Java-Themen 2
M Design von Java Klassen Allgemeine Java-Themen 2
G java design von klassen und projekten Allgemeine Java-Themen 6
K Design: Klassen in Pakete aufteilen - Eure Meinung Allgemeine Java-Themen 8
S Programmierstil / design Allgemeine Java-Themen 9
S Exception design Allgemeine Java-Themen 2
m@nu Exception-Design Allgemeine Java-Themen 4
R Design-Frage Allgemeine Java-Themen 9
N Hilfe beim Design Allgemeine Java-Themen 13
Torres Design-Problem mit Jakarta Struts Allgemeine Java-Themen 2
A Anwendungs-Design (Plugin-Architektur) Allgemeine Java-Themen 4
mihe7 equals und instanceOf pattern matching Allgemeine Java-Themen 9
L Pattern Eventhandler Allgemeine Java-Themen 5
EinNickname9 Best Practice Singleton und Singleton mit Instanz zu anderer Klasse -Pattern Allgemeine Java-Themen 30
Z MVC Pattern - sinnvolle Integration Allgemeine Java-Themen 6
Kirby.exe Filename nach bestimmtem Pattern durchsuchen Allgemeine Java-Themen 5
Meeresgott Best Practice "Spezifisches" Factory Pattern ? Allgemeine Java-Themen 1
H Strategy Pattern - changeColor() Methode - input rgd oder hex einlesen Allgemeine Java-Themen 1
M Vaadin MVP Pattern Allgemeine Java-Themen 1
N Java MVC Pattern richtig anwenden Allgemeine Java-Themen 24
K Factory Pattern: Mit Generics umgehen Allgemeine Java-Themen 6
J Compilerfehler bis in java.util.regex.Pattern... Allgemeine Java-Themen 2
B MVC-Pattern größeres Beispiel Allgemeine Java-Themen 16
GreenTeaYT Verstehe nicht ganz das Observer Pattern in einer Arrayliste? Allgemeine Java-Themen 3
L Erste Schritte Java Date Format Pattern bestimmten Allgemeine Java-Themen 2
D Pattern mit Pattern vergleichen Allgemeine Java-Themen 3
S Hilfe bei geeignetem Pattern (Decorierer) Allgemeine Java-Themen 2
J Pattern aus String entfernen Allgemeine Java-Themen 2
S Pattern.Match Suche: For Schleife einbinden und in Liste schreiben Allgemeine Java-Themen 3
D Variablen zur Laufzeit global speichern (Registry Pattern?) Allgemeine Java-Themen 6
Rudolf State Pattern als Enum? Allgemeine Java-Themen 10
M massenhaft verschiedene Date-Pattern Allgemeine Java-Themen 3

Ähnliche Java Themen

Neue Themen


Oben