私がこの質問をしているのは、彼らが非常に正当な理由でそれを行ったと信じており、ほとんどの人がこれまでのところ業界での私の経験から、それを適切に使用していないためです。しかし、私の理論が真実である場合、なぜそれらがプライベートアクセス修飾子を含んでいたのかわかりません...?
デフォルトのアクセスが適切に使用されている場合、カプセル化を維持しながらテスト性が向上すると私は信じています。また、プライベートアクセス修飾子が冗長になります。
デフォルトのアクセス修飾子を使用して、他の世界から非表示にする必要のあるメソッドに一意のパッケージを使用することで同じ効果を提供できます。これは、テストフォルダー内のパッケージと同じように、テスト性を損なうことなくこれを実行しますソースフォルダで宣言されているすべてのデフォルトメソッドにアクセスできます。
Javaはパッケージアクセスを「デフォルト」として使用する理由です。しかし、なぜプライベートアクセスも含まれていたのかわかりません。有効なユースケースがあると確信しています...
彼らは平均的なプログラマーが何をするかについて良い考えを持っていたと思います。平均的なプログラマーとは、プログラミングがあまり得意ではないが、とにかく仕事が得られる人をいいます。
彼らが「パブリック」アクセスをデフォルトのものにした場合、ほとんどのプログラマーは他のものを使用することを決して気にしません。結果はどこにでも多くの スパゲッティコード になります。 (技術的にどこからでも何かを呼び出すことが許可されているのに、なぜメソッド内にロジックをカプセル化する必要があるのですか?)
「プライベート」をデフォルトのアクセスにすると、ほんの少し良くなるだけです。ほとんどの初心者プログラマーは、「どこでも「公開」を書く必要がある」という経験則を考案(およびブログで共有)し、次にJavaが非常に悪いので不平を言う)どこにでも「公開」することを強制し、多くのスパゲッティコードも生成します。
パッケージアクセスとは、プログラマーが1つのパッケージ内にコードを記述しているときにずさんなプログラミング手法を使用できるようにするものですが、さらにパッケージを作成するときには再検討する必要があります。それは良いビジネス慣行と醜い現実の間の妥協です。のように:あなたが主張するなら、いくつかのスパゲッティコードを書いてください、しかしパッケージ内に醜い混乱を残してください;少なくとも、パッケージ間にいくつかのより良いインターフェースを作成します。
おそらく他の理由もあるでしょうが、私はこれを過小評価しないでしょう。
デフォルトのアクセスでは、プライベートアクセス修飾子が冗長になることはありません。
言語デザイナーの位置付けは、公式チュートリアルに反映されています- クラスのメンバーへのアクセスの制御 であり、非常に明確です(便宜上、引用内の関連するステートメント太字) ):
アクセスレベルの選択に関するヒント:
他のプログラマがクラスを使用する場合、誤用によるエラーが発生しないようにする必要があります。アクセスレベルはこれを行うのに役立ちます。
- 特定のメンバーにとって意味のある最も限定的なアクセスレベルを使用します。 正当な理由がない限り、プライベートを使用してください。
- 定数以外のパブリックフィールドは避けてください。 (チュートリアルの多くの例では、パブリックフィールドを使用しています。これは、いくつかのポイントを簡潔に説明するのに役立ちますが、量産コードには推奨されません。)パブリックフィールドは、特定の実装にリンクし、コードを変更する際の柔軟性を制限する傾向があります。
完全にプライベート修飾子を削除することの正当化としてのテスト容易性へのアピールは間違っています。これは、たとえば TDDの新機能です。今はプライベートメソッドを避けますか? =
もちろん、プライベートメソッドを使用することも、テストすることもできます。
プライベートメソッドを実行するsome方法があり、その場合はその方法でテストできます。または、noプライベートを実行する方法、その場合:なぜ一体それをテストしようとしているのか、いまいましいものを削除してください...
パッケージレベルアクセスの目的と使用に関する言語設計者の位置付けは、別の公式チュートリアル パッケージの作成と使用 で説明されており、プライベート修飾子を削除するという考えと共通点はありません(便宜上、関連性があります)作成された引用内のステートメントbold):
次のようないくつかの理由により、これらのクラスとインターフェイスをパッケージにバンドルする必要があります。
- あなたや他のプログラマーは、これらのタイプが関連していることを簡単に判断できます...
- パッケージ内のタイプが相互に無制限にアクセスできるようにしながら、パッケージ外のタイプのアクセスを制限することができます...
<朗報「私は十分な泣き言を聞いたと思います。大声ではっきり言うときが来たと思います... ">」
以下の注記は、読者が コードカバレッジ に精通していることを前提としています。そうでない場合は、ユニットテストやテストに興味がある人にとって非常に役立つので、時間をかけて習得してください。
よし、プライベートメソッドと単体テストを取得しました。カバレッジ分析でギャップがあることがわかりましたが、プライベートメソッドはテストの対象ではありません。さて...
メソッドはプライベートなので、続行する唯一の方法は、コードを調べて、非プライベートAPIを介してそれがどのように使用されるかを学ぶことです。通常、このような調査では、ギャップの理由は、特定の使用シナリオがテストに欠落していることであることが明らかになっています。
_ void nonPrivateMethod(boolean condition) {
if (condition) {
privateMethod();
}
// other code...
}
// unit tests don't invoke nonPrivateMethod(true)
// => privateMethod isn't covered.
_
完全を期すために、このようなカバレッジギャップのその他の(頻度が少ない)理由は、仕様/設計のバグである可能性があります。ここでは、物事を簡単にするために、これらについては詳しく説明しません。 「メソッドをテスト可能にするためだけに」アクセス制限を弱めると、これらのバグが存在することを知る機会を逃すことになります。
ギャップを修正するために、不足しているシナリオの単体テストを追加し、カバレッジ分析を繰り返し、ギャップがなくなったことを確認します。私は今何を持っていますか?非プライベートAPIの特定の使用法の新しい単体テストを取得しました。
新しいテストでは、変更するとテストが失敗するため、この使用法の予想される動作が予告なく変更されないことを確認します。
外部の読者がこのテストを調べて、 learn がどのように使用され、どのように動作するかを想定します(ここで、外部の読者は私の将来の自己を含みます。それで終わりました)。
新しいテストはリファクタリングに耐性があります(プライベートメソッドをリファクタリングしますか?間違いありません!)privateMethod
に何をしても、常にnonPrivateMethod(true)
をテストしたいと思います。 privateMethod
をどのように処理しても、メソッドは直接呼び出されないため、テストを変更する必要はありません。
悪くない?あなたは賭けます。
上記の代わりに、アクセス制限を単に弱めることを想像してみてください。メソッドを使用するコードの調査をスキップし、exPrivateMethod
を呼び出すテストに進みます。すごい?ない!
上記の非公開APIの特定の使用法のテストを取得できますか?いいえ:以前はnonPrivateMethod(true)
のテストはありませんでした。現在、そのようなテストはありません。
外部の読者は、クラスの使い方をよりよく理解する機会を得ますか?いいえ "-ここでテストされたメソッドの目的は何ですか?-忘れてください、それは厳密に内部使用のためです。-おっと。"
リファクタリングに耐性がありますか?まさか、私がexPrivateMethod
で変更したものは何でも、テストに失敗するでしょう。名前を変更し、他のメソッドにマージし、引数を変更してテストすると、コンパイルが停止します。頭痛?賭けます!
まとめ、プライベートメソッドを使用することで、ユニットテストの有用で信頼性の高い拡張機能が得られます。対照的に、「テスト容易性のために」アクセス制限を弱めると、あいまいで理解しにくいテストコードのみが得られます。率直に言って、私が得たものは疑わしい 技術的負債 のように見えます。
</ rant>
最も可能性の高い理由は、歴史に関係しています。 Javaの祖先であるオークには、プライベート、保護、パブリックの3つのアクセスレベルしかありませんでした。
オークのプライベートがJavaのプライベートパッケージに相当することを除いて。 オークの言語仕様 のセクション4.10を読むことができます(私の強調):
デフォルトでは、クラス内のすべての変数とメソッド(コンストラクターを含む)はプライベートです。プライベート変数とメソッドには、クラスで宣言されたメソッドのみがアクセスでき、そのサブクラスやその他のクラス(を除く)はアクセスできません(同じパッケージ内のクラスを除く)。
つまり、Javaで知られているプライベートアクセスは、もともとそこにはありませんでした。しかし、パッケージにいくつかのクラスがある場合、私たちが知っているようにプライベートでないと、名前の衝突という悪夢につながる可能性があります(たとえば、Java.until.concurrentには60に近いクラスがあります)。それを紹介しました。
ただし、デフォルトのパッケージアクセス(当初はプライベートと呼ばれていました)セマンティクスは、OakとJavaの間で変更されていません。
デフォルトのアクセスが適切に使用されていれば、カプセル化を維持しながらテスト性が向上すると私は信じています。また、プライベートアクセス修飾子が冗長になります。
すべてをpublicで公開するよりも緊密にバインドされた2つの個別のクラスが必要になる場合があります。これには、C++の「友達」と同様の考え方があり、別の「友達」として宣言された別のクラスがプライベートメンバーにアクセスできます。
BigInteger などのクラスの内部を調べると、フィールドとメソッド(リスト内の青い三角形のすべて)に多数のパッケージのデフォルト保護が見つかります。これにより、Java.mathパッケージ内の他のクラスが特定の操作のために内部へのアクセスをより最適化できるようになります(これらのメソッドは BigDecimal で呼び出されます-BigDecimalはBigIntegerによってサポートされ、BigIntegerの再実装を保存しますagain。Java.mathは封印されたパッケージであり、他のクラスを追加できないため、これらがパッケージレベルであることは保護の問題ではありません。
一方で、本来あるべきプライベートなものがたくさんあります。ほとんどのパッケージは密封されていません。プライベートがなければ、同僚はそのパッケージに別のクラスを配置し、(もはや)プライベートフィールドにアクセスしてカプセル化を解除できます。
注目に値するのは、単体テストは、保護スコープが構築されているときに考えられたものではありませんでした。 JUnit(JavaのXUnitテストフレームワーク)は、1997年まで考え出されていませんでした(その話は、 http://www.martinfowler.com/bliki/Xunit.html で読むことができます) )。 JDKのアルファバージョンとベータバージョンは1995年で、JDK 1.0は1996年でしたが、保護レベルはJDK 1.0.2(実際にはprivate protected
保護レベル)。
デフォルトはパッケージレベルではなくプライベートである必要があると主張する人もいます。他の人はdefault保護をすべきではないと主張しますが、すべてを明示的に述べる必要があります-これの一部のフォロワーは次のようなコードを書きます:
public class Foo {
/* package */ int bar = 42;
...
}
そこのコメントに注意してください。
パッケージレベルの保護がデフォルトである本当の理由は、Javaの設計ノートでは失われている可能性があります(私は掘り下げており、見つけることができません)なぜ記事、違いを説明する多くの人が、「これが理由だ」と言っている人はいません。
だから私は推測します。そして、それだけです-推測。
まず、人々はまだ言語設計を理解しようとしていました。それ以来、言語はJavaの過ちと成功から学んできました。これは、allフィールドに定義する必要のあるものがないため、間違いとしてリストされる可能性があります。
public
とprivate
の明示的なステートメントを必要としていました。これらはアクセスに最も大きな影響を与えるからです。したがって、privateはデフォルトではなく、それ以外はすべて適切に機能します。デフォルトのスコープはデフォルトとして残されました。 mayは、作成されたfirstスコープであり、古いコードとの厳密な下位互換性はそのまま残されていました。
私が言ったように、これらはすべて推測です。
カプセル化は、「これについて考える必要がない」という言い方です。
Access Levels
Modifier Class Package Subclass World
public Y Y Y Y
protected Y Y Y N
no modifier Y Y N N
private Y N N N
これらを非技術的な用語に入れます。
当時はテストがあまり一般的でなかったからといって、テストに重点を置いているとは思いません。
彼らが達成したいと思っていたのは、パッケージレベルでのカプセル化でした。ご存知のように、クラスには内部メソッドとフィールドがあり、それらの一部のみをパブリックメンバーを通じて公開します。同様に、パッケージは内部クラス、メソッド、フィールドを持つことができ、一部のみを公開します。このように考えると、パッケージには、クラス、メソッド、およびフィールドのセットの内部実装と、クラス、メソッド、およびフィールドの別の(部分的に重複している可能性がある)セットのパブリックインターフェイスがあります。
私が知っている最も経験豊富なプログラマーはこのように考えています。それらはカプセル化を受け取り、それをクラスよりも高い抽象化レベルに適用します:パッケージ、または必要に応じてコンポーネント。 Javaがいかにうまく機能するかを考えると、Javaの設計者はすでにこの成熟した設計でした。