Nov 1 2009

Maven-Update unter Mac OS X

Auf meinem Snow Leopard System habe ich ein Maven bereits vorinstalliert. Es kann allerdings gut sein, dass Maven mit XCode installiert wurde. Das kann ich gerade nicht genauer sagen.

Jedenfalls habe ich Maven in Version 2.0.9 auf meinem System vorgefunden. Aktuell ist aber Maven in Version 2.2.1. Um nun Maven auf den aktuellen Stand zu bringen, ist erstmal die aktuelle Version runterzuladen. Das runtergeladene Archiv wird entpackt.

Jetzt ist ein wenig Tipperei auf der Konsole angesagt. Also das Terminal gestartet.

Der Befehl which mvn zeigt uns, dass Maven über /usr/bin/mvn aufgerufen wird. Schaut man noch genauer hin und verfolgt die ganzen symbolischen Links, stößt man im Endeffekt auf /usr/share/java/apache-maven-2.0.9. Damit ist ein Update sehr einfach!

  1. In das Verzeichnis /usr/share/java wechseln (cd /usr/share/java).
  2. Die entpackte neue Maven Version dorthin kopieren (sudo cp -r /path/to/apache-maven-2.2.1 .).
  3. Ein Verzeichnis höher wechseln (cd ..).
  4. Den alten Link entfernen (sudo rm maven).
  5. Auf das neue Maven verlinken (sudo ln -s java/apache-maven-2.2.1 maven).

Nach diesen Schritten sollte sich Maven mit der Version 2.2.1 melden.


Aug 10 2009

Daten unter Mac OS X verschlüsseln

Möchte man unter Mac OS X Daten verschlüsseln, so ist das bereits mit Bordmitteln erreichbar.

FileVault

Da wäre FileVault, das das kompletten Homeverzeichnis verschlüsselt. FileVault lässt sich unter den Systemeinstellungen im Bereich “Sicherheit” aktiveren.

Da FileVault das gesamte Homeverzeichnis verschlüsselt, kann das Auswirkungen auf die Performance des Rechners haben. So werden z.B. alle Downloads und Browser-Cache-Dateien ebenfalls verschlüsselt, da sie im Homeverzeichnis landen.

Verschlüsseltes Disk Image

Verschlüsselte Disk Images sind das 2. Bordmittel von Mac OS X, um Daten verschlüsselt abzulegen. Solch ein Disk Image wird über das Festplatten-Dienstprogramm (Disk Utility) erstellt.

Beim Erstellen eines neuen Disk Images muss man angeben, wo das Image abgelegt werden soll. Es folgen die Einstellungen zur (Start-)Größe des Images, zur Verschlüsselung und zum Format des Images (es sollte mitwachsen).

Erstellung eines neuen, verschlüsselten Disk Images.

Erstellung eines neuen, verschlüsselten Disk Images.

Nachdem alle Parameter zur Erstellung des verschlüsselten Disk Images angegeben sind wird das Image erstellt. Nun muss ein Passwort festgelegt werden.

Passwort für das verschlüsselte Disk Image

Passwort für das verschlüsselte Disk Image.

Danach ist das verschlüsselte Disk Image angelegt und kann per Doppelklick gemountet werden. Ist das Image gemountet kann es normal benutzt werden.

Das verschlüsselte Disk Image wächst zwar nun schön mit, wird aber nicht mehr kleiner. Um die Größe des Images einzudampfen muss im Terminal folgender Befehl verwendet werden (das Image darf nicht gemountet sein!):

hdiutil compact encryptedDiskImage.sparcebundle

Man wird nach dem Passwort für das Disk Image gefragt und anschließend wird die Größe des Images verringert. Es folgt noch eine Zusammenfassung, wie viel Platz eingespart werden konnte.

hdiutil compact

hdiutil compact


Aug 10 2009

Veränderung kapseln

Eine Konstante in der Softwareentwicklung, egal wo, was oder in welcher Programmiersprache man entwickelt: Veränderung. Es ist eigentlich egal, wie gut man eine Anwendung entworfen hat. Im Lauf der Zeit wird sie wachsen und sich wandeln müssen.

Für die Wartung und Wiederverwendung von Elementen/Klassen einer Anwendung ist es besser, wenn nicht versucht wird, alles über Vererbung zu lösen. Wird stur vererbt, kann es schnell bei einer lokalen Änderung zu weitreichenden Seiteneffekten kommen. Dazu bietet sich das einfach Beispiel aus dem Buch “Entwurfsmuster von Kopf bis Fuß” an :) .

In einer Entensimulation gibt es die Superklasse Ente, die die Methoden quacken und schwimmen für alle Sub-Enten implementiert. Eine abtrakte Methode anzeigen wird von den Sub-Enten implementiert, um sich selbt darzustellen.

Nun sollen die Enten fliegen können.

Eine Möglichkeit per Vererbung packt die Methode fliegen direkt in die Superklasse Ente. Nur jetzt können z.B. auch Enten fliegen, die es gar nicht sollten, wie eine Gummiente. Wenn nun alle Enten die Methode fliegen überschreiben und praktisch leer implementieren, dann wäre das Problem gelöst, aber bei jeder neuen Ente muss man beachten, ob sie fliegen kann oder nicht.

Eine weitere Möglichkeit wäre die Verwendung eines FlugFähig-Interfaces, das nur von den Enten implementiert wird, die auch tatsächlich fliegen können. Allerdings wird dann Code verdoppelt, da alle fugfähigen Enten das Interface implementieren müssen. Ein Wartungsalptraum ;) .

Da also die Vererbung nicht der Weisheit letzer Schluss ist, kommt man zu folgendem Entwurfsprinzip:

Identifizieren der Aspekte, die sich ändern können und sie von denen trennen, die konstant bleiben.

In obigem Entenbeispiel wird die fliegen Methode in ein Interface herausgezogen und verschiedene Flugverhalten implementiert (z.B. “normales” fliegen und “nicht” fliegen). Jeder Ente wird nun eins der Flugverhalten-Implementierungen übergeben, das zu ihr passt.

Die unterschiedlichen Flugverhalten sind nun nur noch 1x implementiert und werden den Enten “aufgesteckt”. Das fördert Code-Wiederverwendung und die Änderung an einem Flugverhalten stört keine Enten, die ein anderes Flugverhalten haben.

Wie das konkret im Quellcode aussieht, gibts im nächsten Teil über Programmieren auf eine Schnittstelle.

Literatur

  • FREEMAN, E und FREEMAN, E: Entwurfsmuster von Kopf bis Fuß. O’Reilly, 2006.

Jul 11 2009

Wordpress Update

Eben Wordpress auf aktuellen Stand gebracht.

Jetzt muss ich endlich mal anfangen, mehr zu bloggen :-) .


Aug 17 2008

Singleton in Java

Der Artikel Singleton Pattern in Java beschreibt einige Implementierungen des Singletons in Java, bevor am Ende auf die bisher oft angetroffene und favorisierte Lösung eingangen wird.

Aber auch diese favorisierte Lösung hat in bestimmten Situationen Nachteile:

  • Bei entsprechend privilegiertem Zugriff können per Reflection immer noch zusätliche Instanzen der Singleton-Klasse erzeugt werden.
  • Soll das Singleton Serializable sein, so müssen alle Felder als transient markiert werden und die Methode readResolve() muss implementiert werden:

    private Object readResolve()
      return INSTANCE; // Just return the single instance..
    }
    

Setzt man Java 1.5 (oder später) ein, kann man das Singleton Pattern über einen Enum realisieren! Hier bekommt man eine sehr kleine "Klasse", die beide obigen Nachteile nicht hat!

public enum Singleton {
  // Guaranteed to be the single instance
  INSTANCE;

  public void doSomething() {...}
}

Diese Art des Singletons ist laut Item 3 in Effective Java die beste Art das Singleton Pattern in Java zu implementieren.

Literatur

Bücher

Links


Jul 24 2008

equals() in Java

Die equals() Methode gehört in Java automatisch zur öffentlichen Schnittstelle einer jeden Klasse, da sie jede Klasse von Object automatisch erbt.

Da die Collections in Java viel Gebrauch von der equals() Methode machen, lohnt es sich mal einen tieferen Blick darauf zu werfen!

Theorie

Wann sollte equals() überschrieben werden?

equals() muss definitiv nicht für jede Klasse überschrieben werden! Vom Aufwand wäre es sogar am Einfachsten, sie gar nicht zu implementieren ;-) . Dann ist jede Instanz dieser Klasse nur zu sich selbst equal.

Grob gesagt kann man die equals() Methode bei Klassen weglassen, die “Dienste” anbieten und bei denen ihr Inhalt keine große Bedeutung hat. Ganz im Gegenteil bei Klassen, die bestimmte Werte repräsentieren. Diese Klassen sind auch Kandidaten für die Collections von Java und benötigen damit höchstwahrscheinlich eine eigens implementierte equals() Methode!

Der equals-Vertrag

Die folgenden Bedingungen kann man auch direkt der Dokumentation von Object entnehmen:

  • reflexiv: für x != null muss gelten, dass x.equals(x) true zurückgibt
  • symmetrisch: für x, y != null muss gelten, dass x.equals(y) nur true gibt, wenn auch y.equals(x) true gibt
  • transitiv: für x, y, z != null muss gelten, dass wenn x.equals(y) und y.equals(z) true ergibt, dann muss auch x.equals(z) true ergeben
  • konsistent: für x, y != null muss gelten, dass mehrmalige Aufrufe von x.equals(y) konsistent true oder false zurückgibt, sofern sich an den Objekten nichts ändert
  • x.equals(null) muss false zurückgeben (und keine NullPointerException!)

Sie beschreiben den Vertrag, den man als Programmierer dringenst einhalten sollte. Tut man das nicht, ist nicht zu 100% garantiert, dass sich z.B. die Collections Klasse so verhalten, wie sie sollen.

Die letzte Bedingung (x.equals(null) == false) lässt sich mit einem einfachen o instanceof Type prüfen, da instanceof schon direkt false liefert, wenn das Objekt o null ist!

Während Objekthierarchien durchaus mit super.equals(Object other) arbeiten können, dürfen direkte Subklassen von Object dies nicht tun! Da Object.equals(Object other) nur mittels == prüft, wird so gut wie immer false zurückgegeben, außer beide Objekte verweisen auf die gleiche Referenz. Das ist aber gerade dann, wenn man equals() überschreibt höchstwahrscheinlich nicht gewünscht.

Wird eine Unterklasse abgeleitet und dieser Klasse eine Membervariable hinzugefügt, dann lässt sich die Transitivität nicht mehr ohne weiteres herstellen! Die Symmetrie funktioniert noch, da die Member der Unterklasse beim Vergleich mit der Oberklasse ignoriert werden können. Aber bei der Transitivität müsste die Oberklasse wieder mit der Unterklasse verglichen werden und dann kommt man nicht mehr an die Member.

instanceof oder getClass()?

Es gibt im Prinzip 2 Lager: die instanceof-Verfechter und die, die getClass() favorisieren.

Mit getClass() stellt man sicher, dass definitiv beide Objekte (this und other) zur Laufzeit vom gleichen Typ sind (instanceof liefert ja auch beim Vergleich auf Oberklassen true). Man kann sich also ganz sicher sein, dass der equals-Vertrag eingehalten wird.

Allerdings wird argumentiert, dass die getClass() Methode das Substitutionsprinzip verletzt. Z.B. ist es nicht mehr möglich, von einer Klasse abzuleiten und nur Methoden zu überschreiben. Diese neue Unterklasse wird niemals equal mit ihrer Oberklasse sein.

Mit instanceof umgeht man dieses Problem und kann wieder das Substitutionsprinzip anwenden. Allerdings muss man obige Schilderung bedenken, dass man zu den abgeleiteten Klassen keine Member hinzufügen darf!

Allgemein bieten sich folgende Lösungen an:

  • Abstrakte Oberklassen, die keine eigenen Member definieren. Da man in diesem Fall die Oberklasse nicht direkt erzeugen kann.
  • Finale Klassen, die man erst gar nicht ableiten kann eignen sich natürlich hervorragend und arbeiten super mit instanceof zusammen. Diese Lösung ist sogar wahrscheinlich für fast alle “Werte”-Klassen optimal oder wer nutzt komplex verschachtelte Hierarchien von “Werte”-Klassen? Findet man sich darin noch zurecht? :-)

Implementierungsinfos

Ausgehend von der Theorie kann man mit folgenden “Punkten” die equals() Methode implementieren:

  1. == nutzen, um anfangs die eigene Identität zu prüfen. Das ist hauptsächlich eine Performance-Optimierung, die sich bei großen Objekten besonders auswirkt.
  2. Mit instanceof prüfen, ob das übergebene Objekt vom richtigen Typ ist (und damit wird ja auch gleich geprüft, ob das übergebene Objekt vielleicht null ist…).
  3. Das übergebene Objekt auf den eigenen Typ casten.
  4. Für jede signifikante Membervariable prüfen, ob sie bei beiden Objekten übereinstimmt.

    • Primitive Member werden mit == verglichen (außer float und double).
    • float mit Float.equals() und double mit Double.equals() vergleichen, da es z.B. Float.NaN gibt!
    • Referenztypen mit equals() vergleichen.
    • Wenn die Referenztypen null sein können, dann bietet sich folgendes Konstrukt an:

      • (field == null ?
            other.field == null :
                field.equals(other.field))
        
    • Und wenn dabei auch noch field und other.field identisch sein können, bietet sich das Konstrukt an:

      • (field == other.field ||
            (field != null && field.equals(other.field)))
        
  5. Am Ende sollte nochmals geprüft werden, ob die implementierte equals() transitiv, symmetrisch und konsistent ist.

Um die Performance noch etwas zu optimieren, könnem speziell unveränderbare (immutable) Objekte ihren kanonische Form vergleichen satt über alle Member zu gehen. Außerdem sollten die Member, die sich zwischen 2 Objekte oft unterscheiden anfangs überprüft werden, damit sich equals() in dem Fall schnell beenden kann.

Ganz wichtig: wann immer equals() überschrieben wird, muss auch hashCode() überschrieben werden!

Implementierungsbeispiel

public final class Person {
  private final Address address;
  private String prename;
  private int age;
  // ... more members ...

  // ... methods ...

  @Override
  public boolean equals(Object other) {
    if(other == this) {
      return true;
    }
    if(!(other instanceof Person)) {
      return false;
    }

    Person p = (Person) other;
    return this.age == p.age &&
      this.prename.equals(p.prename) &&
      (this.address == null ?
          p.address == null :
              this.address.equals(p.address)) &&
      // ... other comparisons ...
  }

  // ... methods ...
}

Literatur

Bücher

  • BLOCH, J.: Effective Java. Addison-Wesley, 2nd Edition, 2008.

Links


Feb 24 2008

Mehrere Inspektoren auf einmal öffnen

Es gibt bei Programmen, die die Inspektoren benutzen einen Weg, mehrere Inspektoren auf einmal zu öffnen.

Zuerst muss dafür bereits ein Inspektor offen sein. Anschließend klickt man, während die Option-Taste (bzw. die Alt-Taste) gedrückt ist auf einen anderen gewünschten Inspektor. Je nach Platz auf dem Desktop ist das sicherlich ganz nett :) .


Feb 9 2008

Komplette Pfadanzeige im Finder

Mit folgendem Tipp, den ich bei James gefunden habe, zeigt der Finder in der Titelleiste nicht nur den aktuellen Ordnernamen sondern den kompletten Pfad an.

defaults write com.apple.finder _FXShowPosixPathInTitle -bool YES

Mit dem Parameter NO kann man das auch wieder rückgängig machen, aber wofür? ;-)

Aktiviert wird die Anzeige des kompletten Pfads mit einem Neustart des Finders, z.B. durch kurzes Ausloggen oder Neustarten des Rechners. Ich bin mir nicht mehr ganz sicher, ob das Standardverhalten ist oder durch eines meiner installieren Programme kommt, aber ich kann auch den Finder mit Alt+Rechtsklick anwählen und dann einen Relaunch des Finders durchführen.