web-dev-qa-db-ja.com

クラスが独自のパブリックメソッドを使用しても問題ありませんか?

背景

現在、デバイスによって送信および受信されるobjectがある状況にあります。このメッセージには、次のようないくつかの構成要素があります。

public void ReverseData()
public void ScheduleTransmission()

ScheduleTransmissionメソッドは、呼び出されるたびにReverseDataメソッドを呼び出す必要があります。ただし、ReverseDataを外部から呼び出す必要がある場合があります(オブジェクトの場所から名前空間の外側にを追加する必要があります)。アプリケーションでインスタンス化されます。

「受信」については、データを元に戻すために、ReverseDataobject_receivedイベントハンドラーで外部から呼び出されることを意味します。

質問

オブジェクトが独自のパブリックメソッドを呼び出すことは、一般的に許容されますか?

24
Snoop

これは受け入れられるだけでなく、拡張機能を許可する場合は特に推奨されます。 C#でクラスの拡張をサポートするには、以下のコメントに従って、メソッドに仮想フラグを設定する必要があります。ただし、ReverseData()をオーバーライドしてScheduleTransmission()の動作を変更したときに誰かが驚かないように、これを文書化することをお勧めします。

本当にそれはクラスのデザインにかかっています。 ReverseData()は、クラスの基本的な動作のように聞こえます。他の場所でこの動作を使用する必要がある場合は、おそらく他のバージョンの動作を望まないでしょう。 ScheduleTransmission()に固有の詳細がReverseData()にリークしないように注意する必要があります。それは問題を引き起こします。しかし、あなたはすでにこれをクラスの外で使用しているので、おそらくすでにそれを熟考しているでしょう。

33
JimmyJames

もちろんです。

メソッドの可視性は、クラス外または子クラス内のメソッドへのアクセスを許可または拒否することのみを目的としています。 public、protected、privateメソッドは常にクラス自体の内部で呼び出すことができます。

パブリックメソッドの呼び出しに問題はありません。あなたの質問のイラストは、2つのパブリックメソッドを持つことがまさにあなたがすべきことである状況の完璧な例です。

ただし、次のパターンには注意してください。

  1. メソッドHello()は、次のようにWorld(string, int, int)を呼び出します。

    _Hello()
    {
        this.World("Some magic value here", 0, 100);
    }
    _

    このパターンは避けてください。代わりに、 オプションの引数 を使用します。オプションの引数を使用すると、発見しやすくなります。_Hello(_と入力した呼び出し元は、デフォルト値でメソッドを呼び出すことができるメソッドがあることを必ずしも認識しません。オプションの引数も自己文書化されています。 World()は、実際のデフォルト値が何であるかを呼び出し元に示しません。

  2. メソッドHello(ComplexEntity)は、次のようにWorld(string, int, int)を呼び出します。

    _Hello(ComplexEntity entity)
    {
        this.World(entity.Name, entity.Start, entity.Finish);
    }
    _

    代わりに、 overloads を使用してください。同じ理由:発見しやすさ。呼び出し元は、IntelliSenseを介してすべてのオーバーロードを一度に確認し、適切なものを選択できます。

  3. メソッドは、実質的な値を追加せずに、他のパブリックメソッドを呼び出すだけです。

    考え直してください。この方法が本当に必要ですか?それとも削除して、呼び出し元にさまざまなメソッドを呼び出させますか?ヒント:メソッドの名前が正しくない、または見つけるのが難しい場合は、おそらく削除する必要があります。

  4. メソッドは入力を検証してから、他のメソッドを呼び出します。

    _Hello(string name, int start, int end)
    {
        if (name == null) throw new ArgumentNullException(...);
        if (start < 0) throw new OutOfRangeException(...);
        ...
        if (end < start) throw new ArgumentException(...);
    
        this.World(name, start, end);
    }
    _

    代わりに、Worldはパラメーター自体を検証するか、プライベートにする必要があります。

20

何かが公開されている場合、いつでもどのシステムからも呼び出される可能性があります。あなたもそれらのシステムの1つになることができない理由はありません!

高度に最適化されたライブラリでは、Java.util.ArrayList#ensureCapacity(int)に記述されているイディオムをコピーする必要があります。

ensureCapacity

  • ensureCapacityは公開されています
  • ensureCapacityには、必要なすべての境界チェックやデフォルト値などがあります。
  • ensureCapacity呼び出しensureExplicitCapacity

ensureCapacityInternal

  • ensureCapacityInternalは非公開です
  • ensureCapacityInternalは、すべての入力がクラスの内部から取得されるため、最小限のエラーチェックしかありません。
  • ensureCapacityInternal ALSOはensureExplicitCapacityを呼び出します

ensureExplicitCapacity

  • ensureExplicitCapacityもプライベートです
  • ensureExplicitCapacityにはnoエラーチェックがあります
  • ensureExplicitCapacityは実際の作業を行います
  • ensureExplicitCapacityは、ensureCapacityensureCapacityInternal以外ではどこからも呼び出されません

このようにして、信頼できるコードは、その入力が適切であることがわかっているため、特権(より高速)のアクセスを取得します。信頼できないコードは、その完全性を検証して攻撃したり、デフォルトを提供したり、その他の方法で不正な入力を処理したりするために、特定の厳密さを通過します。どちらも実際に機能する場所に誘導します。

ただし、これは、JDKで最もよく使用されるクラスの1つであるArrayListで使用されます。あなたのケースがそのレベルの複雑さと厳密さを必要としない可能性は非常に高いです。要するに、すべてのensureCapacityInternal呼び出しがensureCapacity呼び出しに置き換えられたとしても、パフォーマンスは本当に非常に優れています。これは、おそらく十分に検討した後にのみ行われるマイクロ最適化です。

9
corsiKa

いくつかの良い答えはすでに与えられており、オブジェクトにも他のメソッドからそのパブリックメソッドを呼び出すことができるということにも同意します。ただし、注意が必要な設計上の注意点が若干あります。

パブリックメソッドには、通常、「オブジェクトを一貫した状態にして、賢明なことを行い、オブジェクトを(場合によっては異なる)一貫した状態にしておく」という規約があります。ここで「一貫性がある」とは、たとえば、List<T>LengthがそのCapacity以下であり、0からLength-1までのインデックスの要素を参照してもスローされないことを意味します。 。

ただし、オブジェクトのメソッド内では、オブジェクトが不整合な状態にある可能性があるため、パブリックメソッドの1つを呼び出すと、そのような可能性を考慮して記述されていないため、非常に間違った動作をする可能性があります。したがって、他のメソッドからパブリックメソッドを呼び出す予定がある場合は、それらのコントラクトが「オブジェクトをある状態で取り、何か賢明なことを行い、オブジェクトを(おそらく異なる)のままにします(一貫性がない可能性があります。ただし、最初の状態は一貫していませんでした)状態」。

3
Joker_vD

別のパブリックメソッド内でパブリックメソッドを呼び出す場合のもう1つの例は、CanExecute/Executeアプローチです。 検証と不変式の保存 の両方が必要なときに使用します。

しかし、一般的に私は常にそれについて慎重です。メソッドa()がメソッドb()内で呼び出された場合、それはメソッドa()b()の実装の詳細であることを意味します。多くの場合、それらは異なる抽象化レベルに属していることを示しています。そして、両方とも公開されているという事実は、それが 単一責任の原則 違反かどうか疑問に思います。

1
Zapadlo