Java アプリケーションをどのように構成すればよいですか?クラスをどこに配置すればよいですか?

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

  •  08-06-2019
  •  | 
  •  

質問

まず第一に、私は Java アプリケーションを構築する方法を知っています。しかし、クラスをどこに配置するかについては常に困惑していました。厳密にドメイン指向の方法でパッケージを編成することを支持する人もいれば、階層ごとに分離する人もいます。

私自身も常に問題を抱えていました

  • ネーミング、
  • 置く

それで、

  1. ドメイン固有の定数をどこに置きますか (そしてそのようなクラスに最適な名前は何ですか)?
  2. インフラストラクチャとドメイン固有の両方のクラスをどこに配置しますか (たとえば、ファイルをデータベースまたはデータベースに保存する FileStorageStrategy クラスがあります)。
  3. 例外をどこに置くか?
  4. 参照できる基準はありますか?
役に立ちましたか?

解決

メイビンが本当に好きになりました 標準のディレクトリレイアウト.

私にとって重要なアイデアの 1 つは、次のように 2 つのソース ルートを用意することです。1 つは運用コード用、もう 1 つはテスト コード用です。

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

(ここでは、src/main/java と src/test/java の両方がソース ルートです)。

利点:

  • テストには、テスト対象のクラスに対するパッケージ (または「デフォルト」) レベルのアクセス権があります。
  • src/test/java をソース ルートとしてドロップすることで、本番ソースのみを JAR に簡単にパッケージ化できます。

クラスの配置とパッケージに関する経験則の 1 つは次のとおりです。

一般的に言えば、適切に構造化されたプロジェクトには、 循環依存関係. 。いつ悪いのか(そして、いつ悪いのか)を学びましょう ない)、次のようなツールを検討してください。 J依存 または ソナーJ それはそれらを排除するのに役立ちます。

他のヒント

私は整理されたソースの大ファンなので、常に次のディレクトリ構造を作成します。

/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

/src では、デフォルトの Java パターンを使用しています。ドメイン (org.yourdomain.yourprojectname) で始まるパッケージ名と、クラスで作成している OOP の側面を反映したクラス名 (他のコメント投稿者を参照)。一般的なパッケージ名は次のとおりです ユーティリティ, モデル, ビュー, イベント も便利です。

私は、次のように、特定のトピックの定数を独自のクラスに置く傾向があります。 セッション定数 または サービス定数 ドメイン クラスの同じパッケージ内にあります。

私が働いているところでは Maven 2 を使用しており、プロジェクトに非常に優れた原型を持っています。目標は、懸念事項を適切に分離することでした。そのため、複数のモジュール (アプリケーションの「レイヤー」ごとに 1 つ) を使用してプロジェクト構造を定義しました。- 一般:他のレイヤーで使用される共通コード(例:i18n) - エンティティ:ドメインエンティティ - リポジトリ:このモジュールには、DAOSインターフェイスと実装が含まれています-Services -intf:サービスのインターフェイス(例:userservice、...) - services -impl:サービスの実装(例:userserviceimpl) - web:Webコンテンツに関するすべて(例:CSS、JSP、JSFページなど...) - WS:ウェブサービス

各モジュールには独自の依存関係があり (たとえば、リポジトリには jpa が存在する可能性があります)、一部はプロジェクト全体に依存します (したがって、それらは共通モジュールに属します)。異なるプロジェクト モジュール間の依存関係は、物事を明確に分離します (たとえば、Web 層はサービス層に依存しますが、リポジトリ層については知りません)。

各モジュールには独自の基本パッケージがあります。たとえば、アプリケーション パッケージが「com.foo.bar」の場合、次のようになります。

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

各モジュールは標準の Maven プロジェクト構造を尊重します。

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

特定の層の単体テストは、\src est... の下の場所を簡単に見つけることができます。ドメイン固有のものはすべてエンティティ モジュール内に配置されます。実装が何であるかを正確に知る必要はないため、FileStorageStrategy のようなものをリポジトリ モジュールに含める必要があります。サービス層では、リポジトリ インターフェイスのみがわかり、特定の実装が何であるかは気にしません (懸念事項の分離)。

このアプローチには複数の利点があります。

  • 懸念事項を明確に分離する
  • 各モジュールは jar (Web モジュールの場合は war) としてパッケージ化できるため、コードの再利用が容易になります (例: モジュールを Maven リポジトリにインストールし、別のプロジェクトで再利用できます)
  • プロジェクトの各部分の最大限の独立性

これがすべての質問に答えられるわけではないことは承知していますが、これはあなたを正しい道に導き、他の人にとっても役に立つかもしれないと思います。

クラス名は常に説明的でわかりやすいものにする必要があります。クラスに対して複数の責任ドメインがある場合は、おそらくそれらをリファクタリングする必要があります。

パッケージについても同様です。責任の範囲ごとにグループ化する必要があります。すべてのドメインには独自の例外があります。

通常、汗がひどくなり膨満感を感じるまでは汗をかかないでください。次に、座ってコーディングを行わず、クラスをリファクタリングし、定期的にコンパイルしてすべてが機能することを確認します。その後、以前と同様に続行します。

パッケージを使用して、関連する機能をグループ化します。

通常、パッケージ ツリーの最上位はドメイン名を逆にしたものです (com.domain.subdomain) 一意性を保証するため、通常はアプリケーション用のパッケージが存在します。次に、それを関連領域ごとに細分化します。 FileStorageStrategy 入るかも知れません、と言うと、 com.domain.subdomain.myapp.storage, そして、特定の実装/サブクラス/その他が存在する可能性があります com.domain.subdomain.myapp.storage.file そして com.domain.subdomain.myapp.storage.database. 。これらの名前はかなり長くなる場合がありますが、 import それらはすべてファイルの先頭に保持され、IDE はその管理にも役立ちます。

例外は通常、例外をスローするクラスと同じパッケージに入れられるため、次のようにします。 FileStorageException それは同じパッケージに入ります FileStorageStrategy. 。同様に、定数を定義するインターフェイスも同じパッケージ内にあります。

実際にはそのような標準はありません。常識に従ってください。すべてが面倒になったらリファクタリングしてください。

単体テストに非常に役立つと感じたのは、myApp/src/ ディレクトリと myApp/test_src/ ディレクトリを用意することです。このようにして、単体テストをテストするクラスと同じパッケージに配置できますが、運用環境のインストールを準備するときにテスト ケースを簡単に除外できます。

短い答え:システム アーキテクチャをモジュールの観点から描画し、各モジュールを垂直に層にスライスして並べて描画します (例:ビュー、モデル、永続性)。次に、次のような構造を使用します com.mycompany.myapp.somemodule.somelayer, 、例えば com.mycompany.myapp.client.view または com.mycompany.myapp.server.model.

アプリケーションに最上位レベルのパッケージを使用する モジュール, 、昔ながらのコンピューターサイエンスの意味で、 モジュール式プログラミング, 、明らかなはずです。しかし、私がこれまで取り組んできたプロジェクトのほとんどでは、それを忘れてしまい、トップレベル構造のない混乱したパッケージができてしまいます。このアンチパターンは通常、たまたま同じインターフェイスを実装しているという理由だけで、無関係なクラスをグループ化する「リスナー」や「アクション」などのパッケージとして表示されます。

モジュール内または小規模アプリケーションでは、アプリケーション層のパッケージを使用します。アーキテクチャに応じて、パッケージには次のようなものが含まれる可能性があります。

  • com.mycompany.myapp.view
  • com.mycompany.myapp.model
  • com.mycompany.myapp.services
  • com.mycompany.myapp.rules
  • com.mycompany.myapp.persistence (またはデータアクセス層の場合は「dao」)
  • com.mycompany.myapp.util (これは「その他」であるかのように使用されることに注意してください)

これらの各レイヤー内で、クラスが多数ある場合は、タイプ別にグループ化するのが自然です。ここでの一般的なアンチパターンは、不必要に多くのパッケージとサブパッケージのレベルを導入しすぎて、各パッケージ内のクラスが数個だけになることです。

あまり考えすぎず、シンプルに考えてください。抽象化しすぎたり、レイヤーを重ねすぎたりしないでください。きちんと整理しておけば、成長に合わせてリファクタリングするのは簡単です。IDE の最も優れた機能の 1 つはリファクタリングです。そのため、これを利用して、コード編成などのメタ問題ではなく、アプリに関連する問題を解決するために脳力を節約してみてはいかがでしょうか。

私が過去にやったことの 1 つは、クラスを拡張する場合、その規則に従おうとすることです。たとえば、Spring Frameworkを使用する場合、MVCコントローラークラスをcom.mydomain.myapp.web.servlet.mvcというパッケージに入れて、最も簡単なものを拡張していない場合は、ドメイン オブジェクトの場合は com.mydomain.domain (ただし、大量のドメイン オブジェクトがある場合、このパッケージは少し扱いに​​くくなる可能性があります)。ドメイン固有の定数については、実際に最も関連性の高いクラスのパブリック定数として配置します。たとえば、「Member」クラスがあり、メンバー名の最大長定数がある場合、それを Member クラスに置きます。ショップによっては別個の Constants クラスを作成しているところもありますが、関連のない数値や文字列を 1 つのクラスにまとめることの価値はわかりません。他のショップが SEPARATE Con​​stants クラスを作成することでこの問題を解決しようとしているのを見たことがありますが、それは時間の無駄であり、結果はあまりにも混乱しているように思えます。この設定を使用すると、複数の開発者がいる大規模なプロジェクトで定数があちこちに重複することになります。

私はクラスを相互に関連するパッケージに分割するのが好きです。

例えば:モデル データベース関連の呼び出しの場合

ビュー 見たものを扱う授業

コントロール コア機能クラス

使用量 その他何でも。使用されるクラス (通常は静的関数)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top