工場としての基本クラス?


14

私は週末にいくつかのコードを書いていましたが、ファクトリを基本クラスの静的メソッドとして書きたいと思いました。

私の質問は、これが独創的なアプローチであるかどうかを知ることですか?

基本クラスが派生クラスの知識を持っているという事実からではないかもしれないという私の感覚です。

そうは言っても、同じ結果を得るためのもっと簡単な方法はわかりません。他のファクトリクラス全体は(少なくとも私には)不要な複雑さ(?)

何かのようなもの:

class Animal
{
  public static Animal CreateAnimal(string name)
  {
     switch(name)
     {
        case "Shark":
          return new SeaAnimal();
          break;
        case "Dog":
          return new LandAnimal();
          break;
        default:
          throw new Exception("unknown animal");
     }
  }
}
class LandAnimal : Animal
{
}

class SeaAnimal : Animal
{
}

工場をどのようにテストしますか?

この質問のコードでは、私はしません。しかし、その答えは、より多くの私のコーディングスタイルにテストの統合の道に沿ってインチ私を助けている
アーロンAnodide

アニマルクラスでシーワールドとランドワールドの両方を引き込んだことを考えると、孤立した環境での取り扱いがすべて難しくなります。

回答:


13

さて、別のファクトリクラスの利点は、単体テストでモックアウトできることです。

ただし、それを行わない場合、または他の方法でポリモーフィックにする場合は、クラス自体の静的Factoryメソッドでかまいません。


おかげで、他の方法でポリモーフィックと言うとき、あなたが言及しているものの例を挙げていただけますか?
アーロンアノディード

あるファクトリーメソッドを別のファクトリーメソッドに置き換えたい場合。テスト目的でそれをあざけることは、その最も一般的な例の1つにすぎません。
-pdr

18

Genericsを使用してswitchステートメントを回避し、ダウンストリーム実装を基本クラスから切り離すこともできます。

 public static T CreateAnimal<T>() where T: new, Animal
 {
    return new T();
 }

使用法:

LandAnimal rabbit = Animal.CreateAnimal();  //Type inference should should just figure out the T by the return indicated.

または

 var rabbit = Animal.CreateAnimal<LandAnimal>(); 


2
@PeterK。FowlerはCode Smellsのswitchステートメントを参照しますが、彼の解決策は、それらを置き換えるのではなく、Factoryクラスに抽出することです。とはいえ、開発時に常に型を知るつもりであれば、ジェネリックはさらに優れたソリューションです。しかし、もしあなたがそうしないなら(元の例が暗示しているように)、リフレクションを使用してファクトリーメソッドの切り替えを回避することは誤った経済になり得ると主張します。
-pdr

switchステートメントとif-else-ifチェーンは同じです。デフォルトのケースを処理するように強制するコンパイル時のチェックのため、前者を使用します
アーロンアノディード

4
@ PeterK、switchステートメントでは、コードは1か所にあります。本格的なオブジェクト指向では、同じコードが複数の別々のクラスに広がっている場合があります。灰色の領域があり、多数のスニペットを追加する複雑さがスイッチ文よりも望ましくありません

@Thorbjorn、私はその正確な考えを持っていたしましたが、決してそう簡潔に、言葉に置くためのおかげで
アーロンAnodide

5

極端に言えば、工場も一般的である可能性があります。

interface IFactory<K, T> where K : IComparable
{
    T Create(K key);
}

次に、任意のタイプのオブジェクトのファクトリを作成できます。これにより、任意のタイプのオブジェクトを作成できます。それが単純かどうかはわかりませんが、確かに一般的です。

小さなファクトリー実装のswitchステートメントに問題はありません。多数のオブジェクトまたはオブジェクトの可能な異なるクラス階層に入れば、より一般的なアプローチの方が適していると思います。


1

悪いアイデア。第一に、それはオープンクローズの原則に違反しています。新しい動物については、基本クラスを再度混乱させる必要があり、潜在的にそれを破るでしょう。依存関係は間違った方向に進みます。

構成から動物を作成する必要がある場合は、このような構造体で問題ありませんが、リフレクションを使用して名前に一致する型を取得し、取得した型情報を使用してインスタンス化することをお勧めします。

とにかく、動物のクラス階層から解放された専用のファクトリクラスを作成し、ベースタイプではなくIAnimalインターフェイスを返す必要があります。その後、それは有用になります、あなたはいくつかの分離を達成したでしょう。


0

この質問は私にとってタイムリーです-昨日、ほぼ正確にそのようなコードを書いていました。ここでの議論のために「動物」に固執しますが、「動物」を私のプロジェクトに関連するものに置き換えてください。「switch」ステートメントの代わりに、1つの変数を特定の固定値と比較するだけでなく、より複雑な一連の「if」ステートメントがありました。しかし、それは詳細です。静的なファクトリメソッドは、設計が以前の迅速で汚れたコードのリファクタリングから生まれたため、物事を設計するためのきちんとした方法のように見えました。

派生クラスの知識がある基本クラスを理由に、この設計を拒否しました。LandAnimalクラスとSeaAnimalクラスが小さく、きれいで、簡単な場合、同じソースファイルに含めることができます。しかし、公式に定義された標準に準拠していないテキストファイルを読み取るための大きな厄介な方法があります。LandAnimalクラスを独自のソースファイルに含める必要があります。

循環ファイルの依存関係につながります-LandAnimalはAnimalから派生していますが、AnimalはLandAnimal、SeaAnimal、および15のその他のクラスが存在することを既に知っている必要があります。ファクトリーメソッドを引き出して、独自のファイルに配置しました(動物ライブラリではなく、メインアプリケーションに)静的なファクトリーメソッドは、かわいくて賢いように見えましたが、実際には設計上の問題は解決しないことに気付きました。

私は言語を頻繁に切り替えるため、これが慣用的なC#にどのように関連するのかわかりません。通常、私の通常の仕事以外の言語や開発スタックに特有のイディオムや慣習は無視します。意味があれば、私のC#が「pythonic」に見えるかもしれません。私は一般的な明快さを目指しています。

また、将来の作業で拡張される可能性が低い、小さく単純なクラスの場合に静的ファクトリーメソッドを使用する利点があるかどうかはわかりません。すべてを1つのソースファイルに含めることは、場合によっては便利かもしれません。

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