20

I'm creating a series of builders to clean up the syntax which creates domain classes for my mocks as part of improving our overall unit tests. My builders essentially populate a domain class (such as a Schedule) with some values determined by invoking the appropriate WithXXX and chaining them together.

I've encountered some commonality amongst my builders and I want to abstract that away into a base class to increase code reuse. Unfortunately what I end up with looks like:

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;
    }
}

Take special note of the protected abstract BLDR This { get; }.

A sample implementation of a domain class builder is:

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;
    }

    // ...
}

Because BLDR is not of type BaseBuilder I cannot use return this in the WithId(int) method of BaseBuilder.

Is exposing the child type with the property abstract BLDR This { get; } my only option here, or am I missing some syntax trick?

Update (since I can show why I'm doing this a bit more clearly):

The end result is to have builders that build profiled domain classes that one would expect to retrieve from the database in a [programmer] readable format. There's nothing wrong with...

mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
    new Schedule
    {
        ScheduleId = 1
        // ...
    }
);

as that's pretty readable already. The alternative builder syntax is:

mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
    new ScheduleBuilder()
        .WithId(1)
        // ...
        .Build()
);

the advantage I'm looking for out of using builders (and implementing all these WithXXX methods) is to abstract away complex property creation (automatically expand our database lookup values with the correct Lookup.KnownValues without hitting the database obviously) and having the builder provide commonly reusable test profiles for domain classes...

mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
    new ScheduleBuilder()
        .AsOneDay()
        .Build()
);
cfeduke
  • 23,100
  • 10
  • 61
  • 65

3 Answers3

16

All I can say is that if there is a way of doing it, I want to know about it too - I use exactly this pattern in my Protocol Buffers port. In fact, I'm glad to see that someone else has resorted to it - it means we're at least somewhat likely to be right!

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Yup looks like we're stuck with "abstract T This { get; }" I'll accept and close since I think the solution is all that is available. – cfeduke Oct 29 '08 at 14:50
  • I'm trying to find a relatively clear way to create builders and found that article (http://www.codeproject.com/Articles/240756/Hierarchically-Implementing-the-Bolchs-Builder-Pat). It offers to cast it once.(protected BLDR _this; _this = (BLDR)this; return _this;), but i can't understand why the author added class WindowBuilder : WindowBuilder { } (Look complete example at the bottom of article). In this post I see almost the same pattern, but cfeduke uses class ScheduleIntervalBuilder : BaseBuilder... – Alex Dn Jun 22 '12 at 21:59
  • Personaly I would probably implement each Builder without the base class. I always try not using this *self-generic pattern*: http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx – Pellared Oct 02 '14 at 22:57
  • 3
    @Pellared: But then you don't get the benefits of the situations where you want to deal with "any builder" or "any message" - and yes, those do occur. There are pros and cons with each approach. – Jon Skeet Oct 03 '14 at 05:44
5

I know this is an old question, but I think you can use a simple cast to avoid the abstract BLDR This { get; }

The resulting code would then be:

public abstract class BaseBuilder<T, BLDR> where BLDR : BaseBuilder<T, BLDR>
                                           where T : new()
{
    public abstract T Build();

    protected int Id { get; private set; }

    public BLDR WithId(int id)
    {
        _id = id;
        return (BLDR)this;
    }
}

public class ScheduleIntervalBuilder :
    BaseBuilder<ScheduleInterval,ScheduleIntervalBuilder>
{
    private int _scheduleId;
    // ...

    public override ScheduleInterval Build()
    {
        return new ScheduleInterval
        {
                Id = base.Id,
                ScheduleId = _scheduleId
                    // ...
        };
    }

    public ScheduleIntervalBuilder WithScheduleId(int scheduleId)
    {
        _scheduleId = scheduleId;
        return this;
    }

    // ...
}

Of course you could encapsulate the builder with

protected BLDR This
{
    get
    {
        return (BLDR)this;
    }
}
dvdvorle
  • 941
  • 1
  • 10
  • 25
2

This is a good implementation strategy for C#.

Some other languages (can't think of name of research language I've seen this in) have type systems that either support a covariant "self"/"this" directly, or have other clever ways to express this pattern, but with C#'s type system, this is a good (only?) solution.

Brian
  • 117,631
  • 17
  • 236
  • 300