wake-up-neo.com

Sollen Schnittstellen in einem separaten Paket untergebracht werden?

Ich bin neu in einem Team, das an einem ziemlich großen Projekt mit vielen Komponenten und Abhängigkeiten arbeitet. Für jede Komponente gibt es ein interfaces-Paket, in dem die offengelegten Schnittstellen für diese Komponente platziert werden. Ist das eine gute Praxis?

Meine übliche Praxis war immer, dass Schnittstellen und Implementierungen im selben Paket enthalten sind.

67
kctang

Das Platzieren der Schnittstelle und der Implementierung ist an der Tagesordnung und scheint kein Problem zu sein.

Nehmen Sie zum Beispiel die Java-API - die meisten Klassen haben beide Schnittstellen und ihre Implementierungen im selben Paket. 

Nehmen Sie zum Beispiel das Paket Java.util :

Es enthält die Schnittstellen wie Set, Map, List und die Implementierungen wie HashSet, HashMap und ArrayList.

Darüber hinaus sind die Javadocs so konzipiert, dass sie unter diesen Bedingungen gut funktionieren, da sie die Dokumentation beim Anzeigen des Paketinhalts in die Ansichten Interfaces und Classes unterteilt.

Pakete nur für Schnittstellen zu haben, kann tatsächlich etwas übertrieben sein, es sei denn, es gibt eine enorme Anzahl von Schnittstellen. Das Trennen der Schnittstellen in eigene Pakete, nur um dies zu tun, klingt nach schlechter Praxis.

Wenn der Name einer Schnittstelle von einer Implementierung unterschieden werden muss, kann es eine Namenskonvention geben, um die Identifizierung der Schnittstellen zu erleichtern:

  • Dem Schnittstellennamen eine I voranstellen. Dieser Ansatz wird bei den Schnittstellen im .NET-Framework verwendet. Es ist ziemlich einfach zu erkennen, dass IList eine Schnittstelle für eine Liste ist.

  • Verwenden Sie das Suffix -able. Dieser Ansatz wird häufig in der Java-API verwendet, z. B. Comparable, Iterable und Serializable, um nur einige zu nennen.

85
coobird

Für jede Sprache ist es in Ordnung, sie in ein und demselben Paket zusammenzustellen. Das Wichtigste ist, was der Außenwelt ausgesetzt ist und wie es von außen aussieht. Niemand wird wissen oder sich darum kümmern, ob die Implementierung in demselben Paket enthalten ist oder nicht.

Betrachten wir diese bestimmte Instanz.

Wenn Sie alle öffentlichen Dinge in einem Paket und private Dinge in einem anderen Paket haben, das nicht öffentlich verfügbar ist, wird dem Client der Bibliothek ein Paket angezeigt. Wenn Sie die privaten Dinge mit den öffentlich exponierten Elementen in das Paket verschieben, sie jedoch nicht innerhalb des Pakets verfügbar machen, sieht der Client genau dasselbe.

Daher hat dies den Geruch einer Regel ohne triftigen Grund: Sie trifft eine Entscheidung, die darauf beruht, dass etwas öffentlich sichtbar ist, ohne dass diese Entscheidung Auswirkungen auf das Öffentlich Sichtbare hat.

Wenn es in einem bestimmten Fall jedoch sinnvoll erscheint, die Schnittstelle und die Implementierung in separate Pakete aufzuteilen, machen Sie sofort weiter. Gründe dafür sind, dass das Paket sehr umfangreich ist, oder Sie haben eine alternative Implementierung, in die Sie anstelle der Standard-Implementierung einbinden möchten.

17
Curt J. Sampson

In vielen Frameworks, wie beispielsweise OSGi, muss man das fast tun. Ich denke, dass dies eine lockerere Kopplung fördert, und zwar auf der Verpackungsebene und nicht auf der Tiegelebene.

9
javamonkey79

Ein Argument für das Einfügen von Schnittstellen in verschiedene Pakete ist, dass es einfacher ist, "Api-Gläser" zu erstellen, die an die Verbraucher Ihres Produkts oder Ihrer Dienstleistung verteilt werden können. Es ist durchaus möglich, dies mit Schnittstellen und Implementierungen zusammen zu tun, aber Skripten, die sich in unterschiedlichen Paketen befinden, sind einfacher.

7
Cameron Pope

Das Buch Praktisches Software-Engineering: Ein Case-Study-Ansatz befürwortet das Einfügen von Schnittstellen in separate Projekte/Pakete. 

Die PCMEF + -Architektur, über die das Buch spricht, hat folgende Prinzipien:

  1. Abwärtsabhängigkeitsprinzip (DDP)
  2. Aufwärtsbenachrichtigungsprinzip (UNP)
  3. Nachbarschaftskommunikationsprinzip (NCP)
  4. Explizites Assoziationsprinzip (EAP)
  5. Prinzip der Zyklusbeseitigung (CEP)
  6. Klassenbenennungsprinzip (CNP)
  7. Bekanntschaftspaket-Prinzip (APP)

Die Beschreibung der Prinzipien 3 und 7 erklärt, warum dies eine gute Idee ist:

Das Prinzip der Nachbarschaftskommunikation verlangt, dass ein Paket nur direkt mit dem Nachbarpaket kommunizieren. Dieses Prinzip gewährleistet dass das System nicht in ein inkompressibles Netzwerk von .__ zerfällt. miteinander kommunizierende Objekte. Um dieses Prinzip durchzusetzen, wird die Nachrichtenübergabe zwischen nicht benachbarten Objekten erfolgt die Delegierung (Abschnitt 9.1.5.1). Im Bei komplexeren Szenarien kann das Bekanntschaftspaket (Abschnitt 9.1.8.2) .__ sein. Wird verwendet, um Schnittstellen zu gruppieren, um die Zusammenarbeit zu unterstützen. entfernte Pakete.

Das Bekannte-Paket-Prinzip ist die Folge des Nachbarn Kommunikationsprinzip. Das Bekanntschaftspaket besteht aus Schnittstellen, die ein Objekt anstelle von konkreten Objekten in .__ durchläuft. Argumente für Methodenaufrufe. Die Schnittstellen können in jedem beliebigen .__ implementiert werden. PCMEF-Paket. Dies ermöglicht effektiv die Kommunikation zwischen nicht benachbarte Pakete, während die Verwaltung der Abhängigkeiten auf ein .__ zentralisiert wird. Einzelbekanntschaftspaket. Das Paket für Bekanntschaften war wird in Abschnitt 9.1.8.2 erläutert und in PCMEF .__ noch einmal besprochen. Kontext.

Siehe diesen Link: http://comp.mq.edu.au/books/pse/about_book/Ch9.pdf

5
Kenci

Ja, dies ist eine sehr gute Vorgehensweise, da Sie die Schnittstelle veröffentlichen können, ohne Ihre spezifische Implementierung zu veröffentlichen. Wenn Sie jedoch keine externen Schnittstellen veröffentlichen müssen, ist es kein Problem, die Schnittstellendefinitionen in das gleiche Paket zu packen wie die Implementierung.

4
Paul Sonier

Wir machen das, wo ich arbeite (dh: Schnittstelle in ein Paket und Implementierung in ein anderes einfügen). Der Hauptvorteil, den wir daraus ziehen, ist, dass wir problemlos zwischen Implementierungen wechseln können.

3
hhafez

Ich mache dies für ein Projekt und es funktioniert aus folgenden Gründen gut für mich:

  • Separat gepackte Schnittstellen und Implementierungen gelten für einen anderen Anwendungsfall als für die verschiedenen Map- oder Set-Typen. Es gibt keinen Grund, ein Paket nur für Bäume (Java.util.tree.Map, Java.util.tree.Set) zu haben. Es handelt sich lediglich um eine Standarddatenstruktur, also fügen Sie sie mit den anderen Datenstrukturen zusammen. Wenn Sie jedoch mit einem Puzzlespiel zu tun haben, das über eine wirklich einfache Debug-Oberfläche und eine hübsche Produktionsoberfläche verfügt, können Sie als Teil des Frontends einen com.your.app.skin.debug und eine com.your.app verwenden .skin.pretty. Ich würde diese nicht in ein und dasselbe Paket packen, da sie verschiedene Dinge tun und ich weiß, dass ich auf eine SmurfNamingConvention (DebugAttackSurface, DebugDefenceSurface, PrettyAttackSurface usw.) zurückgreifen würde, um einen informellen Namespace für die beiden zu erstellen, wenn sie im selben Paket wären.

  • Meine Problemumgehung für das Problem, verwandte Schnittstellen und Implementierungen in separaten Paketen zu finden, besteht darin, eine Namenskonfiguration für meine Pakete zu übernehmen. Z.B. Ich kann alle meine Schnittstellen in com.your.app.skin.framework haben und weiß, dass andere Pakete auf derselben Ebene der Paketstruktur Implementierungen sind. Der Nachteil ist, dass dies eine unkonventionelle Konvention ist. Ehrlich gesagt werde ich sehen, wie gut diese Convention in 6 Monaten ist :)

  • Ich verwende diese Technik nicht religiös. Es gibt Schnittstellen, die nur in einer bestimmten Implementierung sinnvoll sind. Ich stecke sie nicht in das Rahmenpaket. Es gibt einige Pakete, bei denen es nicht so aussieht, als würde ich 40 verschiedene Implementierungsklassen erstellen, also kümmere ich mich nicht darum.

  • Meine Anwendung verwendet guice und hat sehr viele Schnittstellen.

Fragen des Programmdesigns beinhalten im Allgemeinen Vor- und Nachteile und es gibt keine einheitlichen Antworten. Warum würden Sie diese Technik jemals in einem kleinen 200-Zeilen-Programm verwenden? Es ist für mich angesichts meiner anderen architektonischen Entscheidungen für mein spezifisches Problem sinnvoll. :)

2
Moray Jaundice

Ich habe nicht viel Java-Erfahrung, aber ich mache dies gerne als eine bewährte Methode in C # /. NET, da dies eine zukünftige Erweiterung ermöglicht, bei der die Assemblys mit den konkreten Klassen, die die Schnittstellen implementieren, möglicherweise nicht bis in die Client, weil sie in einem Webservice-Szenario von einer Zwischenhändlerfabrik oder über die gesamte Leitung vertrieben werden.

2
Jesse C. Slicer

Normalerweise setze ich Interfaces bei der Implementierung ein, aber ich kann irgendwie verstehen, warum Sie sie getrennt halten möchten. Angenommen, jemand wollte Klassen basierend auf Ihren Schnittstellen neu implementieren, er würde mit Ihrer Implementierung eine jar/lib/etc und nicht nur die Schnittstellen benötigen. Mit ihnen können Sie einfach sagen "Hier ist meine Implementierung dieser Schnittstelle" und fertig damit. Wie ich schon sagte, nicht was ich tue, aber ich kann irgendwie verstehen, warum manche es vielleicht wollen.

1
Matt_JD

Ich bevorzuge sie im gleichen Paket. Es macht keinen Sinn, ein Paket speziell für Schnittstellen zu erstellen. Dies ist insbesondere für Teams überflüssig, die ihre Schnittstellenpräfixe und -suffixe lieben (z. B. "I" und "Impl" für Java).

Wenn ein Satz von Schnittstellen als öffentliche API veröffentlicht werden muss, ist es sinnvoller, sie in einem völlig separaten Projekt zu belassen und stattdessen Projektabhängigkeiten zu erstellen. Aber alles hängt von der Bevorzugung und der Bequemlichkeit der Situation ab, die ich vermute.

0
aberrant80

Ich denke, es ist wichtig zu wissen, dass es für das OSGi-Framework schöner ist, dass sie in verschiedenen Paketen enthalten sind, sodass Sie problemlos ein ganzes Paket exportieren können, während Sie die Implementierungspakete ausblenden.

0
jkabugu

Legen Sie sie in Pakete, die Ihre Projekte widerspiegeln. Es ist in Ordnung und üblich, Schnittstellen und Implementierungen zusammenzustellen, wenn sie Teil desselben Projekts sind. Wenn Sie jedoch eine API schreiben, würde wahrscheinlich jemand anderes einen Paketnamen auswählen, der für Ihr Projekt relevant ist.

Wenn es sich um dasselbe Projekt handelt, sehe ich im Allgemeinen keinen Vorteil darin, die Schnittstellen in einem separaten Paket von ihren Impls zu halten. Wenn es unübersichtlich wird, können ungeachtet der Schnittstellenanordnung andere Probleme bei der Benennung von Paketen auftreten.

0
Rog