Being descriptive in code when building objects

As we're working through the course, I've been struggling with some constructors of objects we create. For example, in the first couple of days the team was writing unit tests around creating video game objects. This would be done by specifying a whack of parameters in the constructor.

    9 [Test]

   10 public void Should_create_video_game_based_on_specification_using_classes()

   11 {

   12     VideoGame game = new VideoGame("Gears of War",

   13         2006,

   14         Publisher.Activision,

   15         GameConsole.Xbox360,

   16         Genre.Action);

   17 

   18     Assert.AreEqual(game.GameConsole, GameConsole.Xbox360);

   19 }

This is pretty typical as we want to construct an object that's fully qualified and there's two options to do this. Either via a constructor or by setting properties on an object after the fact. Neither of these really appeal to me so we talked about using the Builder pattern along with being fluent in writing a DSL to create domain objects.

For the same test above to create a video game domain object, this reads much better to me than to parameterize my constructors (which means I might also have to chain my constructors if I don't want to break code later):

   36 [Test]

   37 public void Should_create_video_game_based_on_specification_using_builder()

   38 {

   39     VideoGame game = VideoGameBuilder.

   40         StartRecording().

   41             CreateGame("Gears of War").

   42             ManufacturedBy(Publisher.Activision).

   43             CreatedInYear(2006).

   44             TargetedForConsole(GameConsole.Xbox360).

   45             InGenre(Genre.Action).

   46         Finish();

   47 

   48     Assert.AreEqual(game.GameConsole, GameConsole.Xbox360);

   49 }

Agree or disagree? The nice thing about this is I don't have to specify everything here. For example I could create a video game with just a title and publisher like this:

   34 VideoGame game = VideoGameBuilder.

   35     StartRecording().

   36         CreateGame("Gears of War").

   37         ManufacturedBy(Publisher.Activision).

   38     Finish();

This avoids the problem that I have to do when using constructors in that I don't have to overload the constructor.

Here's the builder class that spits out our VideoGame object. It's nothing more than a bunch of methods that return a VideoGameBuilder and the Finish() method that returns our object created with all it's values.

   44 internal class VideoGameBuilder

   45 {

   46     private VideoGame gameToBuild;

   47 

   48     public VideoGameBuilder()

   49     {

   50         gameToBuild = new VideoGame();

   51     }

   52 

   53     public static VideoGameBuilder StartRecording()

   54     {

   55         return new VideoGameBuilder();

   56     }

   57 

   58     public VideoGameBuilder CreateGame(string title)

   59     {

   60         gameToBuild.Name = title;

   61         return this;

   62     }

   63 

   64     public VideoGameBuilder ManufacturedBy(Publisher manufacturer)

   65     {

   66         gameToBuild.Publisher = manufacturer;

   67         return this;

   68     }

   69 

   70     public VideoGameBuilder CreatedInYear(int year)

   71     {

   72         gameToBuild.YearPublished = year;

   73         return this;

   74     }

   75 

   76     public VideoGameBuilder TargetedForConsole(GameConsole console)

   77     {

   78         gameToBuild.GameConsole = console;

   79         return this;

   80     }

   81 

   82     public VideoGameBuilder InGenre(Genre genre)

   83     {

   84         gameToBuild.Genre = genre;

   85         return this;

   86     }

   87 

   88     public VideoGame Finish()

   89     {

   90         return this.gameToBuild;

   91     }

   92 }

This makes my tests read much better but even in say a presenter or service layer, I know exactly what my domain is creating for me and I think overall it's a better place to be for readability and maintainability.

This isn't anything new (but it's new to me and maybe to my readers). You can check out the start of it via Martin Fowler's post on fluent interfaces, and the followup post early this year on ExpressionBuilder which matches what the VideoGameBuilder class is doing above. And of course Rhino uses this type of syntax. I guess if I'm 6 months behind Martin Fowler in my thinkings, it's not a bad place to be.

8 Comments

  • Your class has a default constructor why not just set the individual properties your self. The game builder appears to provide no value, other the the ability to chain the calls to gather what is the point.

  • Umm, yeah, there are also these things called "properties."

  • That's a ton of work to put in (unless you generate this...) for what I consider minimal gain... you can always just wait a bit for C# 3.0's anonymous constructors.

    Oh, and to clean things up a bit you may consider ditching StartRecording/Finish and adding an implicit operator VideoGame to VideoGameBuilder.

  • @CodeMan/@Joe: Yes, there are properties and they would read well, but what if I want to pass in one value and set two? In the builder, I can take in a value, set a property and even combine values into readable methods.

    @Aaron: Agreed, and really this is just sample code for an idea, it's not meant to be production code.

    Guys, the point here (which was obviously missed probably due to my own inability to be expressive) is that you favor readability over creating parameters to a constructor or having to name properties (or even create them). Why must I expose the inner workings of an object to the outside world or maybe have to create properties that sound correct when I don't need to. It's the same as creating a named interface that simply wraps a generic. I would rather use the domain specific language of IVideoGameRental in my domain, than the programmer-speak of IInterface. The less code I have to crack open to actual read in order to gain intent and understanding, the better.

  • Hey Bil, some people get syntactic sugar, some don't. I'm picturing auto-generated builder classes generated from a class based on custom attributes on public or private properties that let the generator know which helper methods to create and what to name them. I think there is potential there.

  • I agree that in this case, the fluent interface isn't necessary. But like Bil said, it's not meant for production, and as an example of fluent interface design, it works well.

  • I'm sorry. You have got to be joking. You think the Builder class makes instantiating your object MORE readable??

  • @William: Yes. Can you tell me what's more readable:

    Person person = new Person(10, 100, "Abel");

    vs.

    Person person = PersonBuilder.
    StartRecording().
    CreateWithFirstName("Abel").
    IsOfTheAgeOf(10).
    WithNumberOfComputersInHouse(100).
    Finish();

    Okay, grant you could write:

    Person person = new Person();
    person.FirstName = "Abel";
    person.Age = 10;
    person.ComputersInHouse = 100;

    However I have to create properties for all of that and if I were to change those properties (maybe to be more descriptive, maybe to remove them) I would have to update all places where I have this code. With the Builder I can a) direct my developers to the Builder if they need to update how Person is created and b) my consuming code is more readable than parameterized constructors. I mean really, did you know what 10 and 100 were in the constructor before you saw the rest of the code? The less code I have to crack open, read, interpret, and understand the better.

    To each their own though.

Comments have been disabled for this content.