メソッドパラメータではなくコンストラクタにデータを渡すと、クラスの概念はどのように変わりますか?


12

パーサーを作成しているとしましょう。1つの実装は次のようになります。

public sealed class Parser1
{
    public string Parse(string text)
    {
       ...
    }
}

または、代わりにテキストをコンストラクタに渡すこともできます。

public sealed class Parser2
{
    public Parser2(string text)
    {
       this.text = text;
    }

    public string Parse()
    {
       ...
    }
}

どちらの場合も使用法は簡単ですが、他のものと比較して、へのパラメーター入力を有効にするとはどういう意味Parser1ですか?他のプログラマーがAPIを見たときに、どのメッセージを送信しましたか?また、特定の場合に技術的な利点/欠点はありますか?

2番目の実装ではインターフェイスがまったく意味がないことに気づくと、別の質問が出てきます。

public interface IParser
{
    string Parse();
}

...最初のインターフェイスが少なくともいくつかの目的に役立つ場合。これは特に、クラスが「インターフェース可能」かどうかを示していますか?


これは、OOのセマンティクスが実際に意図をどのように表現しているかについて、役立つQ&Aを目にしたのはこれが初めてです。今までのところ、それはすべて私にとって構文上の砂糖でした。

回答:


11

意味的に言えば、OOPでは、クラスの構築に必要なパラメーターのセットのみをコンストラクターに渡す必要があります。同様に、メソッドを呼び出す場合は、ビジネスロジックを実行するために必要なパラメーターのみを渡す必要があります。

コンストラクターで渡す必要があるパラメーターは、適切なデフォルト値を持たないパラメーターであり、クラスが不変(または実際にstruct)の場合は、デフォルト以外のすべてのプロパティを渡す必要があります。

あなたの2つの例について:

  • textコンストラクタに渡すと、Parser2後でテキストのインスタンスを解析するためにクラスが特別に構築されることを示しています。特定のパーサーになります。これは通常、クラスの構築が非常に高価または微妙な場合に当てはまります。RegExはコンストラクターでコンパイルされる可能性があるため、インスタンスを保持すると、コンパイルのコストを支払うことなくインスタンスを再利用できます。別の例としては、PRNGの初期化があります-まれにしか行われないほうがよいでしょう。
  • textメソッドに渡すと、Parser1呼び出しによってさまざまなテキストを解析するために再利用できることを通知します。

3
Parser1が状態を維持するという弱い信号があることを追加します。つまり、特定のテキスト文字列は、以前にtatインスタンスで実行されたものに応じて異なる結果を生成する可能性があります。これは必ずしもそうとは限りませんが、そうかもしれません。
jmoreno 2013年

8

さて、コンストラクタパラメータとして変数を渡すことの意味を思い出してみましょう。オブジェクトのメソッドでインスタンス変数を使用するために、オブジェクトを初期化します。重要なのは、クラスで高い結束力を得たいため、おそらく複数のメソッドで使用したいということです。

メソッドに直接パラメーターを渡すことは、オブジェクトにメッセージを送信し、おそらく回答を受け取ることを意味します。それによって、クライアントはオブジェクトにサービスを提供することを望みます。

結論として、これらはパラメータを渡す2つの非常に異なる手段であり、オブジェクトがサービスを提供するか、一部の情報を内部的に管理しながら、本質的にいくつかの機能を提供するかを選択する必要があります。


+1。凝集度は、それがメソッドとコンストラクタのどちらに属しているかを決定する方法です。
jhewlett 2013年

4

どちらの場合も使用法は簡単ですが、Parser1へのパラメーター入力を有効にすると、他と比べてどういう意味ですか?

その根本的なデザインの変化。そしてデザインは意図と意味を伝えるべきです。解析したい文字列ごとに個別のオブジェクトが必要ですか?言い換えると、なぜstringXを持つパーサーのインスタンスとstringYを持つ別のインスタンスが必要なのでしょうか。解析と指定された文字列について、2人が一緒に生きて死ぬ必要があることは何ですか?(基礎となる[解析]実装)(Robert Harveyが言うように)が変化しないと仮定すると、意味がないようです。そして、それでもその疑わしい私見。

メソッドパラメータではなくコンストラクタにデータを渡すと、クラスの概念はどのように変わりますか?

コンストラクタパラメータは、オブジェクトにこれらのものが必要であることを教えてくれます。それらがなければ適切な状態は保証されません。また、あるパーサーが他のパーサーとどのように/なぜ根本的に異なるのかを知っています。

コンストラクタパラメータを使用すると、クラスの使用方法についてあまり知らなくて済みます。代わりに特定のプロパティを設定することになっている場合、どうすればそれを確認できますか?ワームの缶全体が開きます。どんな特性?どんな順番で?どの方法を使用する前に?等々。

2番目の実装ではインターフェイスがまったく意味がないことに気づくと、別の質問が出てきます。

APIと同様に、インターフェイスはクライアントコードに公開されるメソッドとプロパティです。public interface { ... }独占で包まれないでください。したがって、インターフェイスの意味は、どちらかまたはコンストラクタvsメソッドパラメータのジレンマであり、NOT public interface Iparservspublic sealed class Parser

sealedクラスは奇数です。異なるパーサーの実装について考えている場合-「Iparser」について言及していました-継承が私の最初の考えです。それは私の考えの自然な概念の拡張です。IE all ParserXは基本的にParsersです。他にどのように言うか...ドイツのシェパードは犬(継承)ですが、オウムを吠えさせる(犬のように振る舞う-「インターフェース」)ように訓練することができます。しかし、ポリーは犬ではなく、単にふりをして、犬らしさのサブセットを学びました。抽象またはその他のクラスは、インターフェースとして完全に機能します


それがパーサーのように歩き、パーサーのように話すなら、それは...アヒルです!

2

クラスの2番目のバージョンは不変にすることができます。

インターフェースを使用して、基礎となる実装をスワップアウトする機能を提供できます。


関数内でクラス内でデータを渡すことにより、最初のバージョンでも不変にできませんか?
ciscoheat 2013年

2
もちろんです。ただし、不変性の一般的なパターンは、コンストラクターを使用してクラスメンバーを設定し、読み取り専用のプロパティを持つことです。関数型プログラミングの場合、クラスは必要ありません。
Robert Harvey 2013年

ScyllaとCharybdis:OOまたは不変データを選択しますか?それが前にそのように置かれるのを聞いたことがありません。

2

パーサー1

デフォルトのコンストラクターでビルドし、入力テキストをメソッドに渡すことは、Parser1が再利用可能であることを意味します。

パーサー2

入力テキストをコンストラクターに渡すということは、入力ストリングごとに新しいParser2を作成する必要があることを意味します。


もちろん、次のレベルに進む場合、再利用できないオブジェクトと比較して、再利用可能なオブジェクトについて何を結論付ければよいでしょうか。
ciscoheat 2013年

私はそれ以上は想定せず、代わりにドキュメンテーションに延期します。
Mike Partridge 2013年

IDが変更される「再利用可能なオブジェクト」は、不連続ではありません。また、管理されたフレームワークを使用すると、物を捨てたり、上書きしたり、スコープから外したりする必要さえありません。離れて構築!
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.