# Strukturierung eine API



## rewireable (16. Feb 2012)

Hallo,

ich bin gerade dabei eine exisiterende API weiterzuentwickeln bzw zu überarbeiten. Die Eigentliche Schnittstelle besteht aus ca 10 Interfaces. Die implementierung ist auf ca 100 Klassen in verschiedensten Unterpackages aufgeteilt.

jetzt der Punkt: ich möchte ja die implementierung vom nutzer der API verstecken, er soll immer nur die Interfaces verwenden und zum erstellen neuer Objektinstanzen biete ich ihm noch eine Factory Klasse an.

Heisst das jetzt, dass ich alle 100 Klasen die zur implementierung gehören aus der package-hierachie rausreissen muss und in mein oberstes package legen damit ich sie package-private machen kann?? Das würde das ganz projekt deutlich unübersichtlicher machen, also scheu ich mich noch davor.

Gibts hierfür ein Standardvorgehen?

besten Dank für euer Hilfe!

Stefan


----------



## SlaterB (16. Feb 2012)

dass man nicht mehrere packages, insbesondere hierarchisch zusammengehörgie, zusammen abschotten kann, ist in der Tat schade,
meiner Meinung nach solltest du auf sinnvolle package-Unterteilung nicht verzichten, auch wenn das public-Klassen bedeutet,
ist in der Java-API und anderen Libraries ähnlich

die Nutzung der Interface setzt dann eben aktive Zusammenarbeit der Nutzer voraus,
nicht alles was public ist muss ja auch genutzt werden,

besonders wenn die Factory nur Interface zurückgibt, müsste der Nutzer schon 'vorsätzlich' casten, um überhaupt Probleme zu bekommen


----------



## mvitz (16. Feb 2012)

Mit OSGI könntest du den Zugriff für Clients deiner API einschränken.

Für größere JAVA APIs geht das nur bedingt (wie gesagt mit Package-Privat) aber meistens wird dies schnell unmöglich ohne alle Klassen in ein Package zu packen.

Ansonsten bleibt dir nur, das ganze zu kommunzieren, damit die Clients (wenn alle Intern sind geht das noch relativ gut) nur über die Factories die Implementierung nutzen.


----------



## bygones (16. Feb 2012)

die frage ist auch, wie du deine API verteilst bzw in welchen Systeme sie genutzt werden.

Du hast ein API projekt und ein impl Projekt. Bei der Nutzung deiner API bindet man nur das API projekt ein, im laufenden System gibt es dann auch das impl projekt.


----------



## Marco13 (16. Feb 2012)

Hmja, stehe im Moment vor einem ähnlichen Problem. Man hat ja "oft" packages wie
api <- Interfaces
api.impl <- Implementierungen der Interfaces

Und die scheinbar banale Frage: Wie kommt man an die Implementierungen, ohne sie public zu machen? Factories im impl-Package? Dann hat man doch wieder public-Klassen aus dem impl, dei verwendet werden können - und _andere_ implementierungen (also aus einem api.otherimpl package) kann man nicht verwenden, womit ein Teil der Intention des Factory-Patterns ad absurdum geführt wird. Da bräuchte man sowas wie "friend" in C++... In "Practical API Design: Confessions of a Java Framework Architect" (als PDF: http://swebooks.googlecode.com/svn-...ns.of.a.Java.Framework.Architect.Jul.2008.pdf ) beschreibt Jaroslav Tulach (der "Netbeans-Chef") in einem Kapitel "Allow Access Only from Friend Code" (S. 75 ff) ein paar Ansätze dazu, aber die sind recht aufwändig und nicht wirklich "schön" - da muss man wohl Prioritäten setzen...


----------



## bygones (16. Feb 2012)

Java kennt das Friend system nicht und man sollte es auch nicht mit biegen und brechen versuchen.

Die Regel ist einfach - Api ist als solche zu deklarieren und auch zu nutzen. Wenn sich jemand direkt an die Impl haengt ist es sein Fehler und er muss mit den Konsequenzen leben. 

Man kann natuerlich auch in jedem package eine Facade anbieten die wiederum den Zugriff auf die konkreten Implementierungen versteckt... naja.

Ich wuerde immer versuchen 2 Projekte zu machen um die Trennung noch visueller zu haben. Aber es haengt natuerlich auch davon ab, wie man seine API ausliefert


----------



## Marco13 (16. Feb 2012)

bygones hat gesagt.:


> Wenn sich jemand direkt an die Impl haengt ist es sein Fehler und er muss mit den Konsequenzen leben.



Ich weiß, was du meintest, und stimme grundsätzlich zu. Die analoge Argumentation habe ich aber auch schon gehört: "Warum soll ich meine fields nicht public machen? Wer die verwendet ist ja selbst schuld!" (im Ernst :autsch: ). Es wäre gut, wenn man nicht Dependency Injection und ServiceLoaders oder einen OSGi-Overkill verwenden müßte, "nur" um für eine kleine Lib/API eine ""Schwäche"" der Sprache auszugleichen, aber man kann eben nicht alles haben.


----------



## mvitz (16. Feb 2012)

Hier wird in einem Projekt die Konvention genutzt, dass es 4 Package-Name mit "Semantik" gibt.

1. api
2. impl
3. factories
4. common

Dazu gibt es Zugriffsregeln, wer auf wen zugreifen darf (diese gehen dann noch weiter, z.B. darf man nicht auf seine Eltern zugreifen, usw.).

Damit diese Regeln eingehalten werden, gibt es eine eigens geschriebene Bibliothek, die dafür sorgt, dass diese Konventionen/Regeln eingehalten werden.

Somit kann in diesem Projekt gewährleistet werden, dass man von außerhalb nicht auf impl zugreifen darf, sondern eine Instanz eines Interfaces aus api nur per Factory aus factories bekommt.


----------



## bygones (16. Feb 2012)

Marco13 hat gesagt.:


> Ich weiß, was du meintest, und stimme grundsätzlich zu. Die analoge Argumentation habe ich aber auch schon gehört: "Warum soll ich meine fields nicht public machen? Wer die verwendet ist ja selbst schuld!" (im Ernst :autsch: ).


der unterschied ist aber, dass bei public fields du derjenige bist der die Probleme bei Aenderungen hat und Code inkompatibilitaeten deiner API von dir aus gehen bzw du zu verantworten hast.
Bei Nutzung von Impl. statt API ist das Problem beim Verwender, du kannst deine Impl aendern wie du willst.

Somit ist es schon noch ein entscheidender Unterschied.



mvitz hat gesagt.:


> Hier wird in einem Projekt die Konvention genutzt, dass es 4 Package-Name mit "Semantik" gibt.
> 
> 1. api
> 2. impl
> ...


ja - je nachdem wie paranoisch man ist. Wenn man seinen eigenem Team nicht vertraut ist dass natuerlich wichtig


----------



## Spacerat (16. Feb 2012)

Mir fällt dazu nur eines ein...
	
	
	
	





```
public interface MyInterface
{
  void doSomething();
}

...

public final class MyInterfaceFactory
{
  private MyInterfaceFactory()
  {
    // no one should be able to instantiate this
  }

  public static MyInterface createInstance(Parameter ... parameter)
  {
    return new MyInterfaceImpl(Parameter ... parameter);
  }

  private static final class MyInterfaceImpl
  implements MyInterface
  {
    private MyInterfaceImpl(Parameter ... parameter)
    {
      // setup MyInterfaceImpl
    }

    public void doSomething()
    {
      // do something
    }
  }
}
```
Je nach Parametern kann man MyInterface auch in mehreren privaten Implementationen unterbringen und zurückgeben. Aber so bleiben die eigentlichen Implementationen auf jeden Fall geheim.


----------



## maki (16. Feb 2012)

^ Ist auf Dauer nicht praktikabel, auuser man erachtet eine Factory mit mehreren tausend Zeilen als "praktisch"


----------



## bygones (16. Feb 2012)

@Spacerat
das klappt natuerlich wenn du deine Implementierung versteckst - das hat keiner behauptet, dass es nciht geht.

Nur will man auch nicht, dass man jede Implementierung zusammen mit der Factory klasse hat bzw bei einem groesserem System alles in einer Struktur reinpresst.

Bei kleinen Systemen ist das ohne Probleme moeglich, man bietet einfach in jedem Paket die entsprechenden Accessor an


----------



## Spacerat (16. Feb 2012)

Tja... irgendwas ist immer... ;(


----------



## bygones (16. Feb 2012)

Spacerat hat gesagt.:


> Tja... irgendwas ist immer... ;(



sonst waere die Diskussion hier nicht schon ein paar Beitraege ;-)


----------



## Empire@Work (16. Feb 2012)

Einfach an jede der internen methoden ein 
/**Do not use**/ ranpacken, wer die dann kann keiner behaupten dass das ganze ausvershen benutzt wurde.


----------



## rewireable (17. Feb 2012)

Hallo

Danke allen für eure Antworten. 

Solche Hilfslösungen wie eine "intern" o.ä. im package-path klingen mir im moment noch am einfachste machbar. Wobei ich trotzdem ein ungutes gefühl dabei hab, in einen package-namen semantik reinzupacken und diese der nutzer dann auch verstehen und anwenden muss. Ein "Don't Use" im JavaDoc erreicht wohl noch weniger Leute, aber vielleicht ist das als zusätzlich Lösung sinvoll.

Von der Grundidee hat mit die 2-Projekte Lösung eigentlich am besten gefallen. Ein Projekt in dem nur die Interfaces stehen und gegen das dann der Anwender entwickelt. Ein zweiter Projekt mit den implementierungen die dann erst zur laufzeit benötigt werden. Da wir Maven verwenden, sollte das ja auch so konfigurierbar sein.
Allerdings braucht man dann doch an irgendeiner Stelle die verbindung zur implementierung, ich muss mir ja irgendwo die Factory für die Objekte herholen, und dann brauch ich das ganze ja doch wieder zur kompilierzeit, und dadurch ist auch wieder alle zugreifbar. Oder holt man sich dann die Factory über Reflection?


Wirklich überzeugt bin ich noch von keiner Lösung. hat jemand ein Beispiel wie anderen Bibliotheken das Problem umgangen haben?

gruß Stefan


----------



## Spacerat (17. Feb 2012)

Aber natürlich. Beispiele gibt es genug. z.B. lässt sich mein Beispiel auch noch ein bissl anders lösen, indem man die Klassen der Factory nebst deren Konstruktoren package-scoped macht während man die Factory selber in das jeweilige Package legt. Die Factory sucht ihre Klassen nur in diesem Package und das package selber schützt man noch mal vor fremden Erweiterungen (z.B. wie [c]java.awt[/c], ich weis aber leider nicht wie das geht). Oder man unternimmt gar nichts, siehe Erweiterungen der Klasse Thread. Wer dessen [c]run()[/c]-Methode explizit aufruft, ist halt selber schuld.


----------



## bygones (17. Feb 2012)

wie schon gesagt, es gibt in Java keine Bulletproof loesung dafuer. Der Vorteil der 2 Jars ist es, dass man eben genau das api jar nehmen kann fuer die Entwicklung und dann nur beim deployment fuer das System das 2. Jars dabei haben muss.

oder eben wie auch schon gesagt, man bietet in der Implementierung fuer jede Struktur(-verband) eine Factory an, die nach aussen auch nur die API strukturen weitergibt. Die Klassen die sie dann instanziiert liegen im gleichen package und sind package protected. Es sagt ja niemand, dass man nur eine Factory haben darf. 

Aber ich sags nochmal, in einem gut strukturierten Programm ist es klar, was man nehmen sollte oder nicht. Wer sich dann doch von der Impl. direkt abhaengig macht ist selber schuld.


----------



## ...ButAlive (17. Feb 2012)

Die Lösung mit den 2 Jars hat meiner Meinung nach nur dann einen Vorteil wenn es unterschiedliche Implementierungen für die API gibt. Dann wäre vielleicht auch der ServiceLoader interessant.

Wenn jemand gegen die API implementiert, muss dieser auch beide jars im Buildpath der IDE haben und dann sieht man nicht wirklich wo die Klassen herkommen.

Mit Namenskonventionen kommt man schon relativ weit.

Du willst ja sicher stellen, dass nur manche Dinge von außen benutzt werden, und die Möglichkeit besteht, dass man noch etwas ändern kann. Ganz verhindern kannst du es nie, was du machen kannst, ist soviel wie möglich auf package-private setzen, paketübergreifendende Methodenaufrufe durch interne Interfaces kapseln und paketübergreifende Instantiierungen durch Factories  ersetzten. Damit hast du die möglichen Punkte minimiert viel mehr geht aber leider nicht. 

Ich würde mich total freuen wenn package-private von "Sichtbar im gleichen Paket" auf "Sichtbar im gleichen Paket und seinen Unterpaketen" geändert werden würde. Das wird wahrscheinlich nie kommen. Stattdessen wird  versucht so etwas wie OSGI in den Java-Standard aufzunehmen, meiner Meinung nach in 80% der Fälle total oversized und für die restlichen 20% steht OSGI zu Verfügung.


----------



## bygones (17. Feb 2012)

...ButAlive hat gesagt.:


> Wenn jemand gegen die API implementiert, muss dieser auch beide jars im Buildpath der IDE haben und dann sieht man nicht wirklich wo die Klassen herkommen.


nein muss er nicht - wenn man gegen die API programmiert, braucht man nur diese. Man braucht das zweite Jar nur zur Laufzeit seines Systems, nicht zur compilezeit.


----------



## ...ButAlive (17. Feb 2012)

Ok um dagegen zu programmieren bäuchte man sie nicht. Spätestens wenn man Test schreiben will braucht man sie , oder man mockt sie komplett, was bei Factories evt. schwierig wird. 

Ich glaube nur, dass man Leuten, die gegen *Impl programmieren, auch nicht beibringen kann *-api.jar kannst du in den Buildpath aufnehmen *-impl.jar nicht. Durch die 2 jars hat man dann nichts gewonnen.


----------



## bygones (17. Feb 2012)

...ButAlive hat gesagt.:


> Ich glaube nur, dass man Leuten, die gegen *Impl programmieren, auch nicht beibringen kann *-api.jar kannst du in den Buildpath aufnehmen *-impl.jar nicht. Durch die 2 jars hat man dann nichts gewonnen.


nur weil es Idioten gibt, heisst das nicht dass man auf das richtige verzichten soll. Ich lasse es jdf nicht gelten design fragen aufgrund von manchen Ignoranzen zu entscheiden.


----------



## ...ButAlive (17. Feb 2012)

bygones hat gesagt.:


> nur weil es Idioten gibt, heisst das nicht dass man auf das richtige verzichten soll. Ich lasse es jdf nicht gelten design fragen aufgrund von manchen Ignoranzen zu entscheiden.



Da hast du mich falsch verstanden. Ich glaube nicht, dass das aufteilen auf 2 jars irgendwelche Vorteile hat, außer man will die Implementierung variabel halten. Mir fällt kein Projekt ein, dass das macht, wenn es nur eine Implementierung gibt.

Wenn man sich die Mühe macht, Schnittstellen und Factories zur Verfügung stellt, versucht soviel wie möglich zu verstecken und den Rest mit *Impl oder package internal zu markieren reicht es. Wenn es dann einer immer noch für nötig hält, gegen interne Sachen zu programmieren, ist ein Versionswechsel sein Problem. Zwei jars machen dann keinen Unterschied.


----------



## Spacerat (18. Feb 2012)

bygones hat gesagt.:


> nur weil es Idioten gibt, heisst das nicht dass man auf das richtige verzichten soll. Ich lasse es jdf nicht gelten design fragen aufgrund von manchen Ignoranzen zu entscheiden.


Denke auch, dass du ButAlive falsch verstanden hast. Designfragen wären nämlich weitläufig schneller beantwortet, wenn jeder auf jene SKs pfeift, die trotz Warnung, z.B. [c]run()[/c] bei Threads explizit aufrufen. Ok... Was heisst schneller beantwortet... so stellen sie sich eigentlich gar nicht erst.


----------



## bygones (18. Feb 2012)

...ButAlive hat gesagt.:


> Da hast du mich falsch verstanden. Ich glaube nicht, dass das aufteilen auf 2 jars irgendwelche Vorteile hat, außer man will die Implementierung variabel halten. Mir fällt kein Projekt ein, dass das macht, wenn es nur eine Implementierung gibt.
> 
> Wenn man sich die Mühe macht, Schnittstellen und Factories zur Verfügung stellt, versucht soviel wie möglich zu verstecken und den Rest mit *Impl oder package internal zu markieren reicht es. Wenn es dann einer immer noch für nötig hält, gegen interne Sachen zu programmieren, ist ein Versionswechsel sein Problem. Zwei jars machen dann keinen Unterschied.


ok ja hatte dich falsch verstanden... 
abgesehen davon, intern wuerde ich immer mit 2 Projekten entwickeln, daber das steht hier ja nicht zur debatte, aber nach aussen hin stimmt es dass dann auch ein jar mit entsprechender packierung (?) reicht


----------

