4

パブリックアクセス修飾子とプライベートアクセス修飾子がある理由を理解できます。これら2つは、ほとんどすべての言語で使用されています。クラス(緊密に連携しているクラス)を、パブリックインタラクションには適さない方法で相互作用させたい場合があるため、パッケージ修飾子が存在する理由も理解できます(たとえば、クラスの内部、おそらくそれが秘密を明らかにするため、またはそれがいつでも変更される可能性があり、それに依存すると既存のすべてのコードが壊れるためなど)。しかし、なぜ保護された識別子が必要なのですか?誤解しないでください、私は何が保護されているか知っていますつまり、クラスのサブクラスがサブクラスであり、別のパッケージの一部であっても、特定のインスタンス変数にアクセスしたり、特定のメソッドを使用したりする必要があるのはなぜですか?保護された実際のユースケースは何ですか?

(JITコンパイラーは常にアクセサーメソッドをインライン化できるため、インスタンス変数の引数としてのパフォーマンスは考慮されません。これにより、呼び出しのオーバーヘッドがゼロになります)

4

7 に答える 7

15

パブリックメソッドは、パブリックインターフェイスの一部です。プライベートメソッドは内部メソッドです。保護されたメソッドは拡張ポイントです。

このprotectedメソッドをパブリックインターフェイスの一部にすることなく、クラスをオーバーライドすることで、クラスの機能を再定義できます。

もう1つ、保護されたメソッドは、サブクラスで再利用できる一般的なメソッドですが、パブリックインターフェイスの一部である必要はありません。

たとえば、Javaコレクションフレームワークには、AbstractListクラスがあります。保護されたmodCountフィールドと保護されたremoveRangeメソッドがあります。

  • このmodCountフィールドは、変更の数をカウントするためにすべてのサブクラスによって使用(インクリメント)されます。Iteratorによって返されるは、そのAbstractListフィールドを利用します

  • メソッドは、removeRangeサブクラスで再定義する代わりに、サブクラスで再利用できます。

API設計に関するJoshBlochによるこの関連プレゼンテーションを参照してください。

コメントやBlochによるプレゼンテーションに記載されているように、クラスを適切に文書化します。そして、それが継承を目的としている場合は、さらに努力してください。

于 2010-11-25T18:09:44.363 に答える
3

私が目にする最も頻繁な使用法は、実際にはスーパークラスにサブクラスの内部を使用させることです。このことを考慮:

class Foo
{
    private int[] array = new int[] { 4, 3, 2, 1 };

    public void processAllElements()
    {
        for (int i = 0; i < array.length; i++)
            processElement(array[i]);
    }

    protected abstract void processElement(int i);
}

class Bar
{
    protected void processElement(int element)
    {
        System.out.println(element);
    }
}

この場合、Fooの保護された要素を使用する必要がありBar、その逆ではありません。スーパークラスがサブクラスのロジックにアクセスできるようにしたいが、それを公開したくない場合は、protected修飾子以外に選択肢はありません。これはテンプレートメソッドパターンと呼ばれ、よく使用されます。(実際の例を提供していないことをお詫びします。必要に応じてウィキペディアにアクセスしてください。

于 2010-11-25T18:14:11.277 に答える
0

あなたが可能なエクステンダーの心を完全に知らないとき、私はそれらを近道として見ます。基本クラスに5つまたは6つの属性があるとします。あなたは確かにそれらの属性(またはそれらのsetメソッド)を公開したくありません。ただし、エクステンダーは、これらの属性の値を変更する関数を記述したい場合があることがわかります。したがって、それら(またはより良いのはそれらのセット)を保護します。エクステンダーには問題ないかもしれないが、古い消費コードだけには問題がない設計への依存は常に存在します。

そうは言っても、私は生徒たちに「保護されているのはやることリストです」と言います。保護されているものを変更する場合は、誰がそれに依存しているかを探しに行く必要があるためです。したがって、将来の未知のエクステンダーに公開する前に、何かについて本当に確認してください。

于 2010-11-25T18:14:03.887 に答える
0

アクセス修飾子を持たない言語が存在するという事実、およびすべてのメソッドが公開されprotectedているアクセス修飾子をまったく持たない言語でさえそれらの言語は一般に「最も純粋な」オブジェクト指向言語の一部と見なされていることを示しています。実際、アクセス修飾子は「本当に必要」ではありません。protected

于 2010-11-25T19:50:50.687 に答える
0

パブリック継承可能なクラスのパブリックメンバーは、すべての行儀の良いサブクラスを拘束する全世界との契約を構成します。保護されたメンバーは、それを公開しているクラスにのみ拘束力を持つ、直接のサブクラスとの契約を構成します。サブクラスは、保護されたメンバーを自分のサブクラスに公開する義務を負いません(実際、そのような公開を拒否するための規則と、特定の保護されたメンバーがデフォルトで直接のサブクラスにのみ利用可能であることを指定する手段が必要です。サブクラスは、サブクラスでも使用できるようにする必要があることを指定します)。

一部の人々は、クラスがその親によって公開された保護されたメンバーへのアクセスを許可しないという考えに摩擦するかもしれませんが、そのような振る舞いは決してリスコフの置換原則に違反していません。LSPは、コードが基本型オブジェクトを予期しているときに派生型オブジェクトを受け取る可能性がある場合、派生型オブジェクトは基本型オブジェクトが実行できるすべてのことを実行できる必要があると述べています。したがって、Moe機能を公にサポートし、Larryから派生するタイプがサポートされMoeていない場合、タイプのパラメーターを受け入れてMoeその機能を使用しようとするコードは、Larry;が指定されていると失敗します。そのような失敗はLSPの違反を構成します。

ただし、サポートされていない機能がMoe含まれているとします。その機能の使用が許可される唯一のクラスは、から直接派生するクラス、またはそれをサポートする子孫のいずれかです。から派生した場合、すべてのサブタイプがそれをサポートするかどうかを心配することなく、その機能を使用できます。これは、のベースが、のインスタンスまたは派生物である任意のオブジェクトではないためです(機能をサポートする場合としない場合があります)。 -期間になります。protectedLarryMoeMoeCurlyMoeMoeCurlyMoeMoe

派生型がそれらを使用するベースのパブリックメンバーを破壊する方法でそれらのフィールドを使用する場合、保護されたフィールドがいくつかのLSP関連の問題を引き起こす可能性があります。一方、そのために保護された変数を使用する必要はありません。サブタイプが基本タイプの予想される動作とは異なる方法で仮想メンバーを実装する場合、基本クラスの保護されたメンバーに触れなくても、基本タイプのパブリックメンバーが破損する可能性があります。

于 2012-11-02T20:21:29.607 に答える
0

私は、a)パブリックコントラクトのノイズを排除し、b)派生クラスと機能を共有しながら、c)DRYであるコードを記述し、単一責任の原則に留意したい場合に、保護された修飾子を個人的に利用します。典型的なように聞こえますが、例を挙げましょう。

ここに、基本的なクエリハンドラインターフェイスがあります。

public interface IQueryHandler<TCommand, TEntity>
{
    IEnumerable<TEntity> Execute(TCommand command);
}

このインターフェイスは、アプリケーションのさまざまなクエリハンドラーによって実装されます。その後、多くの異なるクエリハンドラからのクエリ結果をキャッシュする必要があるとしましょう。ただし、これは実際には単なる実装の詳細です。具体的なクエリハンドラクラスの利用者は、一般的に言って、それについて心配していません。次に、私の解決策は、キャッシングを処理するが、実際のクエリの責任を派生クラスに委ねる実装を作成することです。

public abstract class CachedQueryHandler<TCommand, TEntity> 
    : IQueryHandler<TCommand, TEntity>
{
    public IEnumerable<TEntity> Execute(TCommand command)
    {
        IEnumerable<TEntity> resultSet = this.CacheManager
            .GetCachedResults<TEntity>(command);

        if (resultSet != null)
            return resultSet;

        resultSet = this.ExecuteCore(command);
        this.CacheManager.SaveResultSet(command, resultSet);

        return resultSet;
    }

    protected abstract IEnumerable<TEntity> ExecuteCore(TCommand command);
}

CachedQueryHandlerは、他の誰かがExecuteCoreメソッドを直接呼び出すことを意図していません。ただし、クエリがどのように実装されているかも関係ありません。保護された修飾子は、このようなシナリオに最適です。

また、すべてのクエリハンドラーで同じボイラープレートタイプのコードを繰り返したくありません。特に、キャッシュマネージャーのインターフェイスが変更された場合にリファクタリングするのは悪夢であり、これでキャッシュが完全に削除された場合に削除するのは非常に困難です。レベル。

具体的なウィジェットクエリハンドラは次のようになります。

public class DatabaseWidgetQueryHandler : CachedQueryHandler<WidgetCommand, Widget>
{
    protected override IEnumerable<Widget> ExecuteCore(WidgetCommand command)
    {
        return this.GetWidgetsFromDatabase();
    }
}

これで、ウィジェットクエリハンドラーのコンシューマーがクエリハンドラーインターフェイスを介してそれを利用する場合、依存性注入を利用すれば確実に強制できますが、CachedQueryProviderクラスに追加されたキャッシュに固有のものは使用されません。その後、必要に応じてキャッシングを追加/削除したり、最小限の労力でキャッシングの実装を完全に変更したりできます。

IQueryHandler<WidgetCommand, Widget> widgetQueryHandler;
var widgets = widgetQueryHandler.Execute(myWidgetCommand);
于 2015-11-18T21:21:58.850 に答える
-1

おそらく、子供が結婚して別の国に住んでいる場合でも、子供と共有する情報/プロパティがいくつかあります。protectedところで、修飾子がない言語があります 。

于 2010-11-25T18:10:45.570 に答える