9

最近、私は多くのリクエスト/レスポンスを扱っているようで、一般的なものを作成することを考えました。

私は次のものを持っていますが、複数の ifs ステートメントを作成することに満足しておらず、それらを避けたいと考えています。

アイデアは次のとおりです。

リクエスト/レスポンスに関係なく、それらを処理します。一般的なリクエスト Handler 内でこれらの if ステートメントを削除するにはどうすればよいですか?

コード

class Program
{
    private static void Main()
    {
        IRequestResponseFactory factory = new RequestResponseFactory();

        var customerRequest = new CustomerRequest { Name = "Joe", Surname = "Bloggs" };
        var customerResponse = factory.ProcessRequest<CustomerRequest, CustomerResponse>(customerRequest);

        var billRequest = new BillRequest() { Amount = 100m };
        var billResponse = factory.ProcessRequest<BillRequest, BillResponse>(billRequest);

        Console.WriteLine(billResponse.Success);
        Console.WriteLine(customerResponse.Success);
        Console.ReadKey();

    }
}


public class CustomerRequest : IRequestData<CustomerResponse>
{
    public string Name { get; set; }
    public string Surname { get; set; }
}

public class CustomerResponse
{
    public bool Success { get; set; }
}
public class BillRequest : IRequestData<BillResponse>
{
    public decimal Amount { get; set; }
}
public class BillResponse
{
    public bool Success { get; set; }
}
public interface IRequestData<TResponse>
{
}

public interface IRequestHandler<TRequest, TResponse> where TRequest : IRequestData<TResponse>
{
    TResponse ProcessRequest(TRequest request);
}

public interface IRequestResponseFactory
{
    TResponse ProcessRequest<TRequest, TResponse>(TRequest request) where TRequest : IRequestData<TResponse>;
}
class RequestResponseFactory : IRequestResponseFactory
{
    public TResponse ProcessRequest<TRequest, TResponse>(TRequest request) where TRequest : IRequestData<TResponse>
    {
        var handler = new GenericRequestHandler<TRequest, TResponse>();
        TResponse response = handler.ProcessRequest(request);
        return response;
    }
}
public class GenericRequestHandler<TRequest, TResponse> : IRequestHandler<TRequest, TResponse> where TRequest : IRequestData<TResponse>
{
    public TResponse ProcessRequest(TRequest request)
    {
        var response = default(TResponse);

        //How do I avoid this if statements????

        if (request is IRequestData<CustomerResponse>)
        {
            var tempResponse = new CustomerResponse { Success = true };
            response = (TResponse)Convert.ChangeType(tempResponse, typeof(TResponse));
            return response;
        }
        if (request is IRequestData<BillResponse>)
        {
            var tempResponse = new BillResponse { Success = false };
            response = (TResponse)Convert.ChangeType(tempResponse, typeof(TResponse));
            return response;
        }
        return response;
    }
}
4

1 に答える 1

8

GenericRequestHandler<TRequest, TResponse>クラスを特殊なsに置き換えIRequestHandlerて、ファクトリにリフレクションを介して利用可能なすべてのハンドラーを追跡させることができます。

具体的には、これらはハンドラーになる可能性があります。

public class CustomerRequestHandler : IRequestHandler<CustomerRequest, CustomerResponse>
{
    public CustomerResponse ProcessRequest(CustomerRequest request)
    {
        return new CustomerResponse { Success = true };
    }
}

public class BillRequestHandler : IRequestHandler<BillRequest, BillResponse>
{
    public BillResponse ProcessRequest(BillRequest request)
    {
        return new BillResponse { Success = false };
    }
}

これで、ファクトリは、if醜さを含む汎用ハンドラーに呼び出しを転送するだけでなく、次のことを行います。

  • 現在のアセンブリ (場合によっては他のアセンブリも) をスキャンして、実装する型を探して自身を初期化します。IRequestHandler<TRequest, TResponse>
  • TRequestキーが型で、値がハンドラー型である、ハンドラー型の辞書を作成します
  • 着信要求があると、ハンドラー リストをチェックして、その特定のタイプを処理できるかどうかを確認し、次のことを行います。
    • 使用可能なハンドラーに作業を委譲します
    • 適切なハンドラが見つからない場合、例外をスローします

これは、ファクトリ クラスの可能な実装です。

class RequestResponseFactory : IRequestResponseFactory
{
    private readonly Dictionary<Type, Type> _requestHandlerTypes;

    public RequestResponseFactory()
    {
        _requestHandlerTypes =
            typeof(RequestResponseFactory).Assembly.GetTypes()
                .Where(t => !t.IsAbstract)
                .Select(t => new
                {
                    HandlerType = t,
                    RequestType = GetHandledRequestType(t)
                })
                .Where(x => x.RequestType != null)
                .ToDictionary(
                    x => x.RequestType,
                    x => x.HandlerType
                );
    }

    private static Type GetHandledRequestType(Type type)
    {
        var handlerInterface = type.GetInterfaces()
            .FirstOrDefault(i =>
                i.IsGenericType &&
                i.GetGenericTypeDefinition() == typeof(IRequestHandler<,>));

        return handlerInterface == null ? null : handlerInterface.GetGenericArguments()[0];
    }

    public TResponse ProcessRequest<TRequest, TResponse>(TRequest request) where TRequest : IRequestData<TResponse>
    {
        if (!_requestHandlerTypes.ContainsKey(typeof(TRequest)))
            throw new ApplicationException("No handler registered for type: " + typeof(TRequest).FullName);

        var handlerType = _requestHandlerTypes[typeof(TRequest)];

        var handler = (IRequestHandler<TRequest, TResponse>)Activator.CreateInstance(handlerType);

        return handler.ProcessRequest(request);
    }
}

動作サンプルを含む完全なプログラムは、http://ideone.com/TxRnEiで入手できます。

出力は元のプログラムの出力と同じですが、プログラムでは新しいタイプのリクエストと新しいタイプのハンドラーを追加できるようになり、実行時にそれらを動的に使用できるようになりました。長いif/を記述する必要はありません。caseいつ使用するかを指定するステートメント。

于 2012-11-04T15:10:30.367 に答える