我創建了一系列的構建器來清理爲我的模擬創建領域類的語法,作爲改進我們的整體單元測試的一部分。我的構建者實質上填充了一個域類(例如Schedule
),其中一些值通過調用相應的WithXXX
並將它們鏈接在一起確定。帶繼承的生成器設計模式:有更好的方法嗎?
我在構建器中遇到了一些共同點,我想將它抽象到基類中以增加代碼重用。不幸的是我最終看起來像:
public abstract class BaseBuilder<T,BLDR> where BLDR : BaseBuilder<T,BLDR>
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<ScheduleInterval,ScheduleIntervalBuilder>
{
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我不能在BaseBuilder
的WithId(int)
方法使用return this
。
是暴露子類型的屬性abstract BLDR This { get; }
我唯一的選擇在這裏,或者我錯過了一些語法技巧?
更新(因爲我可以告訴我爲什麼多一點清楚這樣做):
最終的結果是有該構建異型域類建設者人們預期從數據庫中檢索[程序員]可讀格式。沒有什麼錯......
mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
new Schedule
{
ScheduleId = 1
// ...
}
);
因爲這已經很可讀了。替代建設者語法是:
mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
new ScheduleBuilder()
.WithId(1)
// ...
.Build()
);
優勢我找出來使用的建設者(和實施所有這些WithXXX
方法)是抽象掉複雜的創造(自動擴展我們的數據庫中查找值與正確Lookup.KnownValues
而無需訪問數據庫很明顯),並具有建設者域類提供常用的可重複使用的測試配置文件...
mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
new ScheduleBuilder()
.AsOneDay()
.Build()
);
YUP看起來像我們堅持「抽象的T這{get;}」我會接受和關閉,因爲我認爲解決方案是所有可用的。 – cfeduke 2008-10-29 14:50:11
我試圖找到一個相對清晰的方式來創建建設者,並發現文章(http://www.codeproject.com/Articles/240756/Hierarchically-Implementing-theBolchs-Builder-Pat)。它提供了一次投射(受保護的BLDR _this; _this =(BLDR)this; return _this;),但我不明白爲什麼作者添加了類WindowBuilder:WindowBuilder {}文章的底部)。在這篇文章中,我看到幾乎相同的模式,但cfeduke使用類ScheduleIntervalBuilder: BaseBuilder ... –
2012-06-22 21:59:49