c# 将 DRY 标准应用于多个 类

c# apply DRY standards to multiple classes

我目前正在做一个项目,其中有很多 class 继承自基础 class。 这是在一个测试项目中。基础 class 看起来像这样:

public class DatabaseContextContext
{
    public DatabaseContext DatabaseContext;
    protected DatabaseContextContext()
    {
        DatabaseContext = Substitute.For<DatabaseContext>();
    }

    public static DatabaseContextContext GivenServices() => new DatabaseContextContext();

    public void WhenList<T>(List<T> response) where T: class
    {
        DatabaseContext.Set<T>().Returns(response.AsQueryable());
    }
}

然后还有一个 class 看起来像这样:

public class VenuesContext: DatabaseContextContext
{
    public IMediator Mediator;
    protected VenuesContext()
    {
        Mediator = Substitute.For<IMediator>();
    }

    public VenuesContext WhenListSucceeds(List<Venue> venues)
    {
        WhenList(venues);
        return this;
    }
}

还有一个像这样:

public class TheatresContext: DatabaseContextContext
{
    public IMediator Mediator;
    protected TheatresContext()
    {
        Mediator = Substitute.For<IMediator>();
    }

    public TheatresContext WhenListSucceeds(List<Theatre> theatres)
    {
        WhenList(theatres);
        return this;
    }
}

你可能已经猜到了,这是一个简单的版本,有数百个这样的 classes,有些更复杂,但思路是一样的。 无论如何,我想尝试删除一些重复的代码,即:

public ListVenuesContext WhenListSucceeds(List<Venue> venues)
{
    WhenList(venues);
    return this;
}

目前您可以像这样将这些方法链接在一起:

var services = VenuesContext.GivenServices().WhenListSucceeds(new List<Venue>());

而且我想保留该功能。 我试着做这样的事情:

public TReturn WhenList<T, TReturn>(List<T> response) where T: class where TReturn: class, new()
{
    DatabaseContext.Set<T>().Returns(response.AsQueryable());

    return new TReturn();
}

但我不确定这是否正确,因为我必须调用 new TReturn() 而不是 this。 此外,链更改为:

var services = VenuesContext.GivenServices().WhenList<Venue, ListVenuesContext>(new List<Venue>());

这并不糟糕,但如果不必在每次调用中都添加 <Venue, ListVenuesContext> 就好了。

谁能想出更优雅的解决方案?


更新

我可能已经找到了使用 http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx

的解决方案

如果我将 DatabaseContextContext 更改为:

public class DatabaseContextContext<T> where T: DatabaseContextContext<T>
{
    public DatabaseContext DatabaseContext;
    protected DatabaseContextContext()
    {
        DatabaseContext = Substitute.For<DatabaseContext>();
    }

    public T WhenList<TM>(List<TM> response) where TM : class
    {
        DatabaseContext.Set<TM>().Returns(response.AsQueryable());

        return (T)this;
    }
}

那么我可以这样做:

public class ListVenuesContext : DatabaseContextContext<ListVenuesContext>
{
    public static ListVenuesContext GivenServices() => new ListVenuesContext();

    public ListVenuesHandler WhenCreateHandler() => new ListVenuesHandler(DatabaseContext);
}

这将允许我这样调用:

var services = ListVenuesContext.GivenServices().WhenList(new List<Venue>());

我能看到的唯一问题是我不能多次继承。例如:

public class ListVenuesContext : VenuesContext
{
    public static ListVenuesContext GivenServices() => new ListVenuesContext();

    public ListVenuesHandler WhenCreateHandler() => new ListVenuesHandler(DatabaseContext);
}

public class VenuesContext: DatabaseContextContext<VenuesContext>
{
    public IMediator Mediator;
    protected VenuesContext()
    {
        Mediator = Substitute.For<IMediator>();
    }

    public void WhenGet(Venue venue)
    {
        Mediator.Send(Arg.Any<GetVenue>()).Returns(Attempt<Venue>.Succeed(venue));
    }
}

已排序。我确实使用了上面的解决方案,但我是这样做的:

public class DatabaseContextContext<T> where T: DatabaseContextContext<T>
{
    public DatabaseContext DatabaseContext;
    protected DatabaseContextContext()
    {
        DatabaseContext = Substitute.For<DatabaseContext>();
    }

    public T WhenListSucceeds<TM>(List<TM> response) where TM : class
    {
        DatabaseContext.Set<TM>().Returns(response.AsQueryable());

        return (T)this;
    }

    public T WhenGetSucceeds<TM>(TM response) where TM : class
    {
        DatabaseContext.Set<TM>().Returns(new List<TM> {response}.AsQueryable());

        return (T)this;
    }
}

如果有一个 class 其他人继承自那个也继承自 DatabaseContextContext,我可以这样做:

public class MediatorContext<T>: DatabaseContextContext<T> where T: MediatorContext<T>
{
    public IMediator Mediator;
    protected MediatorContext()
    {
        Mediator = Substitute.For<IMediator>();
    }
}

然后 child class 可以继承它,像这样:

public class DeleteVenueContext : MediatorContext<DeleteVenueContext>
{
    public static DeleteVenueContext GivenServices() => new DeleteVenueContext();

    public DeleteVenueHandler WhenCreateHandler() => new DeleteVenueHandler(DatabaseContext, Mediator);
}

这意味着我仍然可以像这样使用链接:

var services = DeleteVenueContext.GivenServices().WhenGetSucceeds(new Venue { Id = id });