16

ファクトリ メソッドの設計パターンは、私がやろうとしていることには適していると思いますが、どの程度の責任 (それが作成するサブクラスの知識) を与えるべきかわかりません。ウィキペディアでファクトリ メソッド パターンを使用する例は、私がいる状況をほぼ正確に説明しています。

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関数がどのように見えるかです。この特定の例では、ファイル ヘッダーをチェックして、データがどの画像形式であるかを判断すると仮定します。ファイル ヘッダーを解析し、ファイル タイプが GIF か JPEG かを判断する方法を自身が知っているかどうかInputStreamを知りたいです。ImageReaderFactoryなど、または各クラス内で関数を呼び出して、Readerそれがどのタイプの画像であるかを知らせる場合。このようなもの、多分:

int figureOutImageType(InputStream is)
{
    if(GifReader.isGIF(is))
        return ImageReaderFactory.GIF;
    else if(JpegReader.isJPEG(is))
        return ImageReaderFactory.JPEG;
    // etc.
}

ファクトリに画像を解析する方法を知ってもらうとカプセル化が壊れてしまい、どの画像を作成するかをサブクラスに決定させるのは、ファクトリ メソッドの設計パターンの一部です。それでも、figureOutImageType関数が冗長なコードを追加しているようにも見えます。なぜなら、各サブクラスが関数InputStream内でチェックを実行しgetImageReader、switch ケースをスキップしないからです。

私は以前にファクトリを使用した経験がありませんでした。この問題を処理するための最善の方法について、過去にファクトリを使用したことがある人から洞察を得たいと思っていました。サブクラスの内部動作についてファクトリに知らせても問題ないでしょうか?それとも、どのサブクラスを作成するか、およびどのようにすべてを編成するかをファクトリに知らせる責任がありますか?

ありがとう!

4

5 に答える 5

6

Factoryは、作成する実際のオブジェクトを選択することについてある程度のアイデアを持っている必要があります。たとえばWebRequest.Create、.NETのメソッドは、のプロトコル部分をチェックすることにより、異なるプロトコルクライアントから選択できる必要がありますUri。全体を解析する必要はありません。どのクラスがそれを担当するかを区別するために必要な部分だけです(あなたの例では、おそらくファイルヘッダーだけです)。

カプセル化を破ることについてのあなたの質問に関しては、実際にはそうではありません...ほとんどの場合、ファクトリはハードコーディングされており、さまざまなタイプのクラスとその機能についてすでに知っています。既知のクラスのセットによって提供される機能にすでに依存しているため、あまり追加していません。ファクトリの検出部分を、ファクトリとサブクラスの両方で使用できる別のヘルパークラスにカプセル化することもできます(DRY原則の精神で)。

于 2009-03-31T23:26:02.143 に答える
2

コンテキストに応じて、どちらも有効な選択肢です。

拡張性を考慮して設計している場合 (さまざまな ImageReader のプラグイン モデルなど)、Factory クラスは可能なすべての ImageReader を認識できません。その場合は、ImageReader.CanRead(ImageStream)ルートに進みます-それを読み取ることができる人が見つかるまで、各実装者に尋ねます。

ここでは順序が重要になる場合があることに注意してください。JPG を処理できる GenericImageReader があるかもしれませんが、Jpeg2000ImageReader の方が優れています。ImageReader 実装者のウォークは、最初に実行された時点で停止します。問題がある場合は、可能な ImageReaders のリストをソートすることを検討してください。

それ以外の場合、ImageReaders のリストが有限であり、制御下にある場合は、より伝統的な Factory アプローチを使用できます。その場合、何を作成するかは Factory が決定します。これはすでに ctor によって ImageReader の具体的な実装に結合されているため、各 ImageReader にルールを追加しても結合は増加しません。ImageReader を選択するロジックが主に ImageReader 自体にある場合は、コードの重複を避けるために、このImageReader.CanRead(ImageStream)ルートをたどることができますが、どのタイプを歩くかをハードコーディングすることもできます。

于 2009-04-01T00:11:35.157 に答える
1

拡張性のために、言及したこれらの依存関係のいくつかを外部化することができます。それがどのような種類のファイルであるかを理解したり、ファイルタイプをそれを処理するクラスにマッピングしたりするようなものです。外部レジストリ(つまり、プロパティファイル)には、たとえばGIF-> GifReader、またはより適切なGIF->GifMetadataClassが格納されます。その場合、コードは汎用的であり、すべてのクラスに依存しない可能性があります。さらに、将来的にコードを拡張したり、サードパーティが拡張したりする可能性があります。

于 2009-03-31T23:27:52.797 に答える
1

これがWindowsの場合、コンテンツタイプを推測してから、ファクトリを使用します。実際、私はこれを少し前に行いました。

ファイルのコンテンツタイプを推測するクラスは次のとおりです。

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";
            }
        }
    }
}
于 2009-03-31T23:32:35.967 に答える
0

CanReadFrom共通インターフェースに静的メソッド(または何か)がありImageReaderます(これが可能かどうかはわかりません-FIXME)。リフレクションを使用してすべての実装者を取得し、関数を呼び出します。trueを返す場合は、クラスのインスタンスを返します。

于 2009-03-31T23:27:37.133 に答える