オブジェクトを作成するためにコンストラクターの代わりにファクトリを使用するためのしきい値は何ですか?
- あなたはいつも工場を使います。
- ヌルのチェック以外の不変チェックがある場合にのみ、ファクトリを使用します。
- 常にコンストラクターを使用します
- あなたはめったに工場を使いません...それらの場合は何ですか?
長所と短所
更新:プロジェクトにドメイン駆動設計のファクトリパターンを適用しています。また、ファクトリを作成する理由の1つは、ドメインモデルのノイズを減らすことです。
ありがとう
オブジェクトを作成するためにコンストラクターの代わりにファクトリを使用するためのしきい値は何ですか?
長所と短所
更新:プロジェクトにドメイン駆動設計のファクトリパターンを適用しています。また、ファクトリを作成する理由の1つは、ドメインモデルのノイズを減らすことです。
ありがとう
抽象基本クラス(またはインターフェイス)といくつかの具象派生クラスがある場合はファクトリを使用し、具象クラスの1つを作成するためのロジックがいくつかあります。私はそのロジックを工場で実装しています。
ファクトリの最も明白なケースは、インターフェイスを実装する特定のクラスが実行時に、たとえば構成ファイルから選択される場合です。私はファクトリを多用しませんが、2 つのオブジェクトを高度に分離したい場合は、一方のオブジェクトのインスタンスを取得するためにファクトリを使用する可能性が高くなります。
このトピックに関連する C# の興味深い点は、クラス定義で指定されたジェネリック型に対する new() 制約により、ジェネリック コンテナー型によって処理される型がパラメーターなしのコンストラクターを実装するように強制されることです。GenericType<T>
new() 制約は、 のようにクラス内でT 型のインスタンスを作成する場合にのみ必要です。これは、クラス ファクトリ、特にジェネリック型を生成するファクトリを明示的にサポートしているように思えます。
この要件を完全に覆すために、Windows Communication Foundation (WCF) には、次の静的ファクトリ メソッドを定義する ChannelFactory クラスがあります。
public static TChannel CreateChannel(Binding binding, EndpointAddress endpointAddress, Uri via)
{
ChannelFactory<TChannel> factory = new ChannelFactory<TChannel>(binding);
if (factory.HasDuplexOperations())
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("SFxInvalidStaticOverloadCalledForDuplexChannelFactory1", new object[] { factory.channelType.Name })));
}
TChannel channel = factory.CreateChannel(endpointAddress, via);
ChannelFactory<TChannel>.SetFactoryToAutoClose(channel);
return channel;
}
クラスの逆アセンブリ (System.ServiceModel アセンブリと System.ServiceModel.Channels 名前空間) で Reflector を見ると、"new()" が制約として使用されていないことがわかります。
これは、CreateChannel メソッドが typeof(TChannel) を使用してオブジェクトの作成をチェーンのさらに下に委任するためです...
public virtual TChannel CreateChannel(EndpointAddress address, Uri via)
{
TChannel local;
bool traceOpenAndClose = base.TraceOpenAndClose;
try
{
using (ServiceModelActivity activity = (DiagnosticUtility.ShouldUseActivity && base.TraceOpenAndClose) ? ServiceModelActivity.CreateBoundedActivity() : null)
{
if (DiagnosticUtility.ShouldUseActivity)
{
ServiceModelActivity.Start(activity, this.OpenActivityName, this.OpenActivityType);
base.TraceOpenAndClose = false;
}
if (address == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address");
}
if (base.HasDuplexOperations())
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("SFxCreateNonDuplexChannel1", new object[] { base.Endpoint.Contract.Name })));
}
base.EnsureOpened();
local = (TChannel) this.ServiceChannelFactory.CreateChannel(typeof(TChannel), address, via);
}
}
finally
{
base.TraceOpenAndClose = traceOpenAndClose;
}
return local;
}
次のメソッドが最終的に呼び出されるまで、 Type クラスが渡されているため、さらにいくつかのレベルの委任チェーンをたどることができます。
RemotingServices.CreateTransparentProxy(this, classToProxy, stub, stubData);
非常に複雑ですが、私が今まで見た中で最も複雑な工場です。興味深いことに、すべての策略は最終的に、WCF が System.Runtime.Remoting.Proxies 名前空間から RealProxy クラスを作成することになります。
結論として、ファクトリは、非常に複雑なオブジェクト、または動的な型構築の恩恵を受ける必要があるオブジェクト向けです。
しきい値をどのように選択したか正確にはわかりません...
ファクトリは、オブジェクトの消費者を構造から抽象化したくない場合に適しています。これが関係する可能性がある例:
このパターンをいつ使用するかについての詳細は、Book of Four (Gamma et. al.) のパターン ブックを参照し、ファクトリ パターンを詳しく調べてください。
ここに急進的な考えがあります (私はそれを支持しているわけではありませんが、それが害になるとは思いません):
常にファクトリーメソッドを使用してください!
ファクトリ メソッドはより柔軟です。たとえば、結果をキャッシュしたり、子クラスを返したりできます。
したがって、代わりに:
class SomeClass {
public SomeClass(/*parameters*/) { /*...*/ }
}
常に使用:
class SomeClass {
protected SomeClass(/*parameters*/) { /*...*/ }
public static SomeClass New(/*parameters*/) {
return new SomeClass(/*parameters*/);
}
}
呼び出し元コードは次のように変更されます。
SomeClass sc = new SomeClass();
に:
SomeClass sc = SomeClass.New();
「コンストラクター」ロジックを変更して、サブクラスまたはキャッシュされたインスタンスを返すことができるようになりました。すべての呼び出し元は影響を受けません。「コンストラクタ」の戻り値を制御できるようになりました。
私は、コンストラクターの数を妥当な低レベルに維持したいと考えています。2 つまたは 3 つよりも多く、オブジェクトの構造がどれだけうまく設計されているか疑問に思います。
さまざまなオプション属性の設定をサポートするために追加のコンストラクターが導入されている場合、 Effective Java (Joshua Bloch、第 2 版) で説明されているように、 Builderを使用するのが好きです。
BuilderパターンとFactoryパターンを混同していると思います。コンストラクターを使用して、それで済ませることをお勧めします。(コードを見ずに)コードを少し考えすぎているか、分析しすぎているようです。
私はそれらの間を測定しようとします。次の場合はファクトリを使用する必要があると思います。
この場合、ファクトリを使用すると、返されるオブジェクトの状態に適切な名前を付けることができます。
インスタンス化する具象クラスの決定がクライアントに委ねられていない場合は、ファクトリを使用します。たとえば、オブジェクトの「ファミリー」がいくつかあり、どのファミリーを使用するかの選択は別の場所で行われます。