web-dev-qa-db-ja.com

パッケージを整理する(そして依存関係のサイクルを防ぐ)方法は?

Javaプロジェクトでいくつかのメトリックを実行していて、パッケージ間に依存関係のサイクルがたくさんあるようです。パッケージにデータを整理する方法がわからなかったので、何をしましたか。私には理にかなっていますが、それは明らかに間違っています。

私のプロジェクトはニューラルネットワークフレームワークです。ニューラルネットワークにはニューロンがあり、それらは接続で相互に接続されています。彼らはお互いに依存する必要があります。ただし、ニューロンにはさまざまな種類があるため、それらすべてを独自の「ニューロン」パッケージに入れることをお勧めします。明らかに、接続はNeuronではないので、パッケージに含めるべきではありませんが、相互に参照しているため、循環依存関係になっています。

これはほんの一例ですが、私にはこのような状況がもっとあります。このような状況にどのように対処しますか?

また、パッケージ階層の上位にあるパッケージ内のクラスは、より深いパッケージ内のクラスを参照することは想定されていないことも読みました。これは、パッケージ 'nn'のNeuralNetworkクラスがパッケージ 'nn.neurons'のNeuronを参照できないことを意味します。あなたたちはこの原則に従いますか?そして、NeuralNetworkを「nn.networks」などに移動するとどうなりますか?その場合、子ではなく兄弟パッケージを参照します。それはより良い習慣ですか?

27
Jordi

antcontribVerifyDesignタスク はあなたが望むことをするのを助けます:

たとえば、1つのソースツリーに3つのパッケージがある場合

* biz.xsoftware.presentation
* biz.xsoftware.business
* biz.xsoftware.dataaccess

そして当然のことながら、プレゼンテーションはビジネスパッケージにのみ依存する必要があり、ビジネスはデータアクセスに依存する必要があります。この方法でデザインを定義し、違反した場合、verifydesignantタスクが呼び出されたときにビルドが失敗します。たとえば、biz.xsoftware.presentationでクラスを作成し、そのクラスがbiz.xsoftware.dataaccessのクラスに依存している場合、ビルドは失敗します。これにより、設計が実際に文書化されている内容に従うことが保証されます(少なくともある程度は)。これは、自動ビルドで特に便利です

したがって、物事をどのように編成するかを決定したら、コンパイル時に要件を適用できます。また、きめ細かい制御が可能になるため、特定のケースでこれらの「ルール」を破ることができます。したがって、いくつかのサイクルを許可できます。

やりたい方法によっては、「utils」パッケージが理にかなっている場合があります。

あなたが引用する特定のケースについて...私はこのようなことをするかもしれません:

  • パッケージnnにはNueronとConnectionが含まれています
  • パッケージnn.neuronsには、Nueronのサブクラスが含まれています

NeuronとConnectionはどちらもNeuralNetowrkで使用される高レベルの概念であるため、これらをすべてまとめることは理にかなっています。 NeuronクラスとConnectionクラスは相互に参照できますが、ConnectionクラスはNeuronサブクラスについて知る必要はありません。

13
TofuBeer

まず第一に、パッケージ間の循環依存が悪いので、あなたは当然のことながら心配しています。そこから生じる問題は、プロジェクトの規模とともに重要性が増しますが、この状況に時間どおりに取り組む理由はありません。

一緒に再利用するクラスを同じパッケージに配置して、クラスを編成する必要があります。したがって、たとえばAbstractNeuronとAbstractConnectionがある場合は、それらを同じパッケージに配置します。 HumanNeuronとHumanConnectionの実装がある場合は、これらを同じパッケージ(たとえば、*。network.humanと呼ばれます)に配置します。または、BaseConnectionと多くの異なるNeuronsなど、1つのタイプの接続しかない場合もあります。原則は同じままです。 BaseConnectionをBaseNeuronと一緒に配置します。 HumanNeuronとHumanSignalなどが一緒にパッケージ化されています。VirtualNeuronとVirtualSignalなどが一緒になっています。「明らかに、接続はNeuronではないので、パッケージに含めるべきではありません。」これはそれほど明白ではなく、正確には正しくありません。

すべてのニューロンを同じパッケージに入れたと言います。すべての実装を一緒に再利用しない限り、これも正しくありません。もう一度、私が上で説明したスキームを見てください。プロジェクトが非常に小さいため、すべてを1つのパッケージに入れるか、説明されているようにパッケージの整理を開始します。詳細については、 一般的な再利用の原則 をご覧ください。

パッケージ内のクラスは一緒に再利用されます。パッケージ内のクラスの1つを再利用する場合は、それらをすべて再利用します。

9
Dan

あなたが説明するような循環依存関係は悪いとは思いませんhave。相互に依存する概念が同じ抽象化レベルにあり、アーキテクチャの同じ部分に関連している限り、これらを互いに隠す必要はないかもしれません。 Neurons and Connectionsは、私の理解ではこの法案に適合しています。

このような結合を減らすための一般的な方法は、インターフェイスを抽出し、場合によってはこれらを別のモジュールに配置することです。単一のプロジェクト内でパッケージごとに整理するだけでは、実装の詳細を十分に隠すことはできません。実装を実際に隠すことができる一般的なパターンは次のとおりです。

クライアントコード---->インターフェース<---実装

このパターンでは、「Implementation」モジュールをクライアントコードから非表示にします。つまり、「Clientcode」モジュールのコードには実装コードすら表示されません。

パッケージのネストにはいくつかの目的があります。一部のプロジェクトには、パッケージで編成されたドメインモデルがある場合があります。この場合、パッケージはドメインのグループ化を反映しており、参照はパッケージを上下させる可能性があります。サービスの実装などに関しては、提案されたパターンは非常に一般的であり、従うのが良いことです。パッケージ階層が深くなるほど、クラスがより具体的になると考えられます。

5
krosenvold

どのようなコードサイズについて話しているのですか?クラスが10〜20しかない場合は、コードをパッケージに整理する必要はありません(また、そうすべきではありません)。

プロジェクトが成長するにつれて、最初に区別したいのは、ユーザーインターフェイスコードを基盤となるデータモデルおよびロジックから分離することです。適切な単体テストを実行できるようにするには、層をきれいに分離することが重要です。

循環依存関係を取り除くのに問題がある場合は、クラスが実際に相互依存しており、同じパッケージに存在する必要がある場合があります。

抽象化レイヤーを正しく取得することは、コード構造全体を設計する際の最も重要な側面の1つです。

5
JesperE

このような状況にどのように対処しますか?

循環依存は本質的に悪いわけではありません。実際、これは「治療が病気よりも悪い」場合である場合があります。インターフェースを抽出すると、コードの複雑さが増し、間接参照の層が追加されます。 非常に単純な関係ではおそらく価値がありません。

5
John Feminella