Factory Method for Using the Is / As Operator

I have a factory that looks something like the following snippet. Foo is a wrapper class for Bar, and in most cases (but not for all) there is a 1: 1 mapping. Normally, Bar knows nothing about Foo, but Foo accepts an instance of Bar. Is there a better / cleaner approach to this?

public Foo Make( Bar obj )
{
    if( obj is Bar1 )
        return new Foo1( obj as Bar1 );
    if( obj is Bar2 )
        return new Foo2( obj as Bar2 );
    if( obj is Bar3 )
        return new Foo3( obj as Bar3 );
    if( obj is Bar4 )
        return new Foo3( obj as Bar4 ); // same wrapper as Bar3
    throw new ArgumentException();
}

At first glance, this question may look like a duplicate (maybe it is), but I did not see it exactly the same. Here is a close, but not quite:

Factory based on Typeof or

+3
source share
3 answers

I'm not sure what you really want to achieve. I will probably try to make it more general.

Foo, , . , .

private Dictionary<Type, Type> fooOfBar = new Dictionary<Type, Type>();
public initialize()
{
  // you could scan all types in the assembly of a certain base class
  // (fooType) and read the attribute

  fooOfBar.Add(attribute.BarType, fooType);
}

public Foo Make( Bar obj )
{
  return (Foo)Activator.CreateInstance(fooOfBar(obj.GetType()), obj);
}
+1

, as is . , as null.

-, , , . , , , , . () () . Foo , Foo.

:

// Foo creator delegate.
public delegate Foo CreateFoo(Bar bar);

// Lookup of creators, for each type of Bar.
public static Dictionary<Type, CreateFoo> Factory = new Dictionary<Type, CreateFoo>();

// Registration.
Factory.Add(typeof(Bar1), (b => new Foo1(b)));

// Factory method.
static Foo Create(Bar bar)
{
    CreateFoo cf;
    if (!Factory.TryGetValue(bar.GetType(), out cf))
        return null;

    return cf(bar);
}
+4

. / . , AutoMapper.

factory . factory Foo1 Bar1, Foo2 Bar2 ..:

Foo1 CreateFoo1(Bar1 bar1) { ... }

Foo2 CreateFoo2(Bar2 bar2) { ... }

factory , factory .

var inputType = input.GetType();
var factory = factories[inputType];
var output = factory(input);

, factory, lambdas, .

​​ , , . , Foo# Foo , Bar# Bar . , , object , .

factory :

public class FooFactory : TypeBasedFactory<Bar, Foo>
{
    private Foo1 CreateFoo1(Bar1 bar1)
    {
        return new Foo1(bar1.Id, bar1.Name, ...);
    }

    private Foo2 CreateFoo2(Bar2 bar2)
    {
        return new Foo2(bar2.Description, ...);
    }
}

, factory . . TypeBasedFactory CreateFrom, factory:

var fooFactory = new TypeBasedFactory<Bar, Foo>();
var foo = fooFactory.CreateFrom(bar);

TypeBasedFactory:

public abstract class TypeBasedFactory<TInput, TOutput>
    where TInput : class where TOutput : class
{
    private readonly Dictionary<Type, Func<TInput, TOutput>> factories;

    protected TypeBasedFactory()
    {
        factories = CreateFactories();
    }

    private Dictionary<Type, Func<TInput, TOutput>> CreateFactories()
    {
        return GetType()
            .GetMethods(
                BindingFlags.Public
                | BindingFlags.NonPublic
                | BindingFlags.Instance)
            .Where(methodInfo =>
                !methodInfo.IsAbstract
                && methodInfo.GetParameters().Length == 1
                && typeof(TOutput).IsAssignableFrom(methodInfo.ReturnType))
            .Select(methodInfo => new
            {
                MethodInfo = methodInfo,
                methodInfo.GetParameters().First().ParameterType
            })
            .Where(factory =>
                typeof(TInput).IsAssignableFrom(factory.ParameterType)
                && !factory.ParameterType.IsAbstract)
            .ToDictionary(
                factory => factory.ParameterType,
                factory => CreateFactory(factory.MethodInfo, factory.ParameterType));
    }

    private Func<TInput, TOutput> CreateFactory(MethodInfo methodInfo, Type parameterType)
    {
        // Create this Func<TInput, TOutput>: (TInput input) => Method((Parameter) input)
        var inputExpression = Expression.Parameter(typeof(TInput), "input");
        var castExpression = Expression.Convert(inputExpression, parameterType);
        var callExpression = Expression.Call(Expression.Constant(this), methodInfo, castExpression);
        var lambdaExpression = Expression.Lambda<Func<TInput, TOutput>>(callExpression, inputExpression);
        return lambdaExpression.Compile();
    }

    public TOutput CreateFrom(TInput input)
    {
        if (input == null)
            throw new ArgumentNullException(nameof(input));
        var inputType = input.GetType();
        Func<TInput, TOutput> factory;
        if (!factories.TryGetValue(inputType, out factory))
            throw new InvalidOperationException($"No factory method defined for {inputType.FullName}.");
        return factory(input);
    }
}

CreateFactories , , TOuput (, ) TInput ( ).

CreateFactory Func<TInput, TOutput>, downcasting factory. , , .

, TypeBasedFactory, , (.. factory ).

+1

Source: https://habr.com/ru/post/1748183/


All Articles