全体的な単体テストの改善の一環として、モック用のドメイン クラスを作成する構文をクリーンアップする一連のビルダーを作成しています。私のビルダーは基本的に、ドメイン クラス ( a など) に、適切な を呼び出してチェーン化する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 型ではないためreturn this
、 のWithId(int)
メソッドでは使用できませんBaseBuilder
。
ここでプロパティを使用して子型を公開するのは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()
);