Hallo liebe Freunde des guten Softwaredesigns,
ich habe in der letzten Zeit viele Dinge an den internen APIs gebastelt und möchte einige Ideen, die ich dort umgesetzt habe, an Euch weitergeben.
Dabei habe ich mir Gedanken über die Sicherung öffentlicher APIs gemacht. Das Hauptproblem ist leicht erklärt. Eine Klasse hat Setter und Getter, jedoch soll nicht jeder die Setter aufrufen dürfen. Was tun? Man macht die Setter nicht public. Dann dürfen aber nur Klassen aus demselben Package darauf zugreifen.
Des Öfteren hatte ich die Anforderung, dass ich einen Setter aus einer Klasse außerhalb des Packages aufrufen musste. Jedoch wollte ich wie gesagt nicht, dass Hinz und Kunz die Setter aufrufen dürfen, das sollte nur nach einer Autorisierungsprüfung möglich sein. Was tat ich also?
Dazu kommt, dass der Umgang mit den Settern nur in wenigen Einzelfällen notwendig ist, nämlich wenn über eine Managementoberfläche Änderungen an den Objekten vorgenommen werden müssen. In 99 Prozent aller Fälle reicht es aus, wenn die Getter-Methoden verfügbar sind. Vor allem, wenn man ein Objekt nach außen gibt, etwa jemandem, dem man nicht so richtig traut, ist Schutz geboten!
Um dieses abstrakte Gefasel klarer werden zu lassen, sei hier ein Beispiel genannt, auf das die geschilderte Problematik zutrifft.
Ich habe für die Weiterentwicklung von C1, das bisher nur XML-Dateien als "managed contend" unterstützte, zu einem System, das beliebige Dateitypen verwalten kann, die bereits bestehende Verwaltung von Dateitypen und deren Versionen aufgebohrt und eine API dafür gebaut.
Instanzen von FileType repräsentierten nun einen Dateityp, und Instanzen von FileTypeVersion eine Version eines bestimmten Dateityps. Z.B.
"HTML": FileType, oder "HTML 4.0": FileTypeVersion.
Für diese beiden Interfaces habe ich Implementierungen in FileTypeImpl und FileTypeVersionImpl.
Jetzt hatten diese Klassen leider einige Setter, die ich nicht öffentlich exponieren wollte, da die Setter nur beim Laden und beim zentralisierten Management der Dateitypen in der Administrationsobeerfläche benötigt werden.
Hier nun meine Lösung:
Ich teilte die Schnittstelle der Klasse auf in zwei Interfaces. Eines, FileType, enthält nur die Getter und das zweite, MutableFileType, ist davon abgeleitet und erweitert das Interface um die Setter.
Um die Implementierung nun soweit abzusichern, damit niemand einfach ein FileType-Objekt auf MutableFileType casten kann, aber die Kapselung der Implementierung nicht aufzubrechen, habe ich folgendes gemacht:
- Die Klasse FileTypeImpl
- nicht public
- final
- implementiert FileType
- enthält sowohl Getter als auch Setter.
- die Setter sind jedoch nicht public.
- Die Klasse MutableFileTypeImpl
- nicht public
- final
- implementiert MutableFileType (Setter werden public)
- ist eine Fassade für eine FileTypeImpl-Instanz
(Alle Aufrufe, auch die der Setter, werden an die Instanz
von FileTypeImpl delegiert, auch hashCode() und equals(Object))
- Der Konstruktor führt einen Aufruf unseres AccessDeciders
durch, der eine Exception wirft, falls dem aktuellen User die
Berechtigung zur Verwaltung der Dateitypen fehlt.
Die Folgen:
- Niemand kann eine Instanz von FileTypeImpl auf MutableFileType casten, weil sie dieses Interface nicht implementiert.
- Niemand kann so einfach eine Instanz von MutableFileTypeImpl erzeugen, weil da eine Permission geprüft ist.
Sylphen ist ein IT-Systemhaus mit Niederlassungen in Gießen, Frankfurt und Hamburg und unterstützt Sie
durch Berater und Programmierer bei der effizienten Software-Entwicklung im Bereich
Java, SQL und Integration. Unter 0641-94468-0 oder
info@sylphen.com beraten wir Sie gerne
unverbindlich.
zurück zu Customer-Solutions