どの工場は、オブジェクトのタイプを作成するために知っているのですか?
-
22-08-2019 - |
質問
私は、ファクトリメソッドデザインパターンは、それを与えるために私がやろうとしているが、私は(それが作成サブクラスの知識)どのくらいの責任わからないんだけど何のために適切であると考えています。ウィキペディアで Factory Methodパターンを使用する例は、私はほぼ正確にいる状況を説明します
public class ImageReaderFactory
{
public static ImageReader getImageReader( InputStream is )
{
int imageType = figureOutImageType( is );
switch( imageType )
{
case ImageReaderFactory.GIF:
return new GifReader( is );
case ImageReaderFactory.JPEG:
return new JpegReader( is );
// etc.
}
}
}
私の質問は、どのようなfigureOutImageType
機能は次のようになりんですか?この具体例では、私はそれがデータである画像フォーマットを決定するためにInputStream
のファイルヘッダをチェックすることを前提とします。私はImageReaderFactory
自体はファイルヘッダを解析し、決定する方法を知っている場合、ファイルの種類がGIFであるかどうかを知りたいです、JPEG、等、またはそれはそれは、画像の種類を知ることができます各Reader
クラス内の関数を呼び出した場合。このような何か、多分ます:
int figureOutImageType(InputStream is)
{
if(GifReader.isGIF(is))
return ImageReaderFactory.GIF;
else if(JpegReader.isJPEG(is))
return ImageReaderFactory.JPEG;
// etc.
}
これは、画像に休憩カプセル化を解析する方法を知っている、とサブクラスは1が作成されるべきかを決定させることファクトリメソッドデザインパターンの一部である工場を持つように思えます。なぜだけではなく、各サブクラスがfigureOutImageType
機能にInputStream
上のチェックを実行し、スイッチケースをスキップする必要があるためgetImageReader
機能だけで、いくつかの冗長なコードを追加しているようしかし、それもそうですか?
私は前に工場を使用して任意の経験を持っていなかった、と私は、この問題に対処するための最良の方法で、過去にそれらを使用している一部の人からいくつかの洞察を得るために期待していました。それは工場がそのサブクラスの内部の仕組みを知っている、または、彼らは工場を作成するために、どのようにあなたがそれをすべて整理しないどの知らせるための責任を負わなければならない持っていても大丈夫ですか?
ありがとうございます。
解決
工場は、作成する実際のオブジェクトの選択についていくつかのアイデアを持っている必要があります。例えば、.NETのWebRequest.Create
方法は、Uri
のプロトコル部分をチェックすることにより、異なるプロトコルのクライアントの間で選択することができなければなりません。これは、全体を解析する必要はありません。ちょうどそれを担当することになるだろうされているクラスを区別するために必要な部品(あなたの例では、それはおそらくちょうどファイルのヘッダーになるでしょう)。
カプセル化を壊すに関するご質問については、本当に...ほとんどの時間、工場がハードコードされていないと、既にクラスとその機能の異なる種類を知っています。それは、すでにクラスの既知のセットが提供する機能に依存しますので、あなたはそれに多くを追加していません。また、工場や(DRY原則の精神で)サブクラスの両方で使用することができ、別のヘルパークラスに工場の検出部をカプセル化することができます。
他のヒント
の両方がコンテキストに応じて有効な選択肢です。
あなたが拡張のために設計するている場合は、 - 別のImageReaderのプラグインモデルを言う - そしてあなたのファクトリクラスは、すべての可能性のImageReaderについて知ることができません。その場合、あなたはImageReader.CanRead(ImageStream)
ルートを行く - あなたはそれを読むことができるものを見つけるまで、各実装を求めて
時々順序は、ここで問題ないことを、注意してください。あなたはJPGのみを扱うことができますGenericImageReaderが、それで良いですJpeg2000ImageReaderを有することができます。どちらで停止しますするImageReader実装を歩くことは初めてです。あなたはそれが問題だ場合に可能のImageReaderのリストを並べ替えを見てみたいことがあります。
ImageReaderのリストが有限であなたのコントロール下にある場合は、そうでなければ、あなたは、より伝統的な工場のアプローチを行くことができます。その場合には、工場出荷時は、作成するかを決定します。これは、まだ結合を増加させない各ImageReaderのためのルールを追加、CTORによってするImageReaderの具体的な実装に結合されています。 ImageReaderのを選ぶためのロジックがするImageReader自体に主である場合には、コードの重複を避けるために、あなたはまだImageReader.CanRead(ImageStream)
ルートを行くことができます - しかし、ちょうどあなたが歩いているタイプのハードコードすることができます。
の拡張のために、あなたが言及これらの依存関係の一部を外部化することができます。それは、ファイルの種類を考え出すか、それを処理するクラスへのファイルの種類をマッピングするように。外部レジストリ(すなわち、プロパティファイル)、たとえばGIFを格納します - > GifReader、またはより良いGIF - > GifMetadataClass。次に、あなたのコードは、一般的なことではなく、すべてのクラスに依存している、プラスあなたが将来的にそれを拡張することができ、またはサードパーティがそれを拡張することができ可能性があります。
私は、コンテンツタイプを推測して、ファクトリを使用しようとするだろう。実際、私はいくつかの時間前にこれをした。
ここでは、ファイルのコンテンツタイプを推測するクラスがあります:
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace Nexum.Abor.Common
{
/// <summary>
/// This will work only on windows
/// </summary>
public class MimeTypeFinder
{
[DllImport(@"urlmon.dll", CharSet = CharSet.Auto)]
private extern static UInt32 FindMimeFromData(
UInt32 pBC,
[MarshalAs(UnmanagedType.LPStr)] String pwzUrl,
[MarshalAs(UnmanagedType.LPArray)] byte[] pBuffer,
UInt32 cbSize,
[MarshalAs(UnmanagedType.LPStr)]String pwzMimeProposed,
UInt32 dwMimeFlags,
out UInt32 ppwzMimeOut,
UInt32 dwReserverd
);
public string getMimeFromFile(string filename)
{
if (!File.Exists(filename))
throw new FileNotFoundException(filename + " not found");
var buffer = new byte[256];
using (var fs = new FileStream(filename, FileMode.Open))
{
if (fs.Length >= 256)
fs.Read(buffer, 0, 256);
else
fs.Read(buffer, 0, (int)fs.Length);
}
try
{
UInt32 mimetype;
FindMimeFromData(0, null, buffer, 256, null, 0, out mimetype, 0);
var mimeTypePtr = new IntPtr(mimetype);
var mime = Marshal.PtrToStringUni(mimeTypePtr);
Marshal.FreeCoTaskMem(mimeTypePtr);
return mime;
}
catch (Exception)
{
return "unknown/unknown";
}
}
}
}
私は、共通のCanReadFrom
インタフェース( - FIXMEこれが可能であるかどうかわからない)で静的ImageReader
方法(か何か)を持っているでしょう。すべての実装を取得し、機能を呼び出すためにリフレクションを使用してください。 1がtrueを返した場合、クラスのインスタンスを返します。