Javaパッケージレベルのスコープは役に立ちますか?


11

私はパッケージスコープの考え方を理解しています。しかし、真剣に使用しようとするたびに、それが役立つと思っていたニーズに合わないことに気づきました。

私の主な問題は常に、私がスコープを制限したいものは決して同じパッケージに入っていないということです。それらは概念的にはすべてリンクされている可能性がありますが、アプリケーション内のデータの論理的な分割では、それらをより大きなパッケージの個別の子パッケージとして扱います。

たとえば、Missionモデルを使用していて、mymissionServicesなどの他のMissionツールのみにいくつかのメソッドを使用させたい場合があります。ただし、最終的にはパッケージとしてMissions.modelsとMissions.servicesが使用されるため、MissionModelとMissionServiceは同じパッケージスコープではありません。昇格されたアクセス許可が必要なものが、パッケージに適切に含まれているにもかかわらず、それらのアクセス許可が不要な多くのものが含まれていないような状況は決してありません。また、メソッドをスコープするパッケージの利点が、すべてを同じパッケージに配置するようにプロジェクトアーキテクチャを変更することを正当化することはほとんどありません。多くの場合、アスペクトまたは何らかの制御の逆転のどちらかが、パッケージのスコープを簡単に検討した問題に対するより優れたアプローチであることが判明します。

私は好奇心が強いのですが、これは一般的にすべてのJava開発者に当てはまると考えられているか、私が行っている作業のほんの一部にすぎません。パッケージスコープは現実世界で多く利用されていますか?それが使用するのに適した形態と考えられる多くのケースがありますか、それとも現代の開発でめったに利用されないレガシーな振る舞いと見なされていますか?

パッケージのプライベートスコープがデフォルトである理由については何も尋ねていません。デフォルトに関係なくいつ使用する必要があるかを尋ねています。なぜそれがデフォルトであるかについてのほとんどの議論は、パッケージスコープが実際に役立つ場合には実際には入りません。代わりに、一般的に使用される他の2つのスコープがデフォルトではないはずであるという理由だけを主張して、除去プロセスによってパッケージが勝つようにします。また、現在の開発状況についてお伺いします。具体的には、他のツールやパラダイムによってパッケージのスコープの有用性が低下するところまで開発したので、それをデフォルトにするという決定が理にかなっていたときのことです。


考えた実験:パッケージがない場合、Javaプログラムはどのように見えますか?大規模な Javaプログラムをまだ見ていないか、作業していない可能性があります。
Robert Harvey

コードを見てみましょうjava.util.Stringjava.util.Integer


2
重複する質問に対するトップ投票の回答は、package-privateの最も重要な使用法をカバーし、それがなぜ有用であるかを暗黙的に回答します。

2
私はその複製にまったく確信が持てません。この質問は、「パッケージのスコープは何ですか?」他の人が(とりわけ)与えられたパッケージスコープがデフォルトであると尋ねている間、プライベートスコープは何ですか?それに加えて、だまされたときの受け入れられた回答とここで受け入れられた回答は、まったく異なる点を示しています。
Ixrec

回答:


2

java.util.*パッケージ全体で、パッケージレベルの保護を使用してコードが記述される場合があります。例えば、このビットjava.util.String- ジャワ6コンストラクタ

// Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

またはこのgetCharsメソッド

/** Copy characters from this string into dst starting at dstBegin. 
    This method doesn't perform any range checking. */
void getChars(char dst[], int dstBegin) {
    System.arraycopy(value, offset, dst, dstBegin, count);
}

この理由は、コードの設計者(およびjava.util.*かなり大きなライブラリと考えることができます)は、さまざまな安全性(配列の範囲チェック、他のフィールドへの直接アクセス)の損失を犠牲にしてより高速なパフォーマンスを実現する機能を望んでいたためですそれは、インターフェースではなく実装を意味します。そうでなければ、パブリックメソッドを介して不変であると見なされるクラスの部分への「安全でない」アクセスです)。

これらのメソッドとフィールドを、java.utilパッケージ内の他のクラスのみがアクセスできるように制限することにより、これらのメソッドとフィールド間の密接な結合が可能になりますが、これらの実装の詳細が公開されることもありません。

アプリケーションで作業していて、他のユーザーについて心配する必要がない場合、パッケージレベルの保護は心配する必要はありません。デザインの純粋さのさまざまな側面を除いて、あなたはすべてpublicを作り、それで大丈夫かもしれません。

ただし、他のユーザーが使用するライブラリで作業している場合(または、後でリファクタリングするためにクラスが複雑になりすぎないようにしたい場合)、必要に応じて提供されている最も厳しいレベルの保護を使用する必要があります。多くの場合、これはを意味しprivateます。ただし、場合によっては、同じパッケージ内の他のクラスに実装の詳細を少し公開して、繰り返しを避けたり、2つのクラスとその実装の結合を犠牲にして実際の実装を少し簡単にする必要があります。したがって、パッケージレベルの保護。


1
私はjava.utilを調べましたが、それは確かです。しかし、私はそれが大きなライブラリであることにも気づきました。今日では、ライブラリがサブパッケージを使用せずに作成されることはほとんどないようです。制限につながるのはサブパッケージの分割です。私はjava.utilが間違っていると言っているのではありませんが、パッケージ内のカプセル化がかなり制限されていて、すべてを正しく実行できると信頼できる非常に優れたプログラマーがいるだけで、大きなパッケージを回避できるようです。私と同じようなパッケージで作業する可能性のある平均的な開発者に悪いことをする可能性が非常に高いと裸で信じていると思います。
dsollen 2015

1
@dsollen SpringやHibernateなどの大きなライブラリを掘り下げると、同様のものが見つかります。より密接な結合が必要な場合があります。一つは、必要があるコードレビューする機会に応募があり、すべてが正しいことを確認してください。より密接な結合が必要ない場合、または他のコーダーをまったく信頼しない場合は、パブリックフィールド、パッケージまたは保護されたフィールドまたはメソッドが常に適切であるとは限りません。ただし、それが役に立たないという意味ではありません。

要するに、私はより制限されたパッケージを作りたくて、日常のニーズに対する最適化のレベル(95%のプロジェクトでは無駄)を気にしないようにしたいと思います。したがって、私のフォローアップの質問は、パッケージレベルのスコープが、広範囲に使用される特定の大規模なAPIでのみ使用される非常に具体的な最適化の可能性になるのでしょうか。IEだけがその程度まで保護を緩和する必要があるか、または望むでしょうか?小規模なユーザーベースで「標準」のAPIに取り組んでいる優れたプログラマがこれに使用できる理由はありますか?
dsollen 2015

フォローアップの投稿が十分な速さで届かなかった:)私はあなたの主張を理解して見ました。私はそれについてまったく議論していません。もっと良いプログラマーにとって有用であるというフォローアップの質問を投げるだけですが、そのような巨大なAPIで作業することはできません。具体的には、それはほとんどあなたが本当に場合にのみ行われるカプセル化のトレードオフを犠牲にして最適化され、実際にパフォーマンスを微調整する必要があります。
dsollen 2015

2
@dsollenそれはパフォーマンスではなく(それが実装を公開する理由の1つです)、むしろカプセル化です。あなたはそれがために、コードの繰り返しを避けるために行うInteger.toString()と、String.valueOf(int)世界にそれを公開することなく、コードを共有することができます。java.utilクラスではなく、完全に自分自身わたしの島である個々のクラスであることよりも、機能性のより大きな全体を提供するために、コンサートで動作するようになっています-彼らは、パッケージレベルのスコープを介したパッケージと共有実装の機能で一緒にされています。

5

プライベートまたは保護されたメソッドの可視性をパッケージにエスカレートして、junit-testが外部からアクセスしてはならない実装の詳細にアクセスできるようにすることがあります。

junit-testはテスト対象のアイテムと同じパッケージを持っているので、これらの実装の詳細にアクセスできます

  public class MyClass {
    public MyClass() {
        this(new MyDependency());
    }

    /* package level to allow junit-tests to insert mock/fake implementations */ 
    MyClass(IDependency someDepenency) {...}
  }

これが「良い」使用法かどうかは議論の余地がありますが、実用的です


2
私はいつもテストを別のパッケージに入れます。そうでなければ、デフォルトのスコープにいくつかのキーメソッドを残しているため、他の誰もそれを呼び出せません...
soru

私はメソッドでそれを行うことはありませんが、注入された依存関係に使用するのに優れた機能です。たとえば、EJBをテストする場合、パッケージレベルのEMをMockオブジェクトに設定して、実行時にアプリケーションサーバーによって挿入されるEntityManagerをモックすることができます。
2015

なぜprotectedパッケージスコープに昇格するのですか? protected はすでにパッケージアクセスを提供しています。少し奇妙な奇妙なことですprotected packagescope void doSomething()が、(新しいキーワードも必要とする)などの複合スコープ宣言を要求したくなかったと思います。
tgm1024--モニカは

2

それがサブパッケージであるかのようにパッケージのドット名を使用する傾向がある世界では、特にデフォルトとして、それはそれほど有用ではありません。Javaにはサブパッケージなどがないため、xyinternalsは、abにアクセスする場合よりもxyにアクセスできます。パッケージ名は同じでも異なっていても、部分一致は共通点がないことと変わりません。

元の開発者はその規則が使用されることを決して期待しておらず、Adaスタイルの階層型パッケージの複雑さを追加せずに物事をシンプルにしたかったと思います。

Java 9はこれにある程度対処しているため、モジュールに複数のパッケージを配置して、一部のみをエクスポートできます。ただし、これにより、パッケージのスコープがさらに冗長になります。

理想的な世界では、デフォルトのスコープを「包含モジュールが存在する場合はモジュールスコープに、それ以外の場合はパッケージスコープ」に変更します。その後、それを使用してモジュール内の実装を共有し、エクスポートされたインターフェースを壊すことなくリファクタリングを行うことができます。しかし、下位互換性を損なう、または他の理由で悪いことになる微妙な点があるかもしれません。


Java 9のコメントは興味深いものです。パッケージヘラクシーを認識し、何らかの方法で(注釈を使用して)親パッケージに対してパッケージスコープを定義する簡単な機能がいいと思います。
dsollen 2015

1
@dsollenかもしれませんが、私の最初の傾向は、タイヤアイアンに金メッキを施すことです。私の意見では、明快さ(ある程度の安全性をもたらす)へのはるかに良い最初のステップは、実際のパッケージスコープキーワードを発明することです。「パッケージ」かもしれません:)
tgm1024--モニカは2017年

2

説明する結束タイプは、実際にパッケージアクセスを使用する最良の例ではありません。パッケージは、インターフェース上で交換可能なコンポーネント、モジュールを作成するのに便利です。

これは大まかなシナリオの例です。コードが、機能の凝集とコンポーネント化に重点を置いて再編成されたとします。おそらく次のような3つのjarファイル:

**MissionManager.jar** - an application which uses the Java Service Provider Interface to load some implementation of missions, possible provided by a 3rd party vendor.

**missions-api.jar** - All interfaces, for services and model
    com...missions
        MissionBuilder.java   - has a createMission method
        Mission.java - interface for a mission domain object
    com...missions.strategy
        ... interfaces that attach strategies to missions ...
    com...missions.reports
        .... interfaces todo with mission reports ....

   **MCo-missionizer-5000.jar** -  provides MCo's Missionizer 5000 brand mission implementation.
       com...missions.mco
                  Mission5000.java - implements the Mission interface.
       com...missions.strategy.mco
              ... strategy stuff etc ...

Mission5000.javaでパッケージレベルを使用することで、他のパッケージやMissionManagerなどのコンポーネントがミッションの実装に密結合できないことを確認できます。これは、「M co」の「サードパーティ」提供コンポーネントのみが実際に独自のブランドのミッションの実装を作成するものです。Mission ManagerはMissionBuiler.createMission(...)を呼び出し、定義されたインターフェースを使用する必要があります。

このように、Mission実装コンポーネントが置き換えられた場合、MissionManager.jarのインプリメンターがAPIをバイパスせずにMCoの実装を直接使用したことがわかります。

したがって、MCo-missionizer5000.jarを競合製品と交換できます。(または、ミッションマネージャーのユニットテスト用のモック)

逆に、MCoは独自の宣教師の企業秘密をある程度隠すことができます。

(これは、コンポーネントの不安定性と抽象化のアイデアの適用にも関連しています。)


-1

Javaのパッケージスコープは階層的な方法で設計されていないため、ほとんど役に立ちません。パッケージスコープはまったく使用せず、すべてのクラスをパブリックとして定義します。

代わりに、パッケージ階層と事前定義されたパッケージ名に基づく独自のアクセスルールを使用します。

「CODERU-Rules」の詳細グーグルに興味があるなら。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.