私が以下を持っているとしましょう:
package me.my.pkg;
public interface Something {
/* ... couple of methods go here ... */
}
そして:
package me.my;
import me.my.pkg.Something;
public class SomeClass implements Something {
/* ... implementation of Something goes here ... */
/* ... some more method implementations go here too ... */
}
つまり、インターフェイスを実装するクラスは、実装するインターフェイスよりもパッケージ階層ルートの近くにありますが、どちらも同じパッケージ階層に属しています。
私が念頭に置いている特定のケースでこれが発生する理由は、Something
インターフェイスが論理的に属している機能と論理的な(両方の「期待する」および「現在のアーキテクチャを前提として必要な場所」)実装クラスは以前から存在し、インターフェースの論理的な配置から1レベル「上」に存在します。 実装クラスは論理的にme.my.pkgのどこにも属していません。
私の特定のケースでは、問題のクラスはいくつかのインターフェースを実装していますが、それはここでは何の違いもない(または少なくとも重要な違いはない)ように感じます。
これが許容可能なパターンであるかどうかを判断できません。それがそうであるか、そうでないか、そしてなぜですか?
はい、クラスとインターフェースの両方が正しいパッケージに確実に含まれている限り、問題ありません。
クラスをパッケージの階層セットに編成することは、クラスを継承階層に編成することに少し似ています。ほとんどの場合、問題なく機能しますが、適切でないケースがたくさんあります。
Javaには実際にはパッケージ階層さえありません-階層的な性質はフォルダー構造よりも拡張されないことに注意してください。 Java=言語デザイナーの代弁者になることはできませんが、彼らがこの決定を誤って行ったのではないかと思います!
プログラマーが探しているパッケージを見つけやすくするための補助機能として、「パッケージ階層」を考えると役立つ場合があります。
正式には合法ですが、これは コードの匂い を示している可能性があります。これがあなたのケースでそうであるかどうかを確認するには、3つの質問を自分自身に尋ねてください:
コードに余分な労力を費やす理由が見当たらない場合は、 maintainability のままにしておくことを検討してください。このアプローチは YAGNI原則 に基づいています。
プログラマは、必要と思われるまで機能を追加してはなりません...「実際に必要なときは常に実装し、必要なときだけは実装しないでください。」
以下の考慮事項は、さらなる分析への投資努力が正当化されることを前提としています。たとえば、長期的にコードが維持されることを期待している、または保守可能なコードを書くスキルの練習と磨きに興味があるとします。これが当てはまらない場合は、残りをスキップして構いません。これは必要ないからです。
注目に値する最初のことは、公式の コーディング規約 と tutorial のどちらもそれに関するガイダンスを提供しないことであり、この種の決定はプログラマの裁量によると予想されることを強く示します。
しかし、 [〜#〜] jdk [〜#〜] の開発者によって実装された設計を見ると、サブパッケージAPIをより高いレベルのものに「漏らす」ことはそこで行われていないことに気付くでしょう。例として、 concurrent util package とそのサブパッケージ- locks と atomic をご覧ください。
さて、コアAPI開発者はそれを避けているようで、なぜそうなのかを理解するために、なぜyouがそのようにパッケージ化したのですか?その自然な理由は、ある種の階層、つまり layers のような種類のパッケージユーザーと通信して、サブパッケージがより低いレベル/より専門的/より狭い使用法の概念に対応するようにすることです。
これは多くの場合、APIをより使いやすく理解しやすくするために、APIユーザーが特定のレイヤー内で操作できるようにするために、他のレイヤーに属する問題に煩わされるのを避けるために行われます。この場合、サブパッケージから低レベルのAPIを公開すると、意図に反することになります。
そのため、パッケージから提供される APIを実行するテストを作成する を強くお勧めします。誰かに review を依頼しても問題はありませんが、経験豊富なAPIレビューアーは、とにかくそのようなテストを提供するように依頼します。実は、APIをレビューするとき、実際の使用例を見るのに勝るものはありません。
テストを行うことのもう1つの利点は、設計を改善するために検討するさまざまなアイデアの評価を大幅に簡略化できることです。
比較的マイナーな実装の変更により、テストが大幅に簡素化され、インポート、オブジェクト、メソッドの呼び出しとパラメーターの量が減少することが時々ありました。テストを実行する前でも、これはさらに追求する価値のある方法の良い指標となります。
反対も私に起こりました-つまり、APIを簡略化することを目的とした私の実装の大規模で野心的な変更が、テストへのそれぞれのマイナーで取るに足らない影響によって間違っていることが証明されたとき、無益なアイデアを削除してコードをそのままにしておくのに役立ちます(おそらく単なるコメント付き)デザインの背景を理解し、他の人が間違った方法について学ぶのを助けるために追加されました)。
具体的なケースでは、最初に頭に浮かぶのは 継承から構成 の変更を検討することです。つまり、SomeClass
にSomething
を直接実装させる代わりに、オブジェクトを含めますそのインターフェースを実装するinsideクラス、
package me.my;
import me.my.pkg.Something;
public class SomeClass {
private Something something = new Something() {
/* ... implementation of Something goes here ... */
}
/* ... some more method implementations go here too ... */
}
このアプローチは将来的に見返りをもたらす可能性があり、SomeClass
の一部のサブクラスが誤ってSomething
のメソッドをオーバーライドして、予期しない方法で動作するリスクを防ぎます。