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.