Subklassen von JFrame/JWindow

15. Januar 2016

Die „Good Practices“ (z. B. hier) empfehlen allgemein, daß man eine eigene Klasse nicht direkt von JFrame bzw. JWindow ableiten sollte, auch wenn dies aus Bequemlichkeit oft verlockend erscheint. Die Gründe hierzu reichen von überfüllter JavaDoc bis zum sauberen Design.

Von Spezialfällen einmal abgesehen, in denen eine Ableitung doch einmal sinnvoll ist ergibt sich ein weiterer Vorteil oft, handelt es sich um ein GUI-Komponente – auch wenn es nur um eine Anzeige in einem angepaßten JDialog ist.

Hier macht es meist mehr Sinn, die GUI-Komponente von JPanel erben zu lassen und der Klasse eine statische Methode zur Verfügung zu stellen, welche die GUI-Komponente in einen JFrame/JDialog erzeugt und diesen anzeigt. Letzteres ist besonders für das Debugging praktisch.

Der Vorteil ist nämlich, daß man die GUI-Komponente in Applikationen benutzt, indem man sie in die jeweilige GUI einbetten kann, anstelle ein ein weiteres Fenster aufpoppen zu lassen.

Ein möglicher (Pseudo-)Quellcode könnte so aussehen:

public class MyOptionPanel extends JPanel
  {
  public MyOptionPanel()
    {
    /*
    Hier die GUI-Elemente im Panel erzeugen und anordnen.
    ...
    */
    }

  public static void showMyOptionPanel( /* Parameter? */ )
  // Statt void evtl. Status von MyOptionPanel als Rückgabe.
    {
    JFrame frame = new JFrame()
    // Weitere Einstellungen von JFrame.

    MyOptionPanel myOptionPanel = new MyOptionPanel();
    frame.setContentPane( myOptionPanel );

    // Sollte in SwingUtilities.showLater(...)
    frame.setVisible(true);
    }
  }

Die Statische Methode showMyOptionPanel(…) ist lediglich als Zusatz zu verstehen. Tatsächlich kann man diese Komponente nun in eigenen Programmen in der GUI einbetten. Hier ein Codeschnipsel:

// Defniere GUI in der Applikation ...
// ...
// JPanel masterpanel = ...

JPanel subpanel = new JPanel(new BorderLayout());
masterpanel.add(subpanel);

subpanel.add(new JLabel("Einstellungen", BorderLayout.NORTH);

MyOptionPanel myOptionPanel = new MyOptionPanel();
// Einstellungen für myOptionPanel ...

subpanel.add(new JLabel(myOptionPanel, BorderLayout.CENTER);
// ...

Java: Tücken mit Timern (Nachtrag: ScheduledExecutorService)

16. Oktober 2015

Das die ursprünglich in Java verwendeten java.util.Timer nicht resistent gegen Änderung des Systemdatums sind, habe ich ja schon einmal erläutert. Will man Anwendungen schreiben, die längere Zeit aktiv sind und das Systemdatum wird verändert, dann kann man die Arbeit mit Timer und TimerTask getrost vergessen.

Seit Java 1.5 gibt es einen besseren Weg eine in zyklischen Abständen auftretenden Task auszuführen, nämlich über einen Executor und eines ScheduledExecutorService. Hierzu ein kurzes Programmbeispiel:

public class ScheduledExecutorServiceTest implements Runnable
  {
  public void run()
    { System.out.println(new Date()); }

  public static void main(String[] args)
    {
    ScheduledExecutorService executor =
       Executors.newSingleThreadScheduledExecutor();

    ScheduledExecutorServiceTest test = 
       new ScheduledExecutorServiceTest();

    executor.scheduleAtFixedRate(test,
       1, 1, TimeUnit.SECONDS);
    }
  }

Mit dem Concurrency-Framework hat sich seinerzeits SUN bemüht, die Bestandteile in Java um Multithreading aufzuräumen. Mit dem ScheduledExecutorService existiert seither ein gegenüber Systemdatumsänderungen stabile Variante des TimerTasks.


Meine Java Wunschliste II – Properties

24. Juli 2015

In einem Javaprogramm gibt Klassen mit unterschiedlichsten Aufgaben. Einige Klassen haben alles: Daten und Funktionen/Prozeduren um komplexe Aufgaben zu meistern. Andere Klassen haben nur statische Methoden und sind nicht instanzierbar – dies sind Utility-Klassen. Und es gibt Klassen, die nur zum Daten erfassen bestimmt sind, nur aus Feldern mit ihren getter/setter-Methoden bestehen und allenfalls einige Zusatzmethoden für einfache Berechnungen haben.

Für die sogenanten Attribute eine Klasse – also deren Felder – gibt es in Java die Konvention: Felder privat – nur über getter/setter von außen erreichbar!

// ...
// In Klasse "MyObject"
private Color color = Color.BLACK;

public Color getColor() { return color; }

public void setColor(Color color) 
     { this.color = color; }

// ...

MyObject object = new MyObject();

myobject.setColor(Color.RED);

System.out.println("Color: " + myobject.getColor());

(Anmerkung: Es kann sinnvoll sein, auch innerhalb der Klasse auf ein Attribut mit getAttribut() zuzugreifen anstatt direkt. Z. B. wenn die Klasse vererbt wird und getAttribut() sinnvoll überschrieben wird.)

Was aus OOP-Sicht durchaus sinnvoll erscheint, führt im praktischen Einsatz zu Klassen mit ellenlangen getter/setter Methodenauflistungen die meist nicht viel mehr machen, als das Attribut einfach nur zu setzen, bzw. zurück zu geben. Schließlich könnte man ja Zuge des Zugriffs auf ein Attribut noch irgend etwas anderes sinnvolles machen (Attribut der GUI setzen; GUI repaint();). Und ich bin OOP-Puristen begegnet, die auf nur ein vorhandenes public boolean Attribut mit stundenlangen Vorträgen reagierten, warum man so etwas nicht macht!

Anders sieht dies z. B. bei der Entwicklung von Android-Apps aus. Hier wird in vielen Tutorials dazu ermuntert, „unnötige“ getter/setter zu vermeiden um Geschwindigkeitsvorteile zu erlangen.

Das es sinnvoller geht zeigen Programmiersprachen wie C# oder Swift. Dort sieht das so aus:

// ...
// In Klasse "MyObject"
var color : UIColor
  {
  get { return _color }

  set(c) { _color = c }
  }

private var _color : UIColor = UIColor.blackColor

var color2 : UIColor = UIColor.greenColor

// ...

var object = MyObject()

object.color = UIColor.redColor
object.color2 = UIColor.yellowColor

println("Colors: \( object.color )   \( object.color2 )") 

Das Attribut color wird hier zum Property, indem man direkt nach seiner Deklaration in geschweiften Klammern get und/oder set nach dem Muster in Zeilen 3-8 definiert. Von außen kann man dann auf das Property genau wie ein Attribut (hier color2) zugreifen, siehe Zeilen 18-21.

Der Nachteil, daß man von außen nicht mehr reine Attribute und Properties unterscheiden kann wird durch die natürliche Anwendung wieder ausgeglichen. Es scheint logisch, auf eine Eigenschaft ganz einfach mit den Punktoperator zuzugreifen, ohne das dies einem Methodenaufruf gleicht. Und eine Zuweisung mit dem Istgleich-Operator hat sich seit Ewigkeiten in den Programmiersprachen festgesetzt, nicht einmal Sprachen wie Pascal konnten ihn mit dem zugegebenen sinnvolleren := Operator verdrängen.

Der Vorteil liegt nun auch darin, daß viele Attribute lediglich gesetzt bzw. gelesen, ohne daß weitere Aktionen durchgeführt werden müssen. Hierbei genügt es, nach dem Schema wie bei color2 zu verfahren. Sollten nun wirklich weitere Aktionen nötig sein, so kann man ganz einfach ein Attribut in ein Property umwandeln – auch nachträglich irgendwann sehr viel später im Projekt – ohne daß sich die API von außen ändert!

Man ist auch nicht gezwungen, get und set immer zu benutzen. Läßt man z. B. set weg, so hat man das Property automatisch in einem von außen Nur-Lesen-Status versetzt.

Properties stellen meiner Ansicht nach eine sinnvolle Weiterentwicklung der OOP dar, um Code schlanker und damit besser lesbar zu machen. Dies alleine spricht schon dafür, daß sie auf meiner Java-Wunschliste stehen.


Kleine Anmerkung: Es gibt Möglichkeiten Properties in Java zu simulieren, z. B. so:

class IntProperty // TODO: z. B. von Number erben, Comparable implementieren, ...
  {
  private int value;

  public void set(int value) { this.value = value; }
  public int get() { return value; }
  }

// ...

class IrgendEineKlasse
 {
 public final IntProperty anzahl = new IntProperty();

 // ...
 }

// ...

IrgendEineKlasse iek = new IrgendEineKlasse();

iek.anzahl.set(4);

System.out.println("Anzahl: " + iek.anzahl.get());

Damit bleibt zwar das OOP-Konzept einigermaßen gewahrt, jedoch ist es immer noch umständlich und man hat nicht die schöne einfach Nutzung mit dem Istgleich-Operator, etc.


Meine Java Wunschliste I – Überschreibung von Operatoren (opartor overloading)

23. Juli 2015

Wenn man mal über den Java-Tassenrand hinaus blickt und andere (OO) Sprachen näher betrachtet, so findet man eventuell Features, die man dann beim Javaprogrammieren durchaus auch gebrauchen könnte. In dieser Artikelserie liste ich nach und nach meine persönliche Wunschliste für Java-Features auf:

Beginnen möchte ich mit einem Feature, das es in der C++-Welt schon seit jeher gab, in Java bewußt weggelassen wurde aber in nachfolgenden Sprachen wie C# oder Swift völlig normal ist: Das Überladen von Operatoren.

Für Operatoren (Plus +, Minus -, …) kann definiert werden, wie diese für ein bestimmtes Objekt funktionieren sollen.
Java hat dies in einem einzigen Fall intern getan, nämlich das Plus für String-Objekte. Führt man den Operator Plus auf zwei Stringobjekte aus, so konkatiert man diese – hängt den einen String also an den anderen an und generiert damit ein neues Stringobjekt.

Dabei wäre es für andere Objekte (insbesondere von Number abgeleitete Objekte, also „Zahlen“) durchaus praktisch, könnte man für diese auch zumindest die Standardoperatoren definieren.

Angenommen, wir haben eine Klasse, die römische Zahlen verarbeiten kann:

Roman r5 = new Roman(5);
Roman r9 = new Roman("IX");
Roman rsumme = r5 + r9; // ☠ wrong!
System.out.println(rsumme); // "XIV"

Zeile 3 ist mit Java nicht möglich. In C++ könnten wir jedoch in einer gleichnamigen Klasse folgende Methode definieren:

public:
Roman operator+ (const Roman &r);
Roman operator+ (const Roman &r)
{
return Roman(intValue() + r.intValue());
}

In Java muß man hierfür je Operator eine eigene neue Methode schreiben, z. B.:

public Roman add(Roman roman)
  {
  return new Roman(this.intValue()
                 + roman.intValue());
  }
// ...
Roman rs = r5.add(r9);

Aber nicht nur für Zahlen, auch für andere Objekte könnten je nach Anwendungsfall bestimmte Operatoren Sinn machen.

Natürlich könnte man jetzt sagen, daß es genügt, Methoden dafür zu schreiben; doch den Weg mit den überladenen Operatoren finde ich schlicht eleganter und erhöht meines Erachtens die Codelesbarkeit nicht unwesentlich.


for für files

24. Juni 2014

Untertitel: Wenn ’nichts‘ nicht ’nichts‘ ist…

Die for-Schleife der Bash arbeitet nach dem Prinzip einer Liste. Man gibt die Liste im Schleifenkopf an und for arbeite sie ab.

for i in a b c; do echo $i; done

> a
> b
> c

Will man das für Dateien machen, dann gibt es viele, die folgendes schreiben:

for f in `ls /tmp/*`; do echo $f; done

Aber es geht auch folgendes, was viele Bash-Tutorials nicht müde werden zu erklären:

for f in /tmp/*; do echo $f; done

Aber Obacht, hierin versteckt liegt eine gemeine Falle!

Was passiert, wenn keine Dateien im Verzeichnis sind, bzw. keine Dateien gefunden werden (z. B. über eine Filterauswahl: *.gz)?

Der Befehl ls würde eine Fehlermeldung ausspucken, allerdings über stderr, die Liste zum Abarbeiten für for wäre jedoch leer. Doch die Dateiauswahl der for-Schleife liefert in diesem Fall keine leere Liste zurück. Tatsächlich würde im obigen Fall als einziges Element ‚/tmp/*‘ als Text an die for-Schleife übergeben.

Man kann sich ausmalen, welches Fehlerpotential hier bei der weiteren Verarbeitung vorhanden ist. Also Achtung bei Dateiauswahl-for-Schleifen!

 


ThreadManager für beschränktes paralleles Abarbeiten von Threads

17. Oktober 2013

Die Situation:

Wir sind ein Server, an dem eine Vielzahl von Clients angemeldet ist. Jetzt müssen wir eine bestimmte Aufgabe auf allen Clients ausführen (z. B. eine Datei übertragen). Dies nacheinander auszuführen ist keine Option, da dies spürbar an Zeit benötigt. Würden wir es aber parallel durchführen, würden evtl. hunderte von Clients gleichzeitig eine Datei übertragen. Auch wenn Gigabitnetzwerke langsam Standard werden, wollen wir unser Netz nicht dermaßen auslasten.

Die Lösung:

Erstelle ein ThreadManager, der unsere Aufgabe in mehrere Threads parallelisiert, aber nur immer eine maximal vorgegebene Anzahl an Threads laufen läßt.

Zunächst lagern wie die Aufgabe pro Client in je ein Runnable aus:

Runnable r = new Runnable()
{
public void run()
{
// Verbindung zum Client mit IP xxx.xxx.xxx.xxx
// und Ausführen der Aufgabe.
}
}

Pro Client erstellen wir eine individuellen Runner und fügen ihn in einer Liste hinzu. Danach lassen wir die Liste von unserer ThreadManager-Routine abarbeiten:

// Liste mit den Runnables
List<Runnable> runnables = ...

// Maximale Anzahl parallel laufender Runnables
int max_threads = 12;

// Arbeits-Thread-Array
Thread[] threads = new Thread[Math.min(
          max_threads, runnables.size())];

for (int i=0; i<threads.length; i++)
  {
  threads[i] = new Thread("ThreadWorker_" + i)
    {
    public void run()
      {
      Runnable r;
      while (true)
        {
        synchronized (runnables)
          { r = runnables.poll(); }
        if (null == r) break;
        r.run();
        }
      }
    }

 // Arbeits-Thread starten.
 threads[i].start();
 }

// Auf das Ende aller Threads warten.
for (int i=0; i<threads.length; i++)
  try { threads[i].join(); }
  catch (InterruptedException e) {}

Die Runnables speichern wir in einer Liste (Zeile 1). Dann legen wir in Zeile 8 ein Array mit genau so vielen Threads an, wie wir maximal zulassen wollen (Zeile 5), aber auch nicht mehr, als Runnables in der Liste vorhanden sind.

Ab Zeile 10 legen wir die Arbeits-Threads an und starten sie auch gleich. Die Arbeits-Threads bestehen aus einer Endlosschleife, die solange läuft, wie es etwas zu tun gibt => Solange Runnables in der Liste vorhanden sind. In Zeile 21 holen wir ein Runnable (synchronisiert!) aus der Liste und löschen es damit gleichzeitig. Damit wird die Liste irgendwann leer sein. Wir starten das Runnable einfach mit dem Aufruf seiner Methode run() (Zeile 23).

Ist die Liste leer, wird null zurück gegeben. In diesem Fall verlassen wir die Endlosschleife und beenden den Arbeitsthread (Zeile 22).

Im Haupt-Thread des ThreadManagers warten wir in Zeile 33-35 darauf, daß alle Arbeits-Threads sich beenden. Danach ist die Arbeit getan und der ThreadManager hat seine Arbeit erledigt.