# Wo hin mit static factory methods?



## Marco13 (10. Mai 2012)

Hi

Gegeben ist eine package-Struktur wie

```
(interfaces)  : interface Something { }
    |
    +--(impl) : class DefaultSomething implements Something { }
```
Also ein Package "interfaces", wo ein interface "Something" drin liegt, und ein Unterpackage "impl", wo eine Default-Implementierung des Something-Interfaces liegt. Zum Erstellen von Instanzen gibt es eine Klasse mit einer statischen factory-Methode

```
class Somethings
{
    public static Something createSomething()
    {
        return new DefaultSomething();
    }
}
```
Wo wäre dann der geeignetste Ort für diese Klasse? Eigentlich sollte sie _nicht_ im Impl-Package liegen, weil sie ja in bezug auf die API (ja eben gerade) _nicht_ implementierungsspezifisch ist (und eigentlich niemand irgendwas im "impl"-Package zu suchen hat). Aber wenn man sie _nicht_ dort hin legt, sondern ins interfaces-Package oder woanders hin, muss die Default-Implementierung "public" sein, was ja genau das ist, was man eigentlich _nicht_ will (die Impementierung sollte package private sein). 

Sowas wie OSGi oder ein DI-Framework erscheint mir für sowas (eine kleine Bibliothek eben) leicht als Overkill. 

Im Moment liegen die Factory-Method-Klassen bei mir im Interfaces-Package. Aber ich tendiere dazu, sie ins Impl-Package zu schieben, weil die nicht-Sichtbarkeit der Default-Implementierungen IMHO ein stärkeres Argument ist, als die "Abgeschirmtheit" des Impl-Packages...

(Mal kurz überlegen, ob das überhaupt so wichtig ist, dass ich darüber nachdenken und sogar einen Thread eröffnen sollte :reflect: )

(Ja  )

Gibt's irgendwelche gefestigten, fundierten Meinungen oder best practices dazu?


----------



## nillehammer (10. Mai 2012)

> Aber ich tendiere dazu, sie ins Impl-Package zu schieben, weil die nicht-Sichtbarkeit der Default-Implementierungen IMHO ein stärkeres Argument ist, als die "Abgeschirmtheit" des Impl-Packages...
> 
> (Mal kurz überlegen, ob das überhaupt so wichtig ist, dass ich darüber nachdenken und sogar einen Thread eröffnen sollte )
> 
> ...


Das Wichtigste ist, dass die Sichtbarkeit der Impl-Klassen package-private ist. Nur so wird zuverlässig verhindert, dass ein Nutzer meiner API nicht direkt dagegen programmiert. Das heißt, eine Factory muss ins Impl-Package. Im Interface-Package hab ich dann auch eine Factory, die an die Impl-Factory delegiert. So hat der Nutzer meiner API die Möglichkeit, alles mit dem Interface-Package zu erledigen.

Nun muss bei diesem Konstrukt leider die Impl-Factory public sein und könnte von einem unvorsichtigen Programmierer verwendet werden. Das kann man zwar nicht verhindern, aber man kann die Impl-Factory-Klasse als @Deprecated annotieren und im JavaDoc explizit darauf hinweisen. Um die Warnungen in der Interface-Factory wegzukriegen, setzt man über diese ein @SuppressWarnings("deprecation").

Das ist denke ich der sicherste Weg. Das ist meine gefestigte und fundierte Meinung und so mache ich es, wenn ich nicht auch mit DI arbeite.


----------



## faetzminator (10. Mai 2012)

Nunja, was heisst _nicht sehen_? Muss die Klasse selbst unbedingt [c]private[/c] sein, oder dürfte könnte man auch mit dem Konstruktor rumspielen? Wenn du dort irgendwelche Argumente übergeben müsstest, könntest du diese mit einem Wrapper übergeben, dessen Konstruktor wieder private ist (und die Klasse final). Allerdings würde ich da eher vorziehen, das in das [c]impl[/c] Package zu verschieben. Schlussendlich gibt die Methode eine Implementation zurück, insofern... Naja, wie auch immer 

Edit: nillehammers Delegate ist gar keine schlechte Idee.


----------



## bygones (10. Mai 2012)

gibts dafuer in c nicht das friends - prinzip ? 

da Java das nicht hat, musst du irgendein kompromiss eingehen... der delegationsschritt ist eigentlich ein guter. Den Leuten per API/doku klar machen die Interface Factory zu nehmen und gut is


----------



## HimBromBeere (10. Mai 2012)

Ich finde die Abschiebumg in die Implementierungen auch am sinnvollsten, schließlich handelt es sich bei einer Fabrik ja um nichts anderes als einen Ort, in dem konkrete Objekte gebaut werden. In deinem Fall ja ein DefaultSomething, was ja eben eine Implementierung ist.


----------



## maki (10. Mai 2012)

Wenn du nicht planst die Interfaces "zusätzlich" nochmal anders zu implementieren, würde ich die Factory im Interface Package lassen, die Implementierungen public machen und per Doku/JavaDoc darauf Hinweisen wie es zu nutzen ist.


----------



## nillehammer (10. Mai 2012)

maki hat gesagt.:
			
		

> Wenn du nicht planst die Interfaces "zusätzlich" nochmal anders zu implementieren, würde ich die Factory im Interface Package lassen, die Implementierungen public machen und per Doku/JavaDoc darauf Hinweisen wie es zu nutzen ist.


Sorry, ist mir nicht stark genug. Es gibt so viele gedankenlose Programmierer, die weder verstehen, was: "Programmiere gegen das Interface!" heißt, noch sich die JavaDocs durchlesen. Sonst müsste man hier im Forum nicht so oft Fragen zu einfachsten Themen beantworten und sowas würde man auch nicht immer wieder lesen müssen:

```
ArrayList<String> strList = new ArrayList<>();
// Verdammt, warum ArrayList als Typ? Nimm List, Du Voll****!
//... ist dann immer das, was ich denke
```
Durch ein striktes API-Design zwingst Du auch die Dooven, es richtig zu machen.


----------



## Marco13 (11. Mai 2012)

Man muss wohl damit rechnen, dass man bei der Frage, welche von zwei Lösungen die bessere ist, n>2 verschiedene Vorschläge bekommt 

@nillehammer: Die Delegation der factory-Methoden vom Interface-Package an eine Deprecated class im Impl-Package ist nochmal ein "weicher constraint", der die falsche Verwendung weniger wahrscheinlich (aber nicht unmöglich) macht. Kein sooo großer Nutzen, und eine "from scratch geplante deprecation" ist irgendwie unschön, aber eine Überlegung wert...

@faetzminator: Hauptsächlich geht es um die Sichtbarkeit der Klassen, und nicht um ihre Verwendbarkeit oder Instantiierbarkeit. 

@bygones: Der C++-Friend kam mir da auch in den Sinn - das wäre ein Parade-Anwendungsbeispiel 

@HimBromBeere & maki: Ja, das sind die beiden ursprünglich angedachten Optionen ... einigt euch, und sagt mir dann, was rausgekommen ist  

Schwierig wird das auch, weil es im konkreten Fall "viele" Interfaces gibt (40 oder 50 vielleicht), und einige davon sind so trivial und ihr Anwendungsbereich so eingeschränkt, dass ich eben schon eine Klasse erstellt habe, die für ca. 10 von ihnen instanzen erstellen kann (statt 10 Klassen mit jeweils einer static factory methode). Die Default-Implementierungen haben im Moment standardmäßig sowieso keine public Konstruktoren, sondern selbst wieder Factory-Methoden, und die könnten ggf. auch wegfallen wenn die Klasse ohnehin nicht mehr public ist.


----------



## schalentier (11. Mai 2012)

Marco13 hat gesagt.:


> Man muss wohl damit rechnen, dass man bei der Frage, welche von zwei Lösungen die bessere ist, n>2 verschiedene Vorschläge bekommt



ja! ;-)



			
				nillehammer hat gesagt.:
			
		

> Durch ein striktes API-Design zwingst Du auch die Dooven, es richtig zu machen.



Ja, aber auch du noetigst auch die Guten zu viel Copy&Paste.
Anstatt irgendwas mit viel Balast zu Verhindern, wuerd ich lieber die allgemeine Benutzbarkeit der API verbessern. Jeder Entwickler nimmt den Weg des geringsten Widerstands, wenn dieser schnell und einfach zu finden ist oder sich logisch ergibt. 



Marco13 hat gesagt.:


> ...eine Klasse erstellt habe, die für ca. 10 von ihnen instanzen erstellen kann (statt 10 Klassen mit jeweils einer static factory methode).



Genau. Wieviele verschiedene Implementierungen eines Interface gibt es denn eigentlich? 

Ich denke, ohne den konkreten Fall kann man keine sinnvollen Hinweis geben. Rein von den Fakten her klingt das wie: Du hast 50 Interfaces, dazu 50 Implementierungen und dazu 50 Factories. Und _das_ wuerd ich an dieser Stelle zuerst hinterfragen.

Ansonsten wuerd ich soviele Factory Klassen wie moeglich zusammenfassen.


----------



## nillehammer (11. Mai 2012)

Marco hat gesagt.:
			
		

> @nillehammer: Die Delegation der factory-Methoden vom Interface-Package an eine Deprecated class im Impl-Package ist nochmal ein "weicher constraint", der die falsche Verwendung weniger wahrscheinlich (aber nicht unmöglich) macht. Kein sooo großer Nutzen, und eine "from scratch geplante deprecation" ist irgendwie unschön, aber eine Überlegung wert...


@Marco: 
Ja, aber im Gesamtkonstrukt kann nur noch die Factory im Impl-Package unerwünschter Weise benutzt werden. Die Impl-Klassen ja nicht. Damit ist die "Angriffsfläche" schon mal viel kleiner. Darin sehe ich den sehr grooßen Nutzen.

Damit der Nutzer absolut keine Klasse aus dem Impl-Package importieren muss, gibt es eben die delegierende Factory. Im Extremfall könnte ich also das ganze Impl-Package wegschmeißen und ein komplett neues bauen. Dieser Nutzen ist dann vielleicht wirklich nicht mehr sooo groß, weil das eher unwahrscheinlich ist. Obwohl, ich sitze gerade an einer Anwendung, wo ich Hibernate-Dao-Impls auf JPA-Dao-Impls umstellen muss, da ist im Clientcode viel Refactoring angesagt...

Aufgrund der Tatsache, dass die Factory im Impl-Package nunmal public sein muss, gibt es keinen Weg die Nutzung hart zu verhindern. Man kann über die Deprecation nur einen möglichst deutlichen Hinweis geben. Und wer sagt denn, dass Deprecated Code nur Code sein darf, der über kurz oder lang rausfliegt? In der Beschreibung von Deprecated steht jedenfalls, dass damit Code gekennzeichnet wird, von dessen Nutzung aus verschiedensten Gründen abgeraten wird.

@schalentier:
Ich glaube, Du hast nicht genug über das Konzept nachgedacht, bevor Du Deine doch sehr allgemein gehaltenen Kommentare geschrieben hast.


			
				schalentier hat gesagt.:
			
		

> Ja, aber auch du noetigst auch die Guten zu viel Copy&Paste.


Wo zwinge ich denn jemanden zu Copy&Paste. Variable vom Typ _<Interface>_ definieren, Factory-Methode aufrufen, fertig. Wo kommt da Copy&Paste vor?


			
				schalentier hat gesagt.:
			
		

> Anstatt irgendwas mit viel Balast zu Verhindern, wuerd ich lieber die allgemeine Benutzbarkeit der API verbessern.


Da ist weder für den Nutzer der API noch für mich Ballast. Interfaces und Klassen hat man sowieso. Ne Factory-Klasse auch. Das einzig neue ist die Factory-Klasse, die delegiert. Die Menge an Code ist für für mich eine Klasse mehr, für die Nutzer der API gleich.


			
				schalentier hat gesagt.:
			
		

> Jeder Entwickler nimmt den Weg des geringsten Widerstands, wenn dieser schnell und einfach zu finden ist oder sich logisch ergibt.


Die Benutzung einer Factory, die im selben Package liegt, wie die Interfaces, die ich benutzen will, halte ich für sehr logisch, logischer geht's garnicht mehr!


----------



## schalentier (11. Mai 2012)

nillehammer hat gesagt.:


> Wo zwinge ich denn jemanden zu Copy&Paste. Variable vom Typ _<Interface>_ definieren, Factory-Methode aufrufen, fertig. Wo kommt da Copy&Paste vor?



Wenn ich als Benutzer eine zweite Implementierung bauen moechte, die nur ein kleines Fitzelchen anders machen soll, als die Referenzimplementierung, dann bleibt mir nix andres uebrig, als diese in mein eignes Package zu kopieren und anzupassen. Oder ich hab was falsch verstanden. ;-)



nillehammer hat gesagt.:


> Die Menge an Code ist für für mich eine Klasse mehr, für die Nutzer der API gleich.



Naja, bei 50 Interfaces, 50 Implementierungen und 50 Factories, hast du nochmal 50 Delegation-Factories. Das seh ich schon als Balast.

Aber wie gesagt, ohne das konkrete Einsatzgebiet find ich es schwierig eine wirklich gute Antwort zu geben.


----------



## maki (11. Mai 2012)

nillehammer hat gesagt.:


> Sorry, ist mir nicht stark genug. Es gibt so viele gedankenlose Programmierer, die weder verstehen, was: "Programmiere gegen das Interface!" heißt, noch sich die JavaDocs durchlesen. Sonst müsste man hier im Forum nicht so oft Fragen zu einfachsten Themen beantworten und sowas würde man auch nicht immer wieder lesen müssen:
> 
> ```
> ArrayList<String> strList = new ArrayList<>();
> ...


Sorry, aber selbst mit deinen Vorschlägen (2 Factories & Delegation) ist die API nicht strikt genug um mögliche Fehlbedienung zu vermeiden (Reflection bietet immer noch eine Alternative, oder direkt die Impl Factory aufrufen während man die "Interface" Factory umgeht), es wird nur mehr Code geschrieben mit der Hoffnung dass dann die Blinden Farben unterscheiden können.
Sowas wie "Idiotensicher" gibt es nunmal nicht, auch wenn es beim API Design wünschenswert wäre.

Man kann Doofe eben nicht zwingen das richtige zu tun, man kann aber die Zusammenhänge der API klar niederschreiben, dass zumindest Leute die lesen können eine gute Chance haben das zu verstehen.

Nebenbei, manchmal muss es wirklich ArrayList sein, weil eine LinkedList keinen Sinn machen würde (Wahlfreier Zugriff, etc. pp.).


----------



## Marco13 (11. Mai 2012)

Ja, dass so viele Methoden ""stumpfsinnig"" weiterdelegiert werden müßten, war auch etwas, was ich daran nicht so schön fand, aber das konnte man beim Eingangspost ja nicht ahnen.



> Du hast 50 Interfaces, dazu 50 Implementierungen und dazu 50 Factories. Und das wuerd ich an dieser Stelle zuerst hinterfragen.



Was genau? Die Interfaces? Die Implementierungen?  Oder die Factories (Factory-methoden, von denen ich ja ggf. welche zusammenfasse) ?


----------



## Marco13 (11. Mai 2012)

@maki: Es stimmt schon, dass man versuchen sollte, es dem Benutzer schwer zu machen, eine API falsch zu verwenden. Dabei geht es nicht um "Sicherheit" im Sinne einer "Firewall" - dass man mit Reflection böse Sachen machen kann, ist klar, aber das verwendet man ja nicht "versehentlich". Die Gefahr, dass jemand eine Klasse Instantiiert, die er eigentlich nicht kennen und direkt verwenden sollte, ist aber sehr real, auch wenn in der JavaDoc steht, dass er sie nicht verwenden soll. WIE viele "Tricks und Hacks" man dafür einsetzen kann oder sollte, weiß ich nicht. Es gibt auch solche Sachen wie

```
public abstract class InterfaceThatJustJoeCanImplement {
    protected InterfaceThatJustJoeCanImplement() {
        if (!"impl.joe.JoesImpl".equals(getClass().getName())) {
            throw new IllegalStateException(
                "Sorry, you are not allowed to implement this class"
            );
       }
    }
    public abstract void everyoneCallThisJoeWillHandleTheRequest();
}
```
(aus "Practical API Design - Confessions of a Java™ Framework Architect" von Jaroslav Tulach)
was man in ähnlicher Form für Instantiierungen verwenden könnte, notfalls indem man den StackTrace abfragt oder so :autsch: Aber irgendwann wird das (subjektiv) schon ziemlich "unschön", bzw. der Vorteil ist im Vergleich zum Aufwand sehr gering.

Dass die Implementierungen nicht public sein sollten, steht für mich eigentlich fest (auch wenn sie es im Moment sind - aber das nervt mich ja gerade). Dass die Factory-Methoden-Klasse dann im Impl-Package liegen muss, und man NUR deswegen das Impl-Package kennen und verwenden muss, ist zwar nicht schön, aber es führt kein Weg dran vorbei - selbst wenn man noch einen Delegation-Schritt einfügt.


----------



## schalentier (11. Mai 2012)

Marco13 hat gesagt.:


> Was genau? Die Interfaces? Die Implementierungen?  Oder die Factories (Factory-methoden, von denen ich ja ggf. welche zusammenfasse) ?



Ich frag mich, wozu man ein Factory Pattern auf Klassenebene braucht, wenn es nur genau eine Implementierung gibt.

Natuerlich gibt es Faelle, wo sowas sinnvoll sein koennte (das will ich ueberhaupt nicht abstreiten), die Frage ist aber, ob das bei dir so ein Fall ist. Aus den bisherigen Informationen kann ich das aber nicht ableiten, deswegen wuerde ich zuerst ueberpruefen, ob man nicht einfach auf die ganzen Interfaces + Factories verzichten koennte und eben die Implementierungen ganz normal benutzt. 

Welches konkrete Problem loesen denn die 50 Factories?


----------



## tfa (11. Mai 2012)

Wie wär's denn mit einem Factory-Interface (oder einer abstrakten Faktory) im öffentlichen "Interface"-Package und einer entsprechenden Implementierung im impl-Package? Die Verknüpfung im Client-Code könnte man leicht durch DI herstellen.


----------



## nillehammer (11. Mai 2012)

> Wenn ich als Benutzer eine zweite Implementierung bauen moechte, die nur ein kleines Fitzelchen anders machen soll, als die Referenzimplementierung, dann bleibt mir nix andres uebrig, als diese in mein eignes Package zu kopieren und anzupassen. Oder ich hab was falsch verstanden.


Wir diskutieren hier darüber, wie man die Implementierungen am besten wegkapselt. Behalte das bitte im Hinterkopf. D.h. die Implementierungsdetails sollen dem Nutzer verborgen bleiben. Das macht man, damit man hinter den Kulissen Änderungen machen kann, ohne Clientcode zu zerstören.

Wenn dem Nutzer die Implementierung einer best. Methode nicht gefällt, kann er auch Komposition benutzen, sprich er implementiert das Interface. Die Methode, die ihm nicht gefällt, schreibt er selbst und in den anderen ruft er die Defaultimplementierung auf. You see, no need for Copy&Paste.



> Naja, bei 50 Interfaces, 50 Implementierungen, 50 Factories


Die hast Du sowieso, das ist bei *allen* hier diskutierten Varianten die Gemeinsamkeit. Abgesehen davon muss man ja nicht für jedes Interface eine eigene Factoryklasse haben.


> , hast du nochmal 50 Delegation-Factories


Ja, das ist der einzige Ballast. Wie gesagt, man muss ja nicht für jedes Interface eine eigene Factory bauen. Damit reduziert sich das.



			
				maki hat gesagt.:
			
		

> Sorry, aber selbst mit deinen Vorschlägen (2 Factories & Delegation) ist die API nicht strikt genug um mögliche Fehlbedienung zu vermeiden (Reflection bietet immer noch eine Alternative, oder direkt die Impl Factory aufrufen während man die "Interface" Factory umgeht), es wird nur mehr Code geschrieben mit der Hoffnung dass dann die Blinden Farben unterscheiden können.
> Sowas wie "Idiotensicher" gibt es nunmal nicht, auch wenn es beim API Design wünschenswert wäre.


Abgesehen von Reflection hab ich das alles schon geschrieben. Ist insofern kein Gegenargument. Was ich behaupte ist, dass das die sicherste aller *möglichen* Alternativen ist. Das ist bisher nicht widerlegt. Ist auf jeden Fall sicherer als alles public zu machen und nur zu dokumentieren.

Und zu dem Reflection-Argument. Wenn ich in meiner API etwas public mache, und jemand das dann nutzt, hat das doch wohl eine ganz andere Qualität als, wenn der Nutzer mit Reflection sämtliche Regeln umgeht oder? Wenn er das tut, darf er sich wirklich nicht beschweren, wenn sein Code nach einer API-Änderung nicht mehr funktioniert. Während er das im ersten Fall mit Recht dürfte.


----------



## maki (11. Mai 2012)

nillehammer hat gesagt.:


> Abgesehen von Reflection hab ich das alles schon geschrieben. Ist insofern kein Gegenargument. Was ich behaupte ist, dass das die sicherste aller *möglichen* Alternativen ist. Das ist bisher nicht widerlegt. Ist auf jeden Fall sicherer als alles public zu machen und nur zu dokumentieren.


nillehammer,

nur das wir uns richtig verstehen:
Finde deine Vorschläge gut, muss aber aus Prinzip des Teufels Advokat spielen 

Das mit dem Deprecation Annotations *Missbrauch* finde ich persönlich nicht so gut, die Klassen sind nunmal nicht Deprecated, ist nur ein Versuch den Nutzer auf etwas hinzuweisen... da wird ein Sprachmittel missbraucht um eine Sprachdefizienz auszugleichen, ist nicht unüblich, aber imho nicht der Weisheit letzter Schluss.
Abgesehen davon, wenn "Semi-Amateure" proggen, sind die Warnings in Eclipse zB. mal schnell im 4 stelligen Bereich (hab ich leider schon öfters gesehen), die eine oder andere mehr wegen deprecation wird  dann schon vollautomatisch ignoriert.



> Und zu dem Reflection-Argument. Wenn ich in meiner API etwas public mache, und jemand das dann nutzt, hat das doch wohl eine ganz andere Qualität als, wenn der Nutzer mit Reflection sämtliche Regeln umgeht oder? Wenn er das tut, darf er sich wirklich nicht beschweren, wenn sein Code nach einer API-Änderung nicht mehr funktioniert. Während er das im ersten Fall mit Recht dürfte.


Das ist für mich dasselbe 
In dieselbe Schublade gehören für mich Leute, die die Doku nicht lesen, dürfen sich auch nicht beschweren wenn sie was machen was sie nciht sollten und danach jammern weil nach einer internen Lib Änderung ihr Code kaputt ist.

"sicherer" ist eben nicht "sicher", egal was man sich einfallen lässt, irgendein "Idiot" findet immer einen Weg das falsch zu machen, deswegen mein Hinweis daraufm, dass man bestimmte Dinge auch Vorraussetzen muss.

Alles in allem mag ich deine Lösung (bis auf die Deprecation Sache), denke aber man kann sich ein bisschen Code sparen (delegierende Factory, Deprecation und damit die Suppress Warnings, usw.) , weil in der Realität würde es IMHO keinen Unterschied machen, kann mich da aber täuschen.


----------



## schalentier (11. Mai 2012)

Koennte man nicht auch eine generische Factory bauen (in einem extra Package oder so), die ein Interface entgegen nimmt und die Default Implementierung per Reflection ermittelt?

Also:

```
public <T> T create( T interface ) {
   ...
}
```

Also quasi Mini Guice ;-) Kann man per Reflection einen privaten Konstruktor aufrufen?


----------



## nillehammer (11. Mai 2012)

maki hat gesagt.:
			
		

> Das mit dem Deprecation Annotations Missbrauch finde ich persönlich nicht so gut,


Das ist nachvollziehbar. Mir ist an der Stelle nichts Besseres eingefallen. Ist halt der klägliche Versuch, den Nutzer zu erziehen. Wenn er die Doku nicht liest, fragt er sich vielleicht, warum in seiner IDE Code durchgestrichen oder gelb unterkringelt ist (so stellt Eclipse das zumindest dar). Wenn der Nutzer auch das missachtet, gibt es vielleicht ein Buildsystem, wo die Warnungen dann in Logs auftauchen und der Buildmanager fragt mal nach... Ich würde das jetzt vielleicht nicht als _Missbrauch_ bezeichnen, aber ein bischen krückenmäßig ist das auf jeden Fall.


			
				maki hat gesagt.:
			
		

> Alles in allem mag ich deine Lösung (bis auf die Deprecation Sache), denke aber man kann sich ein bisschen Code sparen (delegierende Factory, Deprecation und damit die Suppress Warnings, usw.) , weil in der Realität würde es IMHO keinen Unterschied machen, kann mich da aber täuschen.


Damit kann ich leben. Es ist tatsächlich Mehraufwand und Krücke mit wenig Gewinn, aber ich bin da immer etwas authistisch und will es so perfekt wie möglich haben  Das ist der Grund, warum ich mich selbst *nicht* als guten Entwickler bezeichne. Mein Code ist zwar ein Kunstwerk, aber ich brauch auch immer drei Mal solange (mindestens)...


----------



## maki (11. Mai 2012)

Natürlich war "Missbrauch" überzeichnet, aber des Teufels Advokat mag eben Aufmerksamkeit und nutzt gerne mal eine reisserische Formulierung, selbst wenn diese über das Ziel hinausschiesst 

Das Problem ist eben, dass "nacktes" Java da nicht viel bietet, aber wie gesagt, finde deine Lösung gut.
Ansosnten solltest du nicht so bescheiden sein


----------



## Gast2 (11. Mai 2012)

Mach doch mal ein kleines EMF Projekt, generier den Code und schau ihn doch mal dort an, finde ich ganz gut gelöst vielleicht findest, da einen schönen Ansatz.

Eclipse Modeling Framework (EMF) - Tutorial

Grob gibt es da keine statischen Methoden in den Factories, außer eine statische Variable in der Interface Factory die an die Impl Factory weiterleitet. Außerdem kannst du auch immer über die Class ein Objekt bekommen

Beispiel Person:

```
public interface MyFactory extends EFactory
{
    /**
     * The singleton instance of the factory.
     * <!-- begin-user-doc -->
     * <!-- end-user-doc -->
     * @generated
     */
    DefaultnameFactory eINSTANCE = impl.MyFactory();
    Person createPerson();
    EObject create(EClass eClass);
```


```
public class MyFactoryImpl extends EFactoryImpl implements DefaultnameFactory
{

    @Override
    public EObject create(EClass eClass)
    {
        switch (eClass.getClassifierID())
        {
            case DefaultnamePackage.PERSON: return createPerson();
            default:
                throw new IllegalArgumentException("The class '" + eClass.getName() + "' is not a valid classifier");
        }

@Override
   public Person createPerson()
    {
        PersonImpl person = new PersonImpl();
        return person;
    }

    }
```


----------



## nillehammer (11. Mai 2012)

TeufelsAdvokat hat gesagt.:
			
		

> aber des Teufels Advokat mag eben Aufmerksamkeit und nutzt gerne mal eine reisserische Formulierung, selbst wenn diese über das Ziel hinausschiesst


Jo, mag ich auch, sonst wär ich schon lange aus dem Thread hier ausgestiegen. Danke nochmal an Marco für seine Ursprungsfrage :toll:


----------



## fastjack (11. Mai 2012)

Was ist denn nun, wenn ich mal eine von den Implementierungsklassen beerben will? Bei den Vorschlägen hier alles abzusichern und auf friend zu setzen, wäre das dann nur noch möglich, wenn ich meine Klasse auch in das Package der zu beerbenden Klasse setze. Von der API Benutzung her ist das ätzend.
Das komplette Teil neuzuimplementieren, nur weil mir ein Aspekt nicht gefällt ebenso. Ein zu starkes absichern der API kann vor allem zu einem führen: Nicht-Benutzung


----------



## Marco13 (11. Mai 2012)

@schalentier und fastjack: Der Wunsch, "ein kleines Fitzelchen ändern zu wollen" bzw. davon erben zu wollen (um ein Fizelchen zu ändern) ist IMHO kein Grund, die Klasse public zu machen. Wenn man sich klarmacht, welche extrem strengen Anforderungen mit Vererbung einhergehen (allen voran: Lishkov Substitutionsprinzp), verwendet man sie automatisch seltener. Die Tendenz, irgendwelche "praktischen Funktionalitäten" in eine Klasse "rein-zu-erben" kann unschöne Folgen haben, wenn sich an der Klasse, von der man erbt, etwas ändert. Eine Klasse explizit _für Vererbung zu entwerfen_ macht vieles noch schwieriger, als die triviale Frage, wo denn diese static factory methods liegen sollen  

@SirWayne: Bei dem Code ist mir (mangels Kenntnis von EMF) nicht ganz klar, welche Teile EMF-Spezifisch sind (d.h. was von EMF vorgegebene Klassen/Interfaces sind), und wie man das verwenden würde.

Im Moment tendiere ich dazu, die Factory-Methoden-Klassen ins Impl-Package legen - das erscheint mir als der beste Trade-Off zwischen klarer Abkapselung und dem, was rein an Sprachmitteln zur Verfügung steht.


----------



## Gast2 (11. Mai 2012)

Marco13 hat gesagt.:


> @SirWayne: Bei dem Code ist mir (mangels Kenntnis von EMF) nicht ganz klar, welche Teile EMF-Spezifisch sind (d.h. was von EMF vorgegebene Klassen/Interfaces sind), und wie man das verwenden würde.



Du musst EMF ja nicht verwenden (was aber vielleicht gar nicht schlecht für dich ist dann kannst dir deine ganzen Gedanken sparen  ), aber der Aufbau sollte doch klar ein. Sogar die Package struktur kannst du dir anschauen.

dein.package
 -- Interfaces
 -- Factory interfaces
dein.package.util
-- util klassen
dein.package.util.impl
-- Factory implementierung
-- Klassenimplementierung

Wie gesagt versuch das Tutorial mal durch zu machen, das hast ja in 10 minuten gemacht. und schau was dabei rauskommt, vorrausgesetzt du entwickelst mit eclipse. Einfach EMF über die Update Site installieren, ist sogar im indigo/helios release distrubtion unter modeling dabei.


----------



## fastjack (11. Mai 2012)

Es gibt übrigens einen interessanten Beitrag zu diesem Thema:

Java API Design Guidelines


----------



## Marco13 (11. Mai 2012)

Nun, neben einigen allgemeinen (aber Interessierten schon hinlänglich bekannten) Tipps steht dort zu der eigentlichen Frage nur, dass die Implementierung am Packagenamen erkennbar und ihre Verwendung "discouraged" sein sollte. 

@SirWayne: Falls ich das jetzt richtig verstanden habe, wird da ja "nur" noch mal eine Indirektionsschicht eingezogen, indem man die Factories auch wieder als Interfaces beschreibt...?!


----------



## Gast2 (11. Mai 2012)

Marco13 hat gesagt.:


> @SirWayne: Falls ich das jetzt richtig verstanden habe, wird da ja "nur" noch mal eine Indirektionsschicht eingezogen, indem man die Factories auch wieder als Interfaces beschreibt...?!



Ja so wie oben beschrieben es gibt eben für alles Interfaces und Implementationen...
Wie gesagt, bevor du das alles selber immer runter schreibst ist das für dich vielleicht eine Alternative und der Code ist verständlich und super...


----------



## JohannisderKaeufer (11. Mai 2012)

Ich habe allerdings noch ein anderes Programmiermodel, dass sich Joi, Java over Interface nennt.

Hier wird das beschrieben:
Joi Programming Style 
Dort gibt es auch ein Tooling dass darum gebaut wurde, auf dass aber auch verzichtet werden kann. Prinzipiell sorgt das Tooling nur dafür das Checkstyle und Konsorten auf Verstöße gegen das Programmiermodel hinweisen.

Zusammengefaßt gibt es Interface der Business-Objekte und Factories die instanzen dieser Interface liefern.

Die Implementierungen sind alle als private Klassen oder private Enums (Singleton) umgesetzt.

Ein Beispiel

```
public interface HelloWorld{
void sayHello();
}
```


```
public class HelloWorldComponent{
  private static final class Impl implements HelloWorld{
    public void sayHello(){
      System.out.println("Hallo Welt");
    }
  }
  
  public static HelloWorld getInstance(){
    return new Impl();
  }

  private HelloWorldComponent(){
    throw new AssertionError();
  }
}
```

Dadurch das die Implementierungen als private Klassen umgesetzt sind ist das Ding dann schon ziemlich vernagelt.


----------



## Marco13 (11. Mai 2012)

Hmja... ist irgendwie hochtrabend aufgezogen (irgendwas MUSS man ja in seine Thesis schreiben ) aber rein konzeptuell (also abgesehen vom Tooling drumherum) ist das nur die Option, die Implementierungen als innere Klassen der Factory zu bauen ... wo da jetzt der gravierende Unterschied dazu ist, die Implementierungen einfach ins gleiche Package wie die Factory zu legen, hat sich mir noch nicht erschlossen, aber... vielleicht nochmal genauer schauen...


----------



## cmrudolph (12. Mai 2012)

Mir fallen da zwei Unterschiede ein:

wenn man die Factory zur Implementierung legt und alles bis auf die Factory Package Private macht, kann jemand anderes daherkommen und einfach eine Klasse schreiben und in das gleiche Package legen - die ganze Unsichtbarkeit ist dahin
wenn man alle Implementierungsklassen als innere Klassen realisiert, dann hat man den Nachteil, dass alle Implementierungsklassen in der gleichen Datei stehen


----------



## Gast2 (12. Mai 2012)

Ich sehe keinen Grund warum du die Implementierung verstecken willst. Wenn jemand hinkommen will um was zu verändern kommt er hin, nur du machst es im unnötig schwer.
In OSGi könntes vielleicht ein internal package machen und das nicht nach außen sichtbar machen.


----------



## Marco13 (12. Mai 2012)

Wie gesagt, es geht nicht um eine "Firewall". Natürlich kann jeder seine Klassen in ein Package legen, das genauso heißt wie mein Impl-Package, oder mit Reflection rumhantieren. 

Es geht z.B. daraum, dass
1. Das package mit den Interfaces NUR Interfaces enhält (für die Modellklassen).
2. Niemand in der JavaDoc zum "Something" auf die implementierende Klasse "DefaultSomething" aufmerksam gemacht wird, und in Eclipse dann [c]new DefaultS[/c]->[c]Strg+Space[/c]->[c]omething()[/c] schreibt (eben genau diesen Weg: Ohne, dass er in der JavaDoc den Satz liest: "Bitte diese Klasse nicht selbst instantiieren" - auch wenn man dann sagen könnte: Ja, selbst schuld wenn das in Zukunft nicht mehr funktioniert). 

Ich will niemandem irgendwas "unnötig schwer" machen. Im Gegenteil: Ich will, dass das ganze _einfach richtig_ zu verwenden ist, aber eben auch _schwer falsch_ zu verwenden (das wäre dann "_nötig_ schwer"  ). 

Ich weiß, wie kreativ Leute sein können, wenn es um die (Aus-)Nutzung von Features und Verhaltensweisen einer API gibt, die "zufällig" auf eine bestimmte Weise funktionieren, auch wenn diese Funktionsweise nicht Teil der Spezifikation ist. Ich weiß auch, dass jede API den "Amöbeneffekt" hat: Kleine Fortsätze, die sich aus dem spezifizierten Bereich heraus erstrecken, und als gegeben angesehen werden, und mit denen man sich Freiheiten für spätere Änderungen verbaut.

Ich werd' eine Version 0.0.0-alpha vielleicht bis Ende des Monats irgendwo hochladen, und kann dann hoffentlich ein bißchen Feedback sammeln.


----------



## cmrudolph (12. Mai 2012)

Ich finde den Ansatz die Factory in das Implementierungspackage zu legen und an diese von einer Factory im Interfacepaket (oder in ein extra Factorypackage, da du ja nur Interfaces in dem Paket haben wolltest) zu delegieren am besten. Ich würde dabei auf Deprecation verzichten.

Ich würde nicht auf Delegation verzichten, da der Sinn ja gerade ist, dass die Implementierung austauschbar bleibt. Wenn man die Factory nur im Implementierungspaket liegen hat, dann kann man die Implementierung nicht mehr ohne weiteres austauschen. Das kann man mit der Delegation umgehen.

Die Factory im Implementierungspaket bleibt natürlich weiterhin sichtbar.


----------



## schalentier (12. Mai 2012)

Mir bleibt immer noch der Sinn verborgen, wozu man das alles macht, _wenn es eh nur genau eine Implementierung gibt_.

Ich seh nur zwei Usecases fuer sowas:
a) man moechte auf Teufel komm raus, dass man theoretisch und prinzipiell die Implementierung austauschen kann
oder b) man will eine API fuer andere bereitstellen, so dass man die Impl. aendern kann, ohne das der der Benutzer etwas davon merkt.

Zu a) sag ich: Damit versucht man ein Problem zu loesen, was man gar nicht hat. Also wuerd ich es in diesem Fall einfach sein lassen und loesen, wenn es tatsaechlich da ist.
Bei b) bin ich mir selbst noch nicht wirklich sicher. Meine bisherige (praktische) Erfahrung in der IT im allgemeinen ist aber, dass dieser Versuch eine Illusion ist. 

Letztendlich kann man nur zwei Dinge machen: 
i) Die Aenderung ist so massiv, dass die API geaendert werden muss -> Client muss angepasst werden und es gibt keinen Vorteil, dass die Impl hinter einem Interface versteckt ist.
ii) Man kann die Aenderung ohne API Aenderung durchfuehren. Jetzt hat man tatsaechlich den Vorteil, diese ohne Clientanpassungen durchfuehren zu koennen. Nur, kann man daraus irgendwas schliessen? 

Wird die Anwendung wirklich noch so funktionieren, wie vorher (+die Verbesserungen in der Impl.)? Hat der Client vorher vielleicht einen Bug benutzt, oder drauf aufgebaut, der jetzt entfernt wurde? War die API wirklich genauso gedacht und wurde sie genauso wie gedacht auch benutzt?

Diese Fragen kannst du sicher nur mit Tests beantworten. Aber wenn du eh Tests brauchst, wozu braucht man dann diese ganzen Schutzmechanismen, die Dinge auf Sprachebene verhindern, die ein Test (auch mit Schutz) eh ueberpruefen muss?

Zudem musst du dir in jedem Fall auch irgendwas ueberlegen, wie man sinnvoll mit den Interfaces (und moeglicherweise sogar die Factories) testen kann. D.h. du brauchst fuer jedes deiner 50 Interfaces mindestens nochmal eine Mock/Stub Implementation fuers Testframework.

Sooo, vielleicht versteh ich auch irgendwas grundlegend nicht, aber in jedem Fall verursacht die Vorstellung von 50 Interfaces, 50 Implementierungen, 50-x trivial Factories, 50-x delegierende Factories und nochmal 50 Stubimplementierungen irgendwie Bauchschmerzen... ;-)


----------



## Marco13 (12. Mai 2012)

schalentier hat gesagt.:


> Ich seh nur zwei Usecases fuer sowas:
> a) man moechte auf Teufel komm raus, dass man theoretisch und prinzipiell die Implementierung austauschen kann
> oder b) man will eine API fuer andere bereitstellen, so dass man die Impl. aendern kann, ohne das der der Benutzer etwas davon merkt.



Oder 
c) Beides  




> Zu a) sag ich: Damit versucht man ein Problem zu loesen, was man gar nicht hat. Also wuerd ich es in diesem Fall einfach sein lassen und loesen, wenn es tatsaechlich da ist.



Dann ist es zu spät. Ich gehe davon aus: Wenn so eine Bibliothek erstmal veröffentlicht ist, dann kann man keine Funktionalität mehr rausnehmen. Version 1.0 muss perfekt sein (auch wenn ich aus Prinzip bei 0.0.0 zu zählen anfange ). Natürlich wird sie das nicht sein, aber man kann versuchen, möglicht nahe dran zu kommen. Ich weiß über die Anforderungen an diese Bibliothek viel, viel zu wenig, als dass ich auch nur in die Nähe von "perfekt" kommen könnte, und vieles wird erweitert werden müssen, und im schlimmsten Fall auch einiges über den Haufen geworfen. Aber zumindest das Grundgerüst, der allgemeine Ansatz, oder wenn man so will, der "Stil" sollte so sein, dass die API möglichst schnell konvergiert. Was nicht heißt, dass andere dann nicht sagen: "So ein komplizierter, überflüssiger, unbenutzbarer Dreck :autsch: ". Das werd' ich merken 



> Letztendlich kann man nur zwei Dinge machen:
> i) Die Aenderung ist so massiv, dass die API geaendert werden muss -> Client muss angepasst werden und es gibt keinen Vorteil, dass die Impl hinter einem Interface versteckt ist.
> ii) Man kann die Aenderung ohne API Aenderung durchfuehren. Jetzt hat man tatsaechlich den Vorteil, diese ohne Clientanpassungen durchfuehren zu koennen. Nur, kann man daraus irgendwas schliessen?



Hmja. Ich habe mir noch nie gedacht: "Oh, Mist, dieses Interface hätte eine konkrete Klasse sein sollen!". Das umgekehrte aber schon. Selbst bei vermeintlich "trivialen" Klassen, wo ich zuerst dachte: "Na, da KANN es nur diese eine Implementierung geben!". Das nachträglich zu ändern kann unschön sein, und wenn die API erstmal öffentlich ist, ist es unmöglich. Deswegen tendiere ich eher zu Interfaces, und hinterfrage nicht sooo kritisch ob sie denn unbedingt alle "notwendig" sind. Tatsächlich irritiert es mich schon manchmal selbst, wenn ich weiß: Ich könnte einfach eine Klasse schreiben

```
class Something {
    void doSometing() { ... }
    void setSomething(Whatever whatever) { ... }
}
```
aber schreibe stattdessen "stupide"

```
interface Something {
    void doSometing();
    void setSomething(Whatever whatever);
    void addSomethingListener(SomethingListener s);
    void removeSomethingListener(SomethingListener s);
}
interface SomethingListener {
    void whateverChanged(Something s);
}
class AbstractSomething {
    private List<SomethingListener> listeners ...
    public void addSomethingListener(SomethingListener s) { ... }
    public void removeSomethingListener(SomethingListener s) { ... }
    protected void notifyWhateverChanged(Something s) { ... }
}
class DefaultSomething implements Something { ... }

class Somethings {
    Something createSomething() {
        return new DefaultSomething();
    }
}
```
(nicht umsonst gibt es Dinge wie EMF, die einem dieses "stupide" Standard-Zeux weitgehend abnehmen  ). Die Irritation verfliegt, wenn ich später denke: "Aaah, gut, dass ich das so gemacht habe". :bahnhof:






> Wird die Anwendung wirklich noch so funktionieren, wie vorher (+die Verbesserungen in der Impl.)? Hat der Client vorher vielleicht einen Bug benutzt, oder drauf aufgebaut, der jetzt entfernt wurde? War die API wirklich genauso gedacht und wurde sie genauso wie gedacht auch benutzt?


Das ist der Amöbeneffekt. Und es gibt, wie gesagt, genug Beispiele, wo unspezifiziertes Verhalten ausgenutzt wird, oder unbedacht von irgendwelchen in der API vorhandenen Klassen geerbt und munter protected Methoden verwendet oder überschrieben werden, was ursprünglich nicht gedacht war. Und es gibt mehrere Möglichkeiten, das zu verhindern: Interfaces+staticFactories, und/oder alle anderen Klassen final.... In grober(!) Anlehnung an das "Open-Closed-Principle".





> Diese Fragen kannst du sicher nur mit Tests beantworten. Aber wenn du eh Tests brauchst, wozu braucht man dann diese ganzen Schutzmechanismen, die Dinge auf Sprachebene verhindern, die ein Test (auch mit Schutz) eh ueberpruefen muss?
> ...
> Sooo, vielleicht versteh ich auch irgendwas grundlegend nicht, aber in jedem Fall verursacht die Vorstellung von 50 Interfaces, 50 Implementierungen, 50-x trivial Factories, 50-x delegierende Factories und nochmal 50 Stubimplementierungen irgendwie Bauchschmerzen... ;-)



Wenn du nur das damit sagen wolltest: Ja, das Testen ist aufwändig. Aber das ist Testen IMMER. Ich finde aber, dass gerade UnitTests dann besonders viel Sinn machen, wenn man damit schnell sicherstellen kann, dass verschiedene Implementierungen desselben Interfaces ein der Spezifikation entsprechendes Verhalten aufweisen. 
(Im konkreten Fall sind Unit-Tests aus anderen Gründen schwierig bis unmöglich, aber das ist ein anderes Thema).


----------



## fastjack (13. Mai 2012)

Version 1.0 muß perfekt sein... Du fängst aber bei 0.0.0 an. Also hast Du noch viel Zeit die User-Wünsche/Api-Benutzung zu analysieren und dementsprechend weiterzuentwicklen. Aber es gleich augenscheinlich "perfekt" zu machen, ich weis nicht. Du wirst viel einbauen, was später vielleicht gar nicht verwendet wird. Und dann kannst Du es nicht mal mehr ausbauen, es kann ja schon verwendet worden sein kann...

Ein Interface einzuziehen, von dem es von vornherein nur eine Implementierung geben soll, halte ich für sinnfrei. Bei so einem Teil kann jeder Entwickler sich einfach eine neue Implementierung machen, mit der Deine Api-Methoden dann auch klarkommen müssen. 

Außerdem denke ich bei solchen Apis (mit mehreren Factories und Delegationen und und und... aber nur ein oder zwei Stellen, die ich tatsächlich benutzen darf) automatische an fette Titten mit ultra kleinen Nippeln dran zum rumspielen.


----------



## Marco13 (13. Mai 2012)

Sicher, die erste Version wird eine Alpha-Version, und es wird sich noch einiges ändern. Vielleicht auch viel. Vielleicht gibt es auch kein Feedback außer: "Dreck, braucht kein Mensch, kann man nicht benutzen" etc. (dann ändert sich entweder SEHR viel - oder gar nichts mehr  ). Auf jeden Fall werde ich dem Bloch'schen Mantra folgen: "When in doubt - leave it out!". Ich werde versuchen, nichts einzubauen, was später wieder rausfallen sollte. (Ob mir das gelingt, wird sich zeigen). 

Für einige Interfaces gibt es schon von Anfang an mehrere Implementierungen. Einige wenige sind so trivial, dass ich im Moment davon ausgehe, dass es nur eine Implementierung geben KANN, aber ... die werden ohnehin nur "intern" verwendet. Bei den anderen werden die Factories (bzw. in diesem Fall dann eher Builder) auch verwendet, weil das Erstellen einer spezifischen Implementierung mit Konstruktoren und Settern recht aufwändig wäre, oder bestimmten Constraints unterliegt. Aber wenn jemand anderes das Interface der Spezifikation entsprechend implementieren will, kann er das ja gerne tun, dafür sind Interfaces ja da... :bahnhof:

Diese Assoziation... ich spare mir Spekulationen darüber, was die über DICH aussagt  Ich kann mir ungefähr denken, was du damit meinen könntest. Ich sehe daran aber nicht notwendigerweise etwas schlechtes: Information hiding, bzw. eine kleine, öffentliche Schnittstelle (das klingt komisch, mit dieser Assoziation im Hinterkopf  ) wo eine mächtige Funktionalität dahinter steckt, ist doch nicht verkehrt. Ich ärgere mich zwar auch manchmal, a la: "Mist, der Idiot hat die Klasse final / diese Methode private gemacht, die müßte ich überschreiben, um dies-und-das zu erreichen", aber im Zweifelsfall muss man davon ausgehen, dass das bewußte Designentscheidungen waren, und es eben besser ist, wenn man bestimmte Sachen NICHT machen kann. Und speziell am Anfang sollte IMHO alles eher "vernagelt" sein: Eine finale Klasse nachträglich nicht-final zu machen geht immer. Umgekehrt würde man Clients kaputt machen.


----------



## Spacerat (14. Mai 2012)

Ich weis nicht, was genau du vorhast, hat aber hoffentlich nichts mit unserer PN-Konversation über Datentyp-Erkennung zu tun. Ich erkenne nämlich, dass meine Hauptmethode dort ("DataType.getFile()") im Prinzip auch nichts weiter als eine Factory-Methode für das Interface "DataTypeObjects" war. Diese Factory liegt bei mir (auch wenn "DataTypesObject" lange nicht mehr existiert) im Package-Hauptverzeichnis. Das ist insofern kein Problem, weil die Interface-Implementationen über das ServiceProvider-Interface (SPI) geladen werden, von welchem sie auch geprüft bzw. gefiltert werden.
Etwa so:

```
public class SomethingFactory
{
  Something createSomething(Object ... someParameters)
  {
    Iterator<SomethingProvider> it = ServiceLoader.load(SomethingProvider.class).iterator();
      while(it.hasNext()) {
        SomethingProvider sp = it.next();
        try {
          if(sp.acceptsParameters(someParameters) {
            return sp.getSomething();
          }
        } catch(Throwable e) {
          // fuer den Fall, dass einer mogeln will...
          e.printStackTrace();
        }
      }
    }
    return null;
  }
}

abstract class SomethingProvider
{
  public abstract boolean acceptsParameters(Object ... parameters);
  public abstract Something getSomething();
}
```
Wie versteckt nun die einzelnen Somethings sind, bestimmen nun konkrete Implementationen von SomethingProvider und weder du, deine API und am allerwenigsten die Benutzer deiner API haben noch Einfluss darauf.


----------



## Marco13 (14. Mai 2012)

Nein, damit hat es nichts zu tun. Die Möglichkeit, dort über einen ServiceLoader andere Implementierungen einzuklinken gibt es schon, aber auf viel höherer Ebene: Diese eingeklinkten Implementierungen _verwenden_ dann teilweise gerade die Klassen, um die es eigentlich geht. Ich hatte auch schon überlegt, ob man die ServiceLoader-Infrastruktur dort auch auf der niedrigeren Schicht verwenden könnte, und behalte das auch immer im Hinterkopf, aber das erschien mir in diesem Fall bisher nicht sinnvoll.


----------

