Intuitive Namensgebung und Länge von Namen im (Java-)Code

Als Softwareentwickler stehen wir des Öfteren vor dem Problem, einen Namen für eine Klasse, eine Methode oder eine Variable zu finden. Bei dieser doch sehr alltäglichen Arbeit kann man aber schon viele Fehler begehen. Sind schlecht gewählte Bezeichnungen doch schnell irreführend und damit nicht gerade intuitiv verstehbar. Doch gerade bei der Softwareentwicklung ist Intuition und die adäquate Wahl eines „guten“ Namens wichtig. Schließlich „lebt“ eine solche Software meistens einige Jahre bis Jahrzehnte. Gerade im Enterprise-Umfeld muss eine Software auch nach fünf Jahren noch wartbar sein. Aber auch das private Heimprojekt sollte so gestaltet sein, dass man nach einem halben Jahr noch die Hauptfunktionen durch einfaches Lesen im Code herausfinden kann. Hier sind intuitive Namen das A und O! Mehr lesen

Entwicklung eines Browserspiels mit Java

Seit einigen Jahren ist die Welt der Computer-Spiele um eine Nische reicher. Es handelt sich dabei um Spiele, die im Browser gespielt werden und daher auch den Namen „Browserspiele“ tragen. Es gibt wahrscheinlich kaum einen Internet-Nutzer, der nicht wenigstens kurz eines dieser Spiele ausprobiert hat und somit eine ungefähre Ahnung davon hat, wie sie aussehen. In diesem Beitrag geht es jedoch nicht um das, was jeder sieht, sondern um das „Verborgene“: die Serverseite der Browserspiele.

Am Beispiel des Browserspiels „FMO“, das die Welt der Fussball Manager simuliert, stelle ich einen möglichen Systemaufbau der Browserspiele dar und gehe dabei auch auf die jeweils eingesetzten Java-Technologien ein. Auf der Client-Seite stellen die Browserspiele keine großen Anforderungen an die Hardware und Software der User. Jeder
Rechner, der über einen Browser verfügt, und das sind so gut wie alle, kann zum Spielen eingesetzt werden.

Auf der Serverseite muss das System jedoch etwas ernsteren Anforderungen genügen. Es muss mit Tausenden gleichzeitig laufenden Sitzungen umgehen können und stets eine gute Performance abliefern. Erreicht wird dies durch die Skalierung.

Die Skalierung

Die FMO-User gehören jeweils einer bestimmten Spielwelt an, die über eine eigene Hardware verfügt und auf der jeweils eine Instanz der Spiellogik, bestehend aus mehreren Modulen, läuft. Kommt eine Spielwelt an die Grenzen ihrer Leistung, so wird eine neue Spielwelt gestartet, die wiederum über ihre eigene Hardware mit der
entsprechenden Logik darauf verfügt. Auf diese Weise kann das System horizontal uneingeschränkt skalieren. Einige bestimmte Anforderungen trüben jedoch dieses perfekte Bild. Dazu gehören

  • der zentral organisierte Authentifizierungsdienst
  • der zentral organisierte Bezahldienst
  • die Anforderung der Suchmaschinenfreundlichkeit

Um diesen Anforderungen gerecht zu werden wird ein zusätzlicher zentraler Server eingesetzt, der seine spezifischen Aufgaben mittels der entsprechenden Module erfüllt und ansonsten die ankommenden Anfragen an den jeweils zuständigen Server, für User völlig transparent, weiterleitet. Leider stellt der zentraler Server auf diese Weise einen Flaschenhals dar. Um dieses Problem zu
mildern kann jedoch ein Loadbalancer eingesetzt werden.

Die Schichten-Architektur

Das FMO-System basiert auf einer Vier-Schichten-Architektur. Die Geschäftslogik des Spiels wird in mehreren eigenständigen Komponenten abgebildet. Diese laufen als eigenständige Prozesse und
können von der Präsentationsschicht sowie anderen Geschäftskomponenten angesprochen werden. Zur Datenspeicherung nutzen sie die darunter liegende Persistenzschicht. Oberhalb der Geschäftslogik ist die WebApp-Schicht angesiedelt. Die Web-Apps bedienen die Anfragen der Web-Clients (Browser) und greift während der Verarbeitung der Anfragen auf die Dienste der Geschäftskomponenten zurück.

Schließlich ist im Browser der Spieler die vierte Schicht des Systems angesiedelt. Sie ist für die grafische Repräsentation verantwortlich und besteht aus den JavaScript- und HTML-Modulen, die
mittels der AJAX-Anfragen über HTTP mit den Web-Apps kommunizieren.

Die Java-Technologien

Die Geschäftslogik und weite Teile der Präsentations-Logik bilden den Kern des FMO-Systems. Beide Schichten sind mittels der Java-Technologien, -Frameworks und -Bibliotheken aufgebaut.

Die Komponenten der Geschäftslogik sind reine Java-Komponenten. Hier werden z.B. die Fussball-Spiele berechnet, das Training simuliert und die finanztechnischen Prozesse abgewickelt. Die nach fachlichen Gesichtspunkten gebildete Komponenten laufen als eigenständige Prozesse, nehmen Anfragen per RMI entgegen und lassen ihre Daten von der Persistenzschicht verwalten.

Die Persistenzschicht arbeitet sowohl mit dem Dateisystem als auch mit einem relationalen Datenbanksystem. Im ersten Fall werden die Daten im XML-Format gespeichert. Zur Umwandlung der in XML vorliegenden Daten zu Java-Objekten und vice versa wird die Java-Bibliothek XStream eingesetzt. Der Zugriff auf die Datenbank erfolgt per JDBC. Zur Abbildung der relationalen Daten auf der Objektebene wird außerdem auf das O/R-Tool Hibernate zurückgegriffen.

Die Web-Apps des Systems basieren auf der Servlet-Technologie und laufen in einem Servlet-Container (Tomcat). Der Datenaustausch zwischen den beiden Teilen der Präsentationsschicht (Browser- und Web-Apps) erfolgt mittels der AJAX-Technologie. Dabei wird auf die Hilfe der JSON-RPC Bibliothek jabsorb zurückgegriffen.

Fazit

Java als Programmiersprache mit den entsprechenden Frameworks und Bibliotheken hat sich bei der Entwicklung des Browserspiels „FMO Fussball Manager“ als eine sehr gute Wahl erwiesen. Sowohl in der Entwicklungs- als auch in der Betriebsphase konnten die Anforderungen problemlos erfüllt werden. Java kann daher zum Einsatz in den Projekten zur Entwicklung von browserbasierten Spielen bedingungslos empfohlen werden.

Steckbrief des Autors: Alexander Zent ist selbständiger Software-Entwickler. Er entwickelt und betreibt das Browserspiel FMO Fussball Manager.

[Java] LRU Cache mit Hilfe einer LinkedHashMap

Oftmals benötigt man als Java-Entwickler eine kleine, feine Cache-Klasse, um verschiedene Objekte zwischenzuspeichern und die Datenbank zu schonen. Oftmals wird dann selber mit Arrays, Maps und Listen rumgefuchtelt. Viele kennen nämlich gar nicht die praktische Klasse java.util.LinkedHashMap.

Dabei ist es mit Hilfe dieser Klasse extrem einfach, einen sogenannten LRU-Cache aufzubauen, also einen Least-Recently-Used-Cache. Sprich einen Cache, der die n letzten Objekte speichert. Man gibt also eine feste Cachegröße vor, zum Beispiel 100 und der Cache sorgt dann selber dafür, das Objekte mit einer hohen Zugriffsanzahl weiterhin gecached bleiben und Objekte mit wenigen Zugriffen aus dem Cache entfernt werden. Altes fliegt also nach hinten raus, in dem Fall wenn es mehr als 100 Elemente sind.

Christian d’Heureuse hat dazu jetzt auf Basis der genannten Klasse einen Cache geschrieben, den ich selber leicht abgewandelt so schon benutzt habe. Auf seiner Seite source-code.biz findet sich der entsprechende Source-Code dazu. BTW: Der Trick daraus einen LRU Cache zu machen, ist das Überschreiben der Methode removeEldestEntry der Klasse LinkedHashMap. Damit hat man einen relativ performanten Cache, ohne selber Iterationen einbauen zu müssen, um ggf. veraltete Elemente zu erkennen.

/**
* An LRU cache, based on <code>LinkedHashMap</code>.
*
*

* This cache has a fixed maximum number of elements (cacheSize). * If the cache is full and another entry is added, the LRU (least recently used) entry is dropped. * *

* This class is thread-safe. All methods of this class are synchronized. * *

* Author: Christian d’Heureuse, Inventec Informatik AG, Zurich, Switzerland
* Multi-licensed: EPL / LGPL / GPL / AL / BSD. */ public class LRUCache<K,V> { private static final float hashTableLoadFactor = 0.75f; private LinkedHashMap<K,V> map; private int cacheSize; /** * Creates a new LRU cache. * @param cacheSize the maximum number of entries that will be kept in this cache. */ public LRUCache (int cacheSize) { this.cacheSize = cacheSize; int hashTableCapacity = (int)Math.ceil(cacheSize / hashTableLoadFactor) + 1; map = new LinkedHashMap<K,V>(hashTableCapacity, hashTableLoadFactor, true) { // (an anonymous inner class) private static final long serialVersionUID = 1; @Override protected boolean removeEldestEntry (Map.Entry<K,V> eldest) { return size() > LRUCache.this.cacheSize; }}; } /** * Retrieves an entry from the cache.
* The retrieved entry becomes the MRU (most recently used) entry. * @param key the key whose associated value is to be returned. * @return the value associated to this key, or null if no value with this key exists in the cache. */ public synchronized V get (K key) { return map.get(key); } /** * Adds an entry to this cache. * The new entry becomes the MRU (most recently used) entry. * If an entry with the specified key already exists in the cache, it is replaced by the new entry. * If the cache is full, the LRU (least recently used) entry is removed from the cache. * @param key the key with which the specified value is to be associated. * @param value a value to be associated with the specified key. */ public synchronized void put (K key, V value) { map.put (key, value); } /** * Clears the cache. */ public synchronized void clear() { map.clear(); } /** * Returns the number of used entries in the cache. * @return the number of entries currently in the cache. */ public synchronized int usedEntries() { return map.size(); } /** * Returns a Collection that contains a copy of all cache entries. * @return a Collection with a copy of the cache content. */ public synchronized Collection<Map.Entry<K,V>> getAll() { return new ArrayList<Map.Entry<K,V>>(map.entrySet()); } } // end class LRUCache

Benutzen könnt ihr die Klasse dann wie folgt:

import java.util.Map;
 
public class UseCache {
	// Test routine for the LRUCache class.
	public static void main(String[] args) {
		LRUCache&lt;String, String&gt; c = new LRUCache&lt;String, String&gt;(3);
		c.put("1", "one"); // 1
		c.put("2", "two"); // 2 1
		c.put("3", "three"); // 3 2 1
		c.put("4", "four"); // 4 3 2
		if (c.get("2") == null)
			throw new Error(); // 2 4 3
		c.put("5", "five"); // 5 2 4
		c.put("4", "second four"); // 4 5 2
		// Verify cache content.
		if (c.usedEntries() != 3)
			throw new Error();
		if (!c.get("4").equals("second four"))
			throw new Error();
		if (!c.get("5").equals("five"))
			throw new Error();
		if (!c.get("2").equals("two"))
			throw new Error();
		// List cache content.
		for (Map.Entry&lt;String, String&gt; e : c.getAll())
			System.out.println(e.getKey() + " : " + e.getValue());
	}
}

Eine echt praktische Klasse die einem mit Java-Standard-Mitteln eine nette kleine Caching-Funktion bietet.

Programmierer-Jargon oder was Yoda und Pokémon gemeinsam haben!

Vor kurzem habe ich auf StackOverflow eine sehr schöne Zusammenfassung von verschiedenen Ausdrücken gefunden, die Entwickler/Programmierer gerne benutzen, um oftmals schlecht gemachte Codestellen oder Konstrukte zu beschreiben, gerne auch humorvoll ;).

Falls ihr also selber programmiert oder euch mal grundsätzlich damit auseinandersetzen wollt, was man lieber nicht tun sollte, dann seid ihr auf StackOverflow genau richtig!. Und natürlich solltet ihr versuchen solche Bad Smells zu vermeiden und sauberen Code erstellen. Dabei kann auch StackOverflow ebenso helfen, grundsätzlich handelt es sich dabei nämlich um ein Forum für jegliche Programmiersprachen. In der weltweit vernetzten Community bekommt man auf fast jede Frage eine gute Antwort!

Meine „Favoriten“ sind auf jeden Fall:

  • Yoda Conditions / Yoda Code: Der Code liest sich so wie Yoda spircht, nicht gerade sofort verständlich!
  • Pokémon Exception Handling: Getreu dem Motto „Gotta Catch ‚Em All“! Wer z.B. Java-Entwickler ist versteht was gemeint ist ;).
  • Sehr schön sind auch die verschiedenen Arten an Error-Reports, die man so zu Gesicht bekommt.

Schaut euch mal die Seite an, ihr werdet noch mehr amüsantes und bekanntes finden!

[Ant] Datenbank leeren, alle Tabellen löschen (Oracle)

Wie man manuell alle Tabellen innerhalb einer Oracle Datenbank löschen kann, habe ich bereits vor einiger Zeit dargestellt. Damit ein Entwickler dies nicht immerzu manuell durchführen muss, habe ich dazu ein Ant-Target geschrieben, welches die entsprechende Aufgabe automatisch durchführt. Natürlich mit einer entsprechenden Sicherheitsabfrage!

Mit Hilfe des Ant-Property allowed.clean.db.name kann zusätzlich angegeben werden, das nur eine bestimmte DB gereinigt werden darf, dies verhindert unachtsames Löschen einer Produktivumgebung ;-). Zusätzlich muss jede Ausführung des Ant-Targets explizit mit „yes“ bestätigt werden, dazu erscheint eine entsprechende Dialogbox beim Start.

Die Variablen die mit oracle. beginnen, müssen entsprechend gesetzt/ersetzt werden. Dies muss also an eure Konfiguration angepasst werden. Ansonsten ist nicht viel zu beachten :-).

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
			<input type="text" />
 
 
 
			Task durch User abgebrochen!
 
 
 
 
	 				Begin
	 					for c in (select table_name from user_tables) loop
	 						execute immediate ('drop table '||c.table_name||' cascade constraints');
	 					end loop;
	 				End;