Javaがパッケージアクセスをデフォルトにしたのはなぜですか?


26

私がこの質問をしているのは、彼らが非常に正当な理由でそれをし、ほとんどの人がそれを適切に使用していないと信じているからです。しかし、私の理論が本当なら、なぜプライベートアクセス修飾子が含まれているのか分かりません...?

デフォルトのアクセスが適切に使用されると、カプセル化を維持しながら、テスト容易性が向上すると考えています。また、プライベートアクセス修飾子を冗長にします。

デフォルトのアクセス修飾子は、他の世界から隠される必要があるメソッドに固有のパッケージを使用することで同じ効果を提供するために使用できます。テストフォルダー内のパッケージは、ソースフォルダーで宣言されているすべてのデフォルトメソッドにアクセスできます。

これが、Javaがパッケージアクセスを「デフォルト」として使用する理由だと思います。しかし、なぜプライベートアクセスも含まれているのか分かりません、有効なユースケースがあると確信しています...



以前に説明したプライベートメソッドのプライベートメソッドに関するもの。-行う方法-あなたのユニット・テスト・プライベート・メソッド。回答; してはいけません
リチャードティングル

回答:


25

彼らは平均的なプログラマが何をするかについて良い考えを持っていたと思います。そして、平均的なプログラマーとは、プログラミングがあまり得意ではないが、良いものが十分になく、高価であるため、とにかく仕事を得る人を意味します。

彼らが「パブリック」アクセスをデフォルトのものにした場合、ほとんどのプログラマーは他のものを使用することを決して気にしません。結果は、至る所に大量のスパゲッティコードになります。(技術的にどこからでも何かを呼び出すことを許可されている場合、なぜメソッド内にいくつかのロジックをカプセル化する必要があるのですか?)

「プライベート」をデフォルトのアクセスにすると、ほんの少しだけ良くなります。ほとんどの初心者プログラマーは、単に「どこでも「パブリック」を書く必要がある」という経験則を単純に考案し(そしてブログで共有し)、Javaがどこでも「パブリック」を書くことを余儀なくする理由を不満に思うでしょう。また、多くのスパゲッティコードも生成されます。

パッケージへのアクセスは、プログラマーが1つのパッケージ内でコードを記述している間に、ずさんなプログラミングテクニックを使用できるようにするものですが、その後、パッケージを作成するときにそれを再検討する必要があります。それは良いビジネス慣行とpracticesい現実の間の妥協です。たとえば、スパゲッティコードを書きます。主張する場合は、パッケージ内にifい混乱を残してください。少なくともパッケージ間でより良いインターフェースを作成します。

おそらく他の理由もありますが、私はこれを過小評価しません。


14

デフォルトアクセスでは、プライベートアクセス修飾子は冗長になりません。

言語デザイナーの立場は、公式チュートリアル- クラスのメンバーへのアクセスの制御に反映されており、わかりやすくなっています(便宜上、引用符内の関連する文は太字になっています)。

アクセスレベルの選択に関するヒント:

他のプログラマーがクラスを使用する場合、誤用によるエラーが発生しないようにする必要があります。アクセスレベルは、これを行うのに役立ちます。

  • 特定のメンバーにとって意味のある最も制限の厳しいアクセスレベルを使用します。正当な理由がない限り、プライベートを使用してください。
  • 定数以外のパブリックフィールドは避けてください。(チュートリアルの例の多くはパブリックフィールドを使用しています。これは、いくつかのポイントを簡潔に説明するのに役立ちますが、本番コードにはお勧めできません。)

TDDの新機能の回答などで証明されているように、プライベート修飾子を完全に削除する正当化としてのテスト容易性へのアピールは間違っています。今私はプライベートメソッドを避けるべきですか?

もちろん、プライベートメソッドを持つこともできますし、もちろんテストすることもできます。

どちらかがあり、いくつかのあなたがそのようにそれをテストすることができ、または存在しない、その場合には、実行にプライベートメソッドを取得する方法、を実行するプライベート取得する方法が、その場合には:なぜ一体あなただけ、それをテストしようとしていますいまいましいものを削除...


パッケージレベルのアクセスの目的と使用に関する言語デザイナーの位置は、別の公式チュートリアル「パッケージの作成と使用」で説明されており、プライベート修飾子を削除するという考えとは共通点はありません(便宜上、引用符内の関連する文は太字になっています) :

次のようないくつかの理由で、これらのクラスとインターフェースをパッケージにバンドルする必要があります。

  • あなたと他のプログラマは、これらのタイプが関連していることを簡単に判断できます...
  • パッケージ内の型が相互に無制限にアクセスできるようにしながら、パッケージ外の型のアクセスを制限することができます ...

<大声で「十分な泣き声を聞いたと思う。声を出してはっきりと言う時間だと思う...」>

プライベートメソッドは、単体テストに役立ちます。

以下の注は、コードカバレッジに精通していることを前提としています。そうでない場合は、時間をかけて学習してください。ユニットテストとテストに興味がある人には非常に役立つからです。

よし、プライベートメソッドとユニットテスト、およびギャップがあることを教えてくれるカバレッジ分析があり、プライベートメソッドはテストでカバーされていない。今...

非公開にすることで何が得られますか

メソッドはプライベートであるため、続行する唯一の方法は、非プライベートAPIを介してコードがどのように使用されるかを調べるためにコードを調べることです。通常、このような調査により、ギャップの理由はテストで特定の使用シナリオが欠落していることが明らかになります。

    void nonPrivateMethod(boolean condition) {
        if (condition) {
            privateMethod();
        }
        // other code...
    }

    // unit tests don't invoke nonPrivateMethod(true)
    //   => privateMethod isn't covered.

完全を期すために、このようなカバレッジギャップの他の(あまり頻繁ではない)理由は、仕様/設計のバグである可能性があります。物事をシンプルにするために、ここでは深く掘り下げません。「メソッドをテスト可能にするためだけに」アクセス制限を弱めると、これらのバグがまったく存在しないことを知る機会を逃してしまうと言えば十分です。

ギャップを修正するために、不足しているシナリオの単体テストを追加し、カバレッジ分析を繰り返して、ギャップがなくなったことを確認します。私は今何を持っていますか?非プライベートAPIの特定の使用法について、新しい単体テストとして入手しました。

  1. 新しいテストでは、この使用法の予想される動作が予告なく変更されないことが保証されています。変更された場合、テストは失敗します。

  2. 外部の読者は、このテストを調べて、それがどのように使用および動作するのかを学習する場合があります(ここでは、外部の読者がコードを忘れてしまいます。

  3. 新しいテストは私がやるものは何でも(私はあなたが賭ける?プライベートメソッドをリファクタリングでください!)リファクタリングに耐性があるprivateMethod、私はいつもテストしたいと思うでしょうnonPrivateMethod(true)privateMethodメソッドが何であっても、メソッドは直接呼び出されないため、テストを変更する必要はありません。

悪くない?あなたは賭けます。

アクセス制限を弱めることで何を失うのか

ここで、上記の代わりに、アクセス制限を単純に弱めることを想像してください。メソッドを使用するコードの調査をスキップし、myを呼び出すテストに直接進みますexPrivateMethod。すばらしいです?違います!

  1. 上記の非プライベートAPIの特定の使用法についてテストを受けますか?いいえ:nonPrivateMethod(true)以前のテストはありませんでした。現在、そのようなテストはありません。

  2. 外部の読者は、クラスの使用法をよりよく理解する機会を得ますか?いいえ。「-ここでテストしたメソッドの目的は何ですか?-忘れてください、厳密には内部使用のためです。-おっと。」

  3. リファクタリングは許容されますか?方法はありません:私が変更するものは何でもexPrivateMethod、テストを破るでしょう。名前を変更し、他のメソッドにマージし、引数を変更すると、テストはコンパイルを停止します。頭痛?あなたは賭けます!

要約すると、プライベートメソッドに固執することで、ユニットテストの有用で信頼性の高い機能強化がもたらされます。対照的に、「テスト容易性のために」アクセス制限を弱めると、あいまいで理解しにくいテストコードのみが得られます。これは、小さなリファクタリングによって破損するという永続的なリスクがあります。率直に言って、私が得るものは、技術的な負債のように疑わしく見えます。

</ rant>


7
プライベートメソッドをテストするための説得力のある議論を見つけたことがありません。クラスのパブリックAPIとは無関係の理由で、常にコードをメソッドにリファクタリングします。他のコードを使用せずに、これらのメソッドを独立しテストできるのは非常に良いことです。それがあなたがリファクタリングした理由ですよね?独立した機能を取得します。その機能も独立してテスト可能でなければなりません。
ロバートハーベイ

@RobertHarveyの回答は、これに対処する暴言で拡大しました。「プライベートメソッドはユニットテストに有益です...」
gnat

プライベートメソッドとコードカバレッジについてあなたが言っていることはわかりますが、それらをテストできるようにそれらのメソッドをパブリックにすることを実際に推奨していませんでした。 私は、それらがプライベートであっても、それらを独立してテストする方法を持っていることを主張していました。 必要に応じて、プライベートメソッドに触れる公開テストを引き続き行うことができます。そして、私はプライベートテストを受けることができます。
ロバートハーベイ

@RobertHarveyなるほど。コーディングスタイルに要約されると思います。私が通常作成するプライベートメソッドの量と、これらをリファクタリングするレートでは、それらのテストを行うことは、私が買う余裕のない贅沢です。非プライベートAPIは、私はそれを修正することで、非常に消極的と遅くなる傾向にある別の問題です
ブヨ

おそらく、あなたは私よりも初めて動作するメソッドを書くのが上手でしょう。私にとっては、先に進む前にメソッドが最初に機能することを確認するためにメソッドをテストする必要があり、他の一連のメソッドを起動して間接的にテストするのは不器用で厄介です。
ロバートハーベイ

9

最も可能性の高い理由は、それが歴史に関係しているということです。Javaの祖先であるオークには、プライベート、保護、パブリックの3つのアクセスレベルしかありませんでした。

ただし、OakのprivateはJavaのprivateパッケージと同等でした。Oakの言語仕様のセクション4.10 (エンファシスマイニング)を読むことができます。

デフォルトでは、クラス内のすべての変数とメソッド(コンストラクターを含む)はプライベートです。プライベート変数とメソッドには、クラスで宣言されたメソッドのみがアクセスでき、サブクラスや他のクラス(同じパッケージ内のクラスを除く)はアクセスできません。

つまり、Javaで知られているプラ​​イベートアクセスは元々存在していませんでした。しかし、パッケージ内にいくつかのクラスがある場合、私たちが知っているようにプライベートを持たないと、名前の衝突の悪夢につながります(たとえば、java.until.concurrentには60クラス近くあります)、おそらくそれらが理由ですそれを紹介しました。

ただし、デフォルトのパッケージアクセス(元々プライベートと呼ばれていました)のセマンティクスは、OakとJavaの間で変更されていません。


5

デフォルトのアクセスが適切に使用されると、カプセル化を維持しながら、テスト容易性が向上すると考えています。また、プライベートアクセス修飾子を冗長にします。

すべてをパブリックで公開するよりも緊密にバインドされた2つの個別のクラスが必要な場合があります。これには、C ++の「友人」という同様の考えがあり、別のクラスの「友人」として宣言されている別のクラスがプライベートメンバーにアクセスできます。

BigIntegerなどのクラスの内部を調べると、フィールドとメソッド(リスト内の青い三角形のすべて)にいくつかのパッケージのデフォルト保護があります。これは、あなたが呼ばれるこれらの方法を見つけることができます(持つjava.mathパッケージ内の他のクラスは、特定の操作のために彼らの内臓へのより最適なアクセスを持つことができますのBigDecimalを - BigDecimalのはBigIntegerのに裏打ちされたとのBigIntegerを再実装節約され、再びこれらは、パッケージレベルのISN」であることを。 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保護レベルを持つ前に)に実際には固定されませんでした。

デフォルトはパッケージレベルではなく、プライベートであるべきだと主張する人もいます。他の人は、デフォルトの保護はすべきではないと主張しますが、すべてを明示的に述べる必要があります。

public class Foo {
    /* package */ int bar = 42;
    ...
}

そこのコメントに注意してください。

パッケージレベルの保護がデフォルトである本当の理由は、おそらくJavaのデザインノートに失われます(私は掘り下げてきたので記事、多くの人が違いを説明しているが、「これが理由である")。

だから私は推測します。そして、それだけです-推測。

最初に、人々はまだ言語設計を理解しようとしていました。それ以来、言語はJavaの間違いと成功から学んできました。これは、すべてのフィールドに定義する必要があるものがないという間違いとしてリストされる可能性があります。

  • 設計者は、C ++の「友人」関係がときどき必要になることに気付きました。
  • 設計者はpublic、に対する明示的なステートメントを望んでおりprivate、これらはアクセスに最大の影響を及ぼします。

したがって、privateはデフォルトではなく、他のすべてが適切に配置されました。デフォルトのスコープはデフォルトのままです。それは可能となっている最初に作成されたスコープと古いコードと厳格な後方互換性の利益のために、そのように残っていました。

私が言ったように、これらはすべて推測です


ああ、もし友人機能だけがメンバーに個別に適用できたら、void someFunction()はクラスXでしか見ることができないと言うことができます...それは本当にカプセル化を強化するでしょう!
newlogic

C ++でこれができるのを待ってください、Javaでこれが必要です!
newlogic

2
@ user1037729は、2つのクラス間のより緊密な結合を実現します-メソッドを持つクラスは、そのフレンドである他のクラスを知る必要があります。これは、Javaが提供する設計哲学に反する傾向があります。友人と解けるですが、レベルの保護をパッケージ化していない問題のセットがないという大。

2

カプセル化は、「これについて考える必要はありません」と言う方法です。

                 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

これらを非技術用語に入れます。

  1. 公開:誰もがそれについて考える必要があります。
  2. 保護:デフォルトおよびサブクラスはそれについて知る必要があります。
  3. デフォルトでは、パッケージで作業している場合、それについて知ることができます(目の前にあります)。
  4. プライベート。このクラスで作業している場合にのみ、これについて知る必要があります。

私は...プライベートクラスまたはprotectedクラスのようなものがあるとは思わない
Koray Tugay

@KorayTugay:docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.htmlをご覧ください。内部クラスはプライベートです。最後の段落を読んでください。
-jmoreno

0

テストが当時あまり一般的ではなかったという理由だけで、彼らはテストに焦点を合わせたとは思わない。

彼らが達成したいと思ったのは、パッケージレベルでのカプセル化でした。ご存知のように、クラスは内部メソッドとフィールドを持ち、パブリックメンバーを通じてのみ公開します。同様に、パッケージは内部クラス、メソッド、およびフィールドを持ち、その一部のみを公開できます。このように考えると、パッケージには、クラス、メソッド、およびフィールドのセットに内部実装があり、クラス、メソッド、およびフィールドの別の(おそらく部分的に重複する)セットにあるパブリックインターフェイスがあります。

私が知っている最も経験豊富なコーダーは、このように考えています。それらはカプセル化を取得し、それをクラスよりも高い抽象化レベル(パッケージ、または必要に応じてコンポーネント)に適用します。Javaの設計者は、Javaがどれだけうまく機能しているかを考慮して、すでに設計の成熟度が高いと私には思えます。


自動テストはそれほど一般的ではなかったというのは正しいことです。しかし、それは未熟な設計です。
トムホーティン-タックライン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.