Wie soll ich eine Java-Anwendung strukturieren, wo platziere ich meine Klassen?

StackOverflow https://stackoverflow.com/questions/7596

  •  08-06-2019
  •  | 
  •  

Frage

Zunächst einmal weiß ich, wie man eine Java-Anwendung erstellt.Aber ich habe immer darüber nachgedacht, wo ich meine Kurse unterbringen soll.Es gibt Befürworter einer streng domänenorientierten Organisation der Pakete, andere trennen sie nach Ebenen.

Ich selbst hatte immer Probleme damit

  • Benennung,
  • Platzierung

Also,

  1. Wo platzieren Sie Ihre domänenspezifischen Konstanten (und was ist der beste Name für eine solche Klasse)?
  2. Wo platzieren Sie Klassen für Dinge, die sowohl infrastrukturell als auch domänenspezifisch sind (ich habe beispielsweise eine FileStorageStrategy-Klasse, die die Dateien entweder in der Datenbank oder alternativ in der Datenbank speichert)?
  3. Wohin mit Ausnahmen?
  4. Gibt es Standards, auf die ich mich beziehen kann?
War es hilfreich?

Lösung

Mir gefällt Maven's wirklich sehr gut Standardverzeichnislayout.

Eine der Schlüsselideen für mich besteht darin, zwei Quellwurzeln zu haben – eine für Produktionscode und eine für Testcode, etwa so:

MyProject/src/main/java/com/acme/Widget.java
MyProject/src/test/java/com/acme/WidgetTest.java

(Hier sind sowohl src/main/java als auch src/test/java Quellwurzeln).

Vorteile:

  • Ihre Tests verfügen über Zugriff auf Paketebene (oder „Standard“) auf Ihre zu testenden Klassen.
  • Sie können ganz einfach nur Ihre Produktionsquellen in ein JAR packen, indem Sie src/test/java als Quellstammverzeichnis löschen.

Eine Faustregel zur Klasseneinteilung und zu Paketen:

Im Allgemeinen sind gut strukturierte Projekte kostenlos zirkuläre Abhängigkeiten.Erfahren Sie, wann sie schlecht sind (und wann sie es sind). nicht) und denken Sie über ein Tool wie JAbhängig oder SonarJ Das wird Ihnen helfen, sie zu beseitigen.

Andere Tipps

Ich bin ein großer Fan von organisierten Quellen, daher erstelle ich immer die folgende Verzeichnisstruktur:

/src - for your packages & classes
/test - for unit tests
/docs - for documentation, generated and manually edited
/lib - 3rd party libraries
/etc - unrelated stuff
/bin (or /classes) - compiled classes, output of your compile
/dist - for distribution packages, hopefully auto generated by a build system

In /src verwende ich die Standard-Java-Muster:Paketnamen beginnen mit Ihrer Domain (org.yourdomain.yourprojectname) und Klassennamen, die den OOP-Aspekt widerspiegeln, den Sie mit der Klasse erstellen (siehe die anderen Kommentatoren).Gängige Paketnamen wie util, Modell, Sicht, Veranstaltungen sind auch nützlich.

Ich neige dazu, Konstanten für ein bestimmtes Thema in eine eigene Klasse einzufügen Sitzungskonstanten oder Servicekonstanten im selben Paket der Domänenklassen.

Wo ich arbeite, verwenden wir Maven 2 und wir haben einen ziemlich schönen Archetyp für unsere Projekte.Das Ziel bestand darin, eine gute Trennung der Belange zu erreichen. Deshalb haben wir eine Projektstruktur mit mehreren Modulen definiert (eines für jede Anwendungsschicht):- gemeinsam:Gemeinsamer Code, der von den anderen Ebenen verwendet wird (z. B. i18n) - Entitäten:Die Domain -Unternehmen - Repositories:Dieses Modul enthält die DAOS -Schnittstellen und Implementierungen - Services -Intf:Schnittstellen für die Dienste (z. B. Userservice, ...) - Dienste -IMPL:Implementierungen der Dienste (z. B. UserServiceImpl) - Web:Alles in Bezug auf den Webinhalt (z. B. CSS, JSPS, JSF -Seiten, ...) - WS:Internetdienste

Jedes Modul hat seine eigenen Abhängigkeiten (z. B. könnten Repositorys über JPA verfügen) und einige sind projektweit (daher gehören sie zum gemeinsamen Modul).Abhängigkeiten zwischen den verschiedenen Projektmodulen trennen die Dinge klar voneinander (z. B. hängt die Webschicht von der Serviceschicht ab, weiß aber nichts über die Repository-Schicht).

Jedes Modul hat sein eigenes Basispaket. Wenn das Anwendungspaket beispielsweise „com.foo.bar“ lautet, dann haben wir:

com.foo.bar.common
com.foo.bar.entities
com.foo.bar.repositories
com.foo.bar.services
com.foo.bar.services.impl
...

Jedes Modul respektiert die Standard-Maven-Projektstruktur:

   src\
   ..main\java
     ...\resources
   ..test\java
     ...\resources

Unit-Tests für eine bestimmte Ebene finden problemlos ihren Platz unter \src est...Alles, was domänenspezifisch ist, hat seinen Platz im Entitätenmodul.Nun sollte so etwas wie eine FileStorageStrategy in das Repositories-Modul einfließen, da wir nicht genau wissen müssen, wie die Implementierung aussieht.In der Serviceschicht kennen wir nur die Repository-Schnittstelle, die konkrete Implementierung ist uns egal (Separation of Concerns).

Dieser Ansatz bietet mehrere Vorteile:

  • klare Trennung der Anliegen
  • Jedes Modul kann als JAR (oder als War im Fall des Webmoduls) verpackt werden und ermöglicht so eine einfachere Wiederverwendung von Code (z. B. könnten wir das Modul im Maven-Repository installieren und in einem anderen Projekt wiederverwenden).
  • maximale Unabhängigkeit jedes Teils des Projekts

Ich weiß, dass dies nicht alle Ihre Fragen beantwortet, aber ich denke, dies könnte Sie auf den richtigen Weg bringen und sich für andere als nützlich erweisen.

Klassennamen sollten immer beschreibend und selbsterklärend sein.Wenn Sie mehrere Verantwortungsbereiche für Ihre Klassen haben, sollten diese wahrscheinlich umgestaltet werden.

Ebenso für Ihre Pakete.Sie sollten nach Verantwortungsbereich gruppiert werden.Jede Domain hat ihre eigenen Ausnahmen.

Schwitzen Sie im Allgemeinen nicht, bis Sie den Punkt erreicht haben, an dem es überwältigend und aufgebläht wird.Setzen Sie sich dann hin und programmieren Sie nichts, überarbeiten Sie einfach die Klassen und kompilieren Sie regelmäßig, um sicherzustellen, dass alles funktioniert.Fahren Sie dann wie zuvor fort.

Verwenden Sie Pakete, um zusammengehörige Funktionen zu gruppieren.

Normalerweise steht ganz oben in Ihrem Paketbaum Ihr Domänenname in umgekehrter Reihenfolge (com.domain.subdomain), um die Einzigartigkeit zu gewährleisten, und dann gibt es normalerweise ein Paket für Ihre Anwendung.Dann unterteilen Sie das nach verwandten Bereichen, also Ihrem FileStorageStrategy könnte hineingehen, sagen wir, com.domain.subdomain.myapp.storage, und dann könnte es bestimmte Implementierungen/Unterklassen/was auch immer darin geben com.domain.subdomain.myapp.storage.file Und com.domain.subdomain.myapp.storage.database.Diese Namen können ziemlich lang werden, aber import hält sie alle oben in den Dateien und IDEs können auch dabei helfen, dies zu verwalten.

Ausnahmen befinden sich normalerweise im selben Paket wie die Klassen, die sie auslösen. Wenn Sie also beispielsweise Folgendes hätten: FileStorageException es würde in das gleiche Paket kommen wie FileStorageStrategy.Ebenso wäre eine Schnittstelle, die Konstanten definiert, im selben Paket.

Es gibt nicht wirklich einen Standard als solchen, verwenden Sie einfach Ihren gesunden Menschenverstand und wenn alles zu chaotisch wird, überarbeiten Sie ihn!

Eine Sache, die ich für Unit-Tests sehr hilfreich fand, war, ein myApp/src/- und auch ein myApp/test_src/-Verzeichnis zu haben.Auf diese Weise kann ich Unit-Tests in denselben Paketen platzieren wie die Klassen, die sie testen, und dennoch kann ich die Testfälle problemlos ausschließen, wenn ich meine Produktionsinstallation vorbereite.

Kurze Antwort:Zeichnen Sie Ihre Systemarchitektur in Form von nebeneinander gezeichneten Modulen, wobei jedes Modul vertikal in Schichten unterteilt ist (z. B.Sichtweise, Modell, Persistenz).Dann verwenden Sie eine Struktur wie com.mycompany.myapp.somemodule.somelayer, z.B. com.mycompany.myapp.client.view oder com.mycompany.myapp.server.model.

Verwendung der obersten Paketebene für die Anwendung Module, im altmodischen Sinne der Informatik Modulare Programmierung, sollte klar sein.Allerdings vergessen wir das bei den meisten Projekten, an denen ich gearbeitet habe, und am Ende haben wir ein Durcheinander von Paketen ohne diese Top-Level-Struktur.Dieses Anti-Pattern zeigt sich normalerweise als Paket für etwas wie „Listener“ oder „Aktionen“, das ansonsten nicht verwandte Klassen gruppiert, einfach weil sie zufällig dieselbe Schnittstelle implementieren.

Verwenden Sie innerhalb eines Moduls oder in einer kleinen Anwendung Pakete für die Anwendungsschichten.Abhängig von der Architektur enthalten die Pakete wahrscheinlich Folgendes:

  • com.mycompany.myapp.view
  • com.mycompany.myapp.model
  • com.mycompany.myapp.services
  • com.mycompany.myapp.rules
  • com.mycompany.myapp.persistence (oder „dao“ für Datenzugriffsschicht)
  • com.mycompany.myapp.util (Achten Sie darauf, dass dies so verwendet wird, als wäre es „Verschiedenes“).

Innerhalb jeder dieser Ebenen ist es selbstverständlich, Klassen nach Typ zu gruppieren, wenn es viele davon gibt.Ein häufiges Anti-Pattern besteht hier darin, unnötig viele Pakete und Ebenen von Unterpaketen einzuführen, sodass jedes Paket nur wenige Klassen enthält.

Ich denke, halten Sie es einfach und denken Sie nicht zu viel darüber nach.Abstrahieren Sie nicht zu viel und schichten Sie nicht zu viel.Halten Sie es einfach ordentlich, und wenn es wächst, ist es trivial, es umzugestalten.Eine der besten Funktionen von IDEs ist das Refactoring. Warum also nicht davon Gebrauch machen und Ihre Gehirnleistung für die Lösung von Problemen im Zusammenhang mit Ihrer App einsparen, anstatt Metaprobleme wie die Codeorganisation?

Eine Sache, die ich in der Vergangenheit getan habe: Wenn ich einen Kurs erweitere, versuche ich, deren Konventionen zu befolgen.Wenn ich beispielsweise mit dem Spring Framework arbeite, habe ich meine MVC -Controller -Klassen in einem Paket namens com.mydomain.myapp.web.servlet.mvc Wenn ich nicht etwas erweitere, das ich einfach mit dem einfachsten ausdehne.com.mydomain.domain für Domänenobjekte (obwohl dieses Paket etwas unhandlich werden könnte, wenn Sie eine Menge Domänenobjekte haben).Bei domänenspezifischen Konstanten habe ich sie tatsächlich als öffentliche Konstanten in die am stärksten verwandte Klasse eingefügt.Wenn ich beispielsweise eine „Member“-Klasse und eine Konstante für die maximale Länge des Mitgliedsnamens habe, füge ich sie in die Klasse „Member“ ein.Einige Shops erstellen eine separate Constants-Klasse, aber ich sehe keinen Sinn darin, nicht verwandte Zahlen und Zeichenfolgen in einer einzigen Klasse zusammenzufassen.Ich habe einige andere Shops gesehen, die versucht haben, dieses Problem durch die Erstellung von SEPARATE Constants-Klassen zu lösen, aber das scheint einfach Zeitverschwendung zu sein und das Ergebnis ist zu verwirrend.Bei Verwendung dieses Setups werden bei einem großen Projekt mit mehreren Entwicklern überall Konstanten dupliziert.

Ich unterteile meine Kurse gerne in Pakete, die miteinander in Zusammenhang stehen.

Zum Beispiel:Modell Für datenbankbezogene Aufrufe

Sicht Kurse, die sich mit dem befassen, was Sie sehen

Kontrolle Kernfunktionalitätsklassen

Nutzen Sonstiges.Klassen, die verwendet werden (typischerweise statische Funktionen)

usw.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top