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 eineinterfaces
Paket, in dem die verfügbaren Schnittstellen für diese Komponente platziert sind. Ist das eine gute Praxis?
Meine übliche Praxis war immer, dass Schnittstellen und Implementierungen im selben Paket enthalten sind.
Antworten:
Das Platzieren sowohl der Schnittstelle als auch der Implementierung ist an der Tagesordnung und scheint kein Problem zu sein.
Nehmen wir zum Beispiel die Java-API - die meisten Klassen haben beide Schnittstellen und ihre Implementierungen im selben Paket.
Nehmen Sie zum Beispiel das
java.util
Paket:Es enthält die Schnittstellen wie
Set
,Map
,List
, während auch die Implementierungen, die wieHashSet
,HashMap
undArrayList
.Darüber hinaus sind die Javadocs so konzipiert, dass sie unter diesen Bedingungen gut funktionieren, da die Dokumentation bei der Anzeige des Paketinhalts in die Ansichten Schnittstellen und Klassen unterteilt wird .
Pakete nur für Schnittstellen zu haben, kann tatsächlich etwas übertrieben sein, es sei denn, es gibt eine enorme Anzahl von Schnittstellen. Aber die Schnittstellen in ihre eigenen Pakete zu trennen, nur um dies zu tun, klingt nach schlechter Praxis.
Wenn es notwendig ist, den Namen einer Schnittstelle von einer Implementierung zu unterscheiden, kann eine Namenskonvention verwendet werden, um die Identifizierung von Schnittstellen zu vereinfachen:
Stellen Sie dem Schnittstellennamen ein vor
I
. Dieser Ansatz wird mit den Schnittstellen im .NET Framework verfolgt. Es wäre ziemlich einfach zu sagen, dass diesIList
eine Schnittstelle für eine Liste ist.Verwenden Sie das
able
Suffix - . Dieser Ansatz wird häufig in der Java-API verwendet, z. B.Comparable
undIterable
,Serializable
um nur einige zu nennen.quelle
Für jede Sprache ist es in Ordnung, sie in demselben Paket zusammenzustellen. Wichtig ist, was der Außenwelt ausgesetzt ist und wie sie von außen aussieht. Niemand wird wissen oder sich darum kümmern, ob sich die Implementierung in demselben Paket befindet oder nicht.
Schauen wir uns diese spezielle Instanz an.
Wenn Sie alle öffentlichen Dinge in einem Paket und private Dinge in einem anderen Paket haben, das nicht öffentlich verfügbar ist, sieht der Client der Bibliothek ein Paket. Wenn Sie die privaten Dinge mit den öffentlich zugänglichen Dingen in das Paket verschieben, sie jedoch nicht aus dem Paket heraus verfügbar machen, sieht der Client genau dasselbe.
Dies riecht also nach einer Regel ohne guten Grund: Es trifft eine Entscheidung, die darauf basiert, dass etwas öffentlich sichtbar ist, ohne dass diese Entscheidung Auswirkungen auf das hat, was öffentlich sichtbar ist.
Das heißt, wenn es in einem bestimmten Fall eine gute Idee ist, die Schnittstelle und die Implementierung in separate Pakete aufzuteilen, fahren Sie fort und tun Sie dies. Gründe dafür sind, dass das Paket riesig ist oder Sie eine alternative Implementierung haben, die Sie möglicherweise anstelle der Standardimplementierung verknüpfen möchten.
quelle
friend
(C ++) oderinternal
(C #).In vielen Frameworks wie OSGi müssen Sie fast. Ich denke, dies fördert eine lockerere Kopplung auf der Verpackung anstelle der Glasebene.
quelle
Ein Argument für das Einfügen von Schnittstellen in verschiedene Pakete ist, dass es einfacher ist, API-Jars zu erstellen, die an Verbraucher Ihres Produkts oder Ihrer Dienstleistung verteilt werden können. Es ist durchaus möglich, dies mit Schnittstellen und Implementierungen zusammen zu tun, aber einfacher zu skripten, wenn sie sich in verschiedenen Paketen befinden.
quelle
Das Buch Practical Software Engineering: Ein Fallstudienansatz befürwortet das Einfügen von Schnittstellen in separate Projekte / Pakete.
Die PCMEF + -Architektur, über die das Buch spricht, hat die folgenden Prinzipien:
Die Beschreibung der Prinzipien 3 und 7 erklärt, warum dies eine gute Idee ist:
Siehe diesen Link: http://comp.mq.edu.au/books/pse/about_book/Ch9.pdf
quelle
Ja, dies ist eine sehr gute Vorgehensweise, da Sie damit die Benutzeroberfläche veröffentlichen können, ohne Ihre spezifische Implementierung zu veröffentlichen. Wenn Sie jedoch keine externen Schnittstellen veröffentlichen müssen, können Sie die Schnittstellendefinitionen problemlos in dasselbe Paket wie die Implementierung einfügen.
quelle
Wir tun dies dort, wo ich arbeite (dh: Schnittstelle in ein Paket und Implementierung in ein anderes) und der Hauptvorteil, den wir daraus ziehen, ist, dass wir leicht zwischen Implementierungen wechseln können.
quelle
Ich habe nicht viel Java-Erfahrung, aber ich mache dies gerne als 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 zum Ende verteilt sind Client, weil sie von einer Zwischenhändlerfabrik oder in einem Webdienstszenario über das Kabel übertragen werden.
quelle
Normalerweise setze ich Schnittstellen in die Implementierung ein, aber ich kann irgendwie sehen, warum Sie sie möglicherweise getrennt halten möchten. Angenommen, jemand wollte Klassen basierend auf Ihren Schnittstellen neu implementieren, er würde ein jar / lib / etc für Ihre Implementierung benötigen und nicht nur die Schnittstellen. Wenn sie getrennt sind, können Sie einfach "Hier ist meine Implementierung dieser Schnittstelle" sagen und damit fertig sein. Wie ich schon sagte, nicht was ich tue, aber ich kann irgendwie sehen, warum manche es wollen.
quelle
Ich mache das für ein Projekt und es funktioniert aus folgenden Gründen gut für mich:
Separat verpackte 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 zu haben (java.util.tree.Map, java.util.tree.Set). Es ist nur eine Standard-Datenstruktur, also stellen Sie sie mit den anderen Datenstrukturen zusammen. Wenn Sie es jedoch mit einem Puzzlespiel zu tun haben, das eine wirklich einfache Debug-Oberfläche und eine hübsche Produktionsoberfläche hat, haben Sie als Teil des Frontends möglicherweise einen com.your.app.skin.debug und einen com.your.app .skin.pretty. Ich würde diese nicht in dasselbe Paket einfügen, da sie unterschiedliche Aufgaben ausführen und ich weiß, dass ich auf SmurfNamingConvention (DebugAttackSurface, DebugDefenceSurface, PrettyAttackSurface usw.) zurückgreifen würde, um einen informellen Namespace für die beiden zu erstellen, wenn sie sich im selben Paket befinden.
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. Beispielsweise kann ich alle meine Schnittstellen in com.your.app.skin.framework haben und weiß, dass andere Pakete auf derselben Ebene des Paketbaums Implementierungen sind. Der Nachteil ist, dass dies eine unkonventionelle Konvention ist. Ehrlich gesagt werde ich sehen, wie gut diese Konvention in 6 Monaten ist :)
Ich benutze diese Technik nicht religiös. Es gibt Schnittstellen, die nur in einer bestimmten Implementierung sinnvoll sind. Ich stecke sie nicht in das Framework-Paket. Es gibt einige Pakete, in 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 der Programmgestaltung haben im Allgemeinen Vor- und Nachteile, und es gibt keine einheitliche Antwort. Zum Beispiel, warum würden Sie diese Technik jemals in einem winzigen 200-Zeilen-Programm verwenden? Angesichts meiner anderen architektonischen Entscheidungen ist es für mich sinnvoll, mein spezifisches Problem zu lösen. :) :)
quelle
Ich bevorzuge sie im gleichen Paket. Es ist sinnlos, ein Paket speziell für Schnittstellen zu erstellen. Dies ist besonders redundant für Teams, die ihre Schnittstellenpräfixe und -suffixe einfach lieben (z. B. "I" und "Impl" für Java).
Wenn eine Reihe 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 es läuft alles auf die Präferenz und Bequemlichkeit der Situation hinaus, nehme ich an.
quelle
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ählt wahrscheinlich jemand anderes einen für das Projekt relevanten Paketnamen.
Wenn es sich um dasselbe Projekt handelt, sehe ich im Allgemeinen keinen Vorteil darin, die Schnittstellen in einem von ihren Implementierungen getrennten Paket zu halten. Wenn es unübersichtlich wird, kann es unabhängig von der Schnittstellenanordnung zu anderen Problemen bei der Paketbenennung kommen.
quelle
Ich denke, es ist wichtig zu beachten, dass es für das OSGi-Framework schöner ist, dass sie sich in verschiedenen Paketen befinden, sodass Sie problemlos ein ganzes Paket exportieren können, während Sie die Implementierungspakete ausblenden.
quelle
Es gibt viele gute Gründe, Schnittstellen in einem von der Implementierung getrennten Paket zusammenzufassen. Beispielsweise möchten Sie möglicherweise einen Container verwenden, der Dependency Injection unterstützt , um die erforderlichen Implementierungen zur Laufzeit zu verkabeln. In diesem Fall muss nur die Schnittstelle zur Erstellungszeit vorhanden sein und die Implementierung kann zur Laufzeit bereitgestellt werden. Alternativ möchten Sie möglicherweise mehrere Implementierungen (z. B. die Verwendung von Scheinimplementierungen zum Testen) oder mehrere Versionen einer bestimmten Implementierung zur Laufzeit (z. B. für A / B-Tests usw.) bereitstellen . Für diese Art von Anwendungsfällen ist es bequemer, die Schnittstelle und die Implementierung separat zu verpacken.
quelle