2013-11-26 4 views
3

У меня есть следующие два класса:Как я могу зарегистрировать фабрику общих объектов?

public class KeyedEntity<TEntity> 
{ 
    internal KeyedEntity() { } 

    public Identifier Key { get; set; } 
    public TEntity Entity { get; set; } 
} 

public static class KeyedEntity 
{ 
    public static KeyedEntity<TEntity> Create<TEntity>(Identifier key, TEntity entity) 
    { 
     return new KeyedEntity<TEntity> 
     { 
      Key = key, 
      Entity = entity, 
     }; 
    } 
} 

Причина, по которой конструктор internal и второй класс существует, я хочу, чтобы обеспечить соблюдение более высоко ремонтопригоден KeyedEntity.Create(x, y) синтаксиса, а не new KeyedEntity<T>{ Key = x, Entity = y }. (Обратите внимание, что тип выводится с прежним синтаксисом.)

Я хочу сказать AutoFixture, как создать экземпляр KeyedEntity. Однако метод Register, по-видимому, позволяет регистрировать только один тип, а не открытый общий тип.

Как я могу зарегистрировать KeyedEntity.Create<TEntity> как функцию создания для KeyedEntity<TEntity>?

+1

Почему вы считаете 'KeyedEntity.Create (x, y)' более ремонтопригодным, чем 'new KeyedEntity (x, y)'? –

+0

@Mark: метод factory может возвращать (будущий) подкласс, конструктор никогда не будет. – eFloh

+0

@eFloh А, я на самом деле не считал это, так как я никогда не проектировал свой код для подкласса (* предпочитаю композицию над наследованием *) :) –

ответ

3

Для поддержки открытого универсального типа, вы можете написать пользовательский образца Builder:

public class KeyedEntityBuilder : ISpecimenBuilder 
{ 
    private readonly static MethodInfo createMethod = 
     typeof(KeyedEntity).GetMethod("Create"); 

    public object Create(object request, ISpecimenContext context) 
    { 
     var t = request as Type; 
     if (t == null || 
      !t.IsGenericType || 
      t.GetGenericTypeDefinition() != typeof(KeyedEntity<>)) 
      return new NoSpecimen(request); 

     var entityType = t.GetGenericArguments().Single(); 

     var key = context.Resolve(typeof(Identifier)); 
     var entity = context.Resolve(entityType); 

     return createMethod 
      .MakeGenericMethod(entityType) 
      .Invoke(null, new[] { key, entity }); 
    } 
} 

(Опорный кодирование опущены для ясности.)

Следующий тест блок проходит:

public class Tests 
{ 
    [Fact] 
    public void CreateKeyedEntity() 
    { 
     var fixture = new Fixture(); 
     fixture.ResidueCollectors.Add(new KeyedEntityBuilder()); 

     var actual = fixture.Create<KeyedEntity<Foo>>(); 

     Assert.NotNull(actual.Key); 
     Assert.NotNull(actual.Entity); 
    } 
} 

Для лучшей ремонтопригодности, вы должны инкапсулировать KeyedEntityBuilder in a Customization.

+1

+1 Это идиоматический способ сделать это. –

0

Предполагая, что у вас есть набор производных типов, например:

public class A: KeyedEntity<A> 
{ 
} 

public class B: KeyedEntity<B> 
{ 
} 

Поскольку вышеупомянутый объект граф содержит циклическую ссылку (на T) необходимо настроить Fixture экземпляр опустить задания на первой рекурсии :

Затем Создать общий метод, который будет настраивать алгоритм создания для KeyedEntity<T>:

internal void CustomizeKeyedEntity<T>(IFixture fixture) 
{ 
    fixture.Customize<KeyedEntity<T>>(c => 
     c.FromFactory(() => 
      KeyedEntity.Create(
       fixture.Create<Identifier>(), 
       fixture.Create<T>()))); 
} 

Вы можете использовать вышеупомянутый метод, как:

this.CustomizeKeyedEntity<A>(fixture); 
this.CustomizeKeyedEntity<B>(fixture); 

Пример

[Fact] 
public void Test() 
{ 
    var fixture = new Fixture(); 

    this.CustomizeKeyedEntity<A>(fixture); 
    this.CustomizeKeyedEntity<B>(fixture); 

    var actualA = fixture.Create<A>(); 
    var actualB = fixture.Create<B>(); 
} 
+0

Вы уверены, что это круговая ссылка? –

+0

По-прежнему выглядит так же, как и при использовании метода 'Register'; он не допускает открытую регистрацию. Я знаю, что все еще могу сделать регистрацию для каждого построенного/закрытого типа, который я хочу использовать, но это не так удобно, как хотелось бы. Я могу понять, могу ли я улучшить вопрос, поэтому ясно, что речь идет о регистрации открытого родового типа и фабрики. – Sam

+0

@MarkSeemann Да, если A и B определены как в ответе. –

Смежные вопросы