正式には合法ですが、コードの臭いを示している可能性があります。これがあなたのケースでそうであるかどうかを調べるには、3つの質問を自分自身にしてください:
- 必要ですか?
- なぜそのようにパッケージ化したのですか?
- パッケージAPIが使いやすいかどうかはどのようにしてわかりますか?
必要ですか?
コードの保守性に余分な労力を費やす理由が見当たらない場合は、そのままにすることを検討してください。このアプローチはYAGNIの原則に基づいています
プログラマーは、必要と思われるまで機能を追加すべきではありません...「実際にそれらが必要なときに常に実装し、必要があると予見しているときは決して実装しないでください。」
以下の考慮事項は、さらなる分析への投資努力が正当化されることを前提としています。たとえば、長期的にコードが維持されることを期待している、または保守可能なコードを書くスキルの練習と磨きに興味があるとします。これが当てはまらない場合は、残りをスキップしてかまいません。これは必要ないからです。
なぜそのようにパッケージ化したのですか?
注目に値する最初のことは、公式のコーディング規約とチュートリアルのどちらもそれに関するガイダンスを提供しないことであり、この種の決定はプログラマーの裁量によるものであると予想されることを強く示唆します。
しかし、JDKの開発者によって実装された設計を見ると、サブパッケージAPIをより高いレベルのAPIに「漏らす」ことが実践されていないことに気付くでしょう。例として、並行utilパッケージとそのサブパッケージ-locksとatomicを見てください。
- 内部でサブパッケージAPIを使用することは問題ないと見なされることに注目する価値があります。たとえば、ConcurrentHashMap実装はロックをインポートし、ReentrantLockを使用します。ロックAPIをパブリックとして公開しないだけです。
さて、コアAPI開発者はそれを避けているようで、なぜそれがそうであるかを知るために、なぜあなたはそれをそのようにパッケージ化したのでしょうか?そのための自然な理由は、ユーザーがソートの階層のいくつかの並べ替え、パッケージと通信する願望であろう層をサブパッケージ相当レベル/より特化/狭い使用概念を低下させるように、。
これは多くの場合、APIをより使いやすく理解しやすくするために、APIユーザーが特定のレイヤー内で操作できるように、他のレイヤーに属する問題に煩わされることを避けるために行われます。これが事実である場合、サブパッケージから低レベルのAPIを公開すると、意図に反することになります。
- 余談ですが、なぜこのようにパッケージ化するのかわからない場合は、設計に戻って、これを理解するまで徹底的に分析することを検討してください。考えてみてください。今でもあなたに説明するのが難しい場合、将来パッケージを保守して使用する人にとってどれほど難しいでしょうか?
パッケージAPIが使いやすいかどうかはどのようにしてわかりますか?
そのため、パッケージで提供されるAPIを実行するテストを作成することを強くお勧めします。誰かにレビューを依頼しても害はありませんが、経験豊富なAPIレビューアーは、とにかくそのようなテストを提供するように依頼するでしょう。実は、APIをレビューするときに実際の使用例を見るのに勝るものはありません。
- 単一のパラメーターを持つ単一のメソッドでクラスを見て、驚くほど単純な夢を見ることもできますが、それを呼び出すテストで他に10個のオブジェクトを作成する必要があり、合計50個のパラメーターを持つ20のメソッドのように呼び出すと、これは危険な錯覚を破り、真の複雑さを明らかにしますデザイン。
テストを行うことのもう1つの利点は、設計を改善するために検討するさまざまなアイデアの評価を大幅に簡略化できることです。
ときどき、比較的小さな実装の変更によってテストが大幅に簡素化され、インポート、オブジェクト、メソッドの呼び出し、およびパラメーターの量が減少することがありました。テストを実行する前でも、これはさらに追求する価値のある方法の良い指標となります。
反対も私に起こりました-つまり、APIを簡素化することを目的とした実装での大規模で野心的な変更が、テストへのそれぞれのマイナーで取るに足らない影響によって間違っていることが証明されたとき、無益なアイデアを捨ててコードをそのままにしておくのに役立ちます(おそらくコメントだけで)デザインの背景を理解し、他の人が間違った方法について学ぶのを助けるために追加されました)。
具体的なケースでは、最初に頭に浮かぶのは、継承をコンポジションに変更することを検討することです。つまり、をSomeClass
直接実装する代わりに、クラス内にSomething
そのインターフェースを実装するオブジェクトを含めます。
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
、計画外の方法で動作するようにします。