我正在创建一系列构建器来清理语法,为我的模拟创建域类,作为改进整体单元测试的一部分.我的构建器基本上填充了一个域类(例如a Schedule
),其中一些值通过调用相应的值WithXXX
并将它们链接在一起来确定.
我在构建器中遇到了一些共性,我想将它抽象到基类中以增加代码重用.不幸的是,我最终看起来像:
public abstract class BaseBuilderwhere BLDR : BaseBuilder where T : new() { public abstract T Build(); protected int Id { get; private set; } protected abstract BLDR This { get; } public BLDR WithId(int id) { Id = id; return This; } }
特别注意protected abstract BLDR This { get; }
.
域类构建器的示例实现是:
public class ScheduleIntervalBuilder : BaseBuilder{ private int _scheduleId; // ... // UG! here's the problem: protected override ScheduleIntervalBuilder This { get { return this; } } public override ScheduleInterval Build() { return new ScheduleInterval { Id = base.Id, ScheduleId = _scheduleId // ... }; } public ScheduleIntervalBuilder WithScheduleId(int scheduleId) { _scheduleId = scheduleId; return this; } // ... }
因为BLDR不是BaseBuilder类型,所以我不能return this
在WithId(int)
方法中使用BaseBuilder
.
在这里公开带有属性的子类型是abstract BLDR This { get; }
我唯一的选择,还是我错过了一些语法技巧?
更新(因为我可以更清楚地说明为什么我这样做了):
最终结果是让构建器构建配置文件的域类,人们希望以[程序员]可读格式从数据库中检索这些类.......没有错
mock.Expect(m => m.Select(It.IsAny())).Returns( new Schedule { ScheduleId = 1 // ... } );
因为那已经很可读了.替代构建器语法是:
mock.Expect(m => m.Select(It.IsAny())).Returns( new ScheduleBuilder() .WithId(1) // ... .Build() );
我正在寻找使用构建器(以及实现所有这些WithXXX
方法)的优势是抽象出复杂的属性创建(自动扩展我们的数据库查找值,而Lookup.KnownValues
不是明显地访问数据库)并让构建器提供通常可重用的测试配置文件对于域类...
mock.Expect(m => m.Select(It.IsAny())).Returns( new ScheduleBuilder() .AsOneDay() .Build() );
Jon Skeet.. 11
我可以说的是,如果是这样做的一种方式,我想知道它太-我用的正是我的这种模式Protocol Buffers的端口.事实上,我很高兴看到其他人已经采取了它 - 这意味着我们至少有可能是正确的!
我可以说的是,如果是这样做的一种方式,我想知道它太-我用的正是我的这种模式Protocol Buffers的端口.事实上,我很高兴看到其他人已经采取了它 - 这意味着我们至少有可能是正确的!