ファクトリークラスは知りすぎている
-
22-08-2019 - |
質問
更新しました 私の問題をよりわかりやすく説明するために例を更新しました。私は、ある特定の点が欠けていることに気づきました。 CreateLabel()
メソッドは常にラベル タイプを受け取るため、ファクトリは作成するラベルのタイプを決定できます。つまり、返したいラベルの種類に応じて、多かれ少なかれ情報を取得する必要がある可能性があります。
プリンターに送信するラベルを表すオブジェクトを返すファクトリ クラスがあります。
ファクトリ クラスは次のようになります。
public class LargeLabel : ILabel
{
public string TrackingReference { get; private set; }
public LargeLabel(string trackingReference)
{
TrackingReference = trackingReference;
}
}
public class SmallLabel : ILabel
{
public string TrackingReference { get; private set; }
public SmallLabel(string trackingReference)
{
TrackingReference = trackingReference;
}
}
public class LabelFactory
{
public ILabel CreateLabel(LabelType labelType, string trackingReference)
{
switch (labelType)
{
case LabelType.Small:
return new SmallLabel(trackingReference);
case LabelType.Large:
return new LargeLabel(trackingReference);
}
}
}
CustomLabel という新しいラベル タイプを作成するとします。これを工場から返却したいのですが、追加のデータが必要です。
public class CustomLabel : ILabel
{
public string TrackingReference { get; private set; }
public string CustomText { get; private set; }
public CustomLabel(string trackingReference, string customText)
{
TrackingReference = trackingReference;
CustomText = customText;
}
}
これは、ファクトリ メソッドを変更する必要があることを意味します。
public class LabelFactory
{
public ILabel CreateLabel(LabelType labelType, string trackingReference, string customText)
{
switch (labelType)
{
case LabelType.Small:
return new SmallLabel(trackingReference);
case LabelType.Large:
return new LargeLabel(trackingReference);
case LabelType.Custom:
return new CustomLabel(trackingReference, customText);
}
}
}
ファクトリは最小公倍数に対応する必要があるが、同時に CustomLabel クラスにも対応する必要があるため、これは好きではありません。 ニーズ カスタム テキスト値を取得します。追加のファクトリ メソッドをオーバーライドとして提供することもできますが、CustomLabel に値が必要であるという事実を強制したいと考えています。そうでない場合は、空の文字列のみが与えられます。
このシナリオを実装する正しい方法は何ですか?
解決
さて、あなたはどのようにの呼び出しのファクトリメソッドにしたいですか?
あなたのAPIを使用することができるようにしたいどのように集中し、実装は通常、それ自体はかなり明確になります。あなたはユニットテストとして、あなたのAPIの所望の結果を書き込む場合、これがさらに容易になります。
過負荷がうまくここで行うには正しいことかもしれないが、それは本当にあなたが工場を使用する方法によって異なります。
他のヒント
どのようにして、ちょうどあなたが必要なもののラベルを決定するためにファクトリメソッドを使用していますか?
public class LabelFactory {
public ILabel CreateLabel(string trackingReference, string customText) {
return new CustomLabel(trackingReference, customText);
}
public ILabel CreateLabel(String trackingReference) {
return new BasicLabel(trackingReference);
}
}
(インターフェースでは、動的ロード機能を実装することができます)あなたの工場は、まだそれぞれのタイプについて知っておく必要がありますが、クライアントが知る必要があることはほとんどあり - どのようなデータに基づいて提供され、工場は正しい実装を生成します<。 / P>
これは、あなたが説明した簡単な問題に単純な解決策です。私は質問は、より複雑な問題の単純化であると仮定しますが、あなたの本当の問題が何であるかを知らなくても、私はむしろ複雑なソリューションの上に設計しないと思います。
これはおそらく、工場出荷時のパターンは、あなたのための最良のではないことを示しています。あなたがいずれかを実行必要があるか、それに固執したい場合は、しかし、私は文字列ではなく、工場に渡すことができ、初期化クラス/構造体を作成することをお勧め。あなたは、基本的な情報クラスの様々なサブクラス(基本的にはあなたのラベルクラスのことを模倣する初期クラス階層を作成する)でそれをやりたいか、すべての情報が含まれている一つのクラスは、あなた次第です。
かどうかあなたは、コンフィギュレーション・クラスを使用して工場出荷時にそのインスタンスを渡すようにしてください。設定クラスは、特別な設定クラスを使用すると、工場から期待結果ごとに存在するであろう階層を構築します。各設定クラスには、工場出荷時の結果の特定のプロパティをキャプチャします。
私はBasicLabelConfigurationおよびそれから派生CustomLabelConfigurationを書きたい与えてくれた例。 CustomLabelConfigurationは、カスタムテキストを取り込みながらBasicLabelConfigurationは、追跡参照をキャプチャします。
最後に工場が渡された構成オブジェクトの種類に基づいて決定を行います。
<時間>ここでは、コードの例です。
public class BasicLabelConfiguration
{
public BasicLabelConfiguration()
{
}
public string TrackingReference { get; set; }
}
public class CustomLabelConfiguration : BasicLabelConfiguration
{
public CustomLabelConfiguration()
{
}
public string CustomText { get; set; }
}
public class LabelFactory
{
public ILabel CreateLabel(BasicLabelConfiguration configuration)
{
// Possibly make decision from configuration
CustomLabelConfiguration clc = configuration as CustomLabelConfiguration;
if (clc != null)
{
return new CustomLabel(clc.TrackingReference, clc.CustomText);
}
else
{
return new BasicLabel(configuration.TrackingReference);
}
}
}
最後に、あなたはこのように工場を使用すると思います:
// Create basic label
ILabel label = factory.CreateLabel(new BasicLabelConfiguration
{
TrackingReference = "the reference"
});
または
// Create basic label
ILabel label = factory.CreateLabel(new CustomLabelConfiguration
{
TrackingReference = "the reference",
CustomText = "The custom text"
});
さらなる情報がなければ、どんな助言を与えることはかなり難しいですが、工場出荷時のパターンは、あなたが実際にあなたが次のアプローチを試みることが必要なものであると仮定します:
プロパティマップ(文字列に文字列の例えばマップ)のいくつかの種類に必要な引数を梱包して渡すこと工場のcreateメソッドに引数として。専門の工場は、自分の好みに合わせてマッピングされた値を抽出し、解釈することができ、マップ内のキーとしてよく知られているタグを使用します。
これは、少なくともあなたが当面のための単一のファクトリインタフェースを維持することができ、かつ場合(または時)アーキテクチャの問題に対処延期するあなたは、工場出荷時のパターンは、ここで正しいものではないことがわかります。
(ああ、あなたが本当にここファクトリパターンを使用したい場合、私は強くあなたはそれがプラグイン可能なそれぞれの新しいラベルタイプのために工場を変更することを避けるために行うことをお勧め)。
あなたはそれが収まらないというシナリオにパターンを強制しようとしています。私は、その特定のパターンをあきらめる示唆し、できるだけ簡単な解決策を作るのではなく、焦点を当てるます。
私はこのケースでは、私はちょうど空/通常nullですが、ラベルはカスタムにする必要がある場合は、1つを設定できるカスタムテキスト用のテキストフィールドを持っている一つのクラス、ラベルを持っていると思います。それは、自己説明シンプルで、メンテナンスのプログラマに任意の悪夢を与えることはありません。
public class Label
{
public Label(string trackingReference) : this(trackingReference, string.Empty)
{
}
public Label(string trackingReference, string customText)
{
CustomText = customText;
}
public string CustomText ( get; private set; }
public bool IsCustom
{
get
{
return !string.IsNullOrEmpty(CustomText);
}
}
}
質問の更新後に回答が更新されました - 以下を参照してください
私は今でも、Factory パターンを使用することは正しいと思いますし、CreateLabel メソッドをオーバーロードすることも正しいと思います。しかし、LabelType を CreateLabel メソッドに渡す際に、Factory パターンを使用する点が欠けていると思います。
キーポイント:Factory パターンの全体の目的は、どの具体的なサブクラスをインスタンス化して返すかを選択するロジックをカプセル化することです。呼び出しコードは、どの型をインスタンス化するかをファクトリに指示すべきではありません。したがって、ファクトリを呼び出すコードが将来そのロジックへの変更から保護され、ファクトリへの新しい具象サブクラスの追加からも保護されるという利点があります。呼び出しコードが依存する必要があるのは、Factory と、CreateLabel から返される Interface タイプだけです。
Factory を呼び出す時点のコード内のロジックは、現時点では次の疑似コードのようになっている必要があります...
// Need to create a label now
ILabel label;
if(we need to create a small label)
{
label = factory.CreateLabel(LabelType.SmallLabel, "ref1");
}
else if(we need to create a large label)
{
label = factory.CreateLabel(LabelType.LargeLabel, "ref1");
}
else if(we need to create a custom label)
{
label = factory.CreateLabel(LabelType.CustomLabel, "ref1", "Custom text")
}
...つまり、何を作成するかをファクトリに明示的に指示していることになります。新しいラベル タイプがシステムに追加されるたびに、次の操作が必要になるため、これは好ましくありません。
- 新しい LabelType 値を処理するためにファクトリ コードを変更します。
- ファクトリが呼び出すすべての場所に新しい else-if を追加します。
ただし、LabelType 値を選択するロジックをファクトリに移動すると、これを回避できます。ロジックは他のすべてのものとともに工場でカプセル化されます。新しいタイプのラベルがシステムに追加された場合は、ファクトリーを変更するだけで済みます。Factory を呼び出す既存のコードはすべて同じままであり、重大な変更はありません。
現在の呼び出しコードが、大きなラベルが必要か小さなラベルが必要かを決定するために使用するデータは何ですか?そのデータはファクトリの CreateLabel() メソッドに渡す必要があります。
Factory クラスと label クラスは次のようになります...
// Unchanged
public class BasicLabel: ILabel
{
public LabelSize Size {get; private set}
public string TrackingReference { get; private set; }
public SmallLabel(LabelSize size, string trackingReference)
{
Size = size;
TrackingReference = trackingReference;
}
}
// ADDED THE NULL OR EMPTY CHECK
public class CustomLabel : ILabel
{
public string TrackingReference { get; private set; }
public string CustomText { get; private set; }
public CustomLabel(string trackingReference, string customText)
{
TrackingReference = trackingReference;
if(customText.IsNullOrEmpty()){
throw new SomeException();
}
CustomText = customText;
}
}
public class LabelFactory
{
public ILabel CreateLabel(string trackingReference, LabelSize labelSize)
{
return new BasicLabel(labelSize, trackingReference);
}
public ILabel CreateLabel(string trackingReference, string customText)
{
return new CustomLabel(trackingReference, customText);
}
}
これがお役に立てば幸いです。
あなたの質問を読んでから、それが聞こえます。私たちは、私が開発CAD / CAMアプリケーションで異なるアプローチを使用します。
私のアプリケーションを起動時にラベルのマスターリストを作成するファクトリメソッドを使用しています。 彼らはお互いの変異体であるので、私のラベルの一部は、初期化パラメータを持っています。たとえば、私たちは、平坦部のラベルの3種類があります。他の人は、ユーザが定義したり、セットアップで知られていないパラメータがありますが。
最初のケースでは、初期化は、ファクトリメソッド内で処理されます。だから私は、必要なパラメータを渡しFlatPartLabelの3つのインスタンスを作成します。
第2のケースでラベルのインタフェースは、configureオプションを持っています。これは、設定パネルを投入するためにラベルプリンタのダイアログで呼ばれています。追跡参照とCustomTextが渡されますあなたのケースでは、これがどこにあるか。
私のラベルインタフェースは、各ラベルタイプの一意のIDを返します。私はラベルの種類に対処するための特定のコマンドを持っていた場合、私は、1のIDと一致する見つける私のアプリケーションでラベルのリストをトラバースラベルの特定の型にキャストし、それを構成します。ユーザーは、特定のフラット部分の1枚のラベルを印刷したいときに我々はこれを行います。
これを行うと、あなたはあなたのラベルが本質的でないパラメータで負担あなたの工場をする必要はありませんし、パラメータに任意の複雑になることを意味します。