问题
I am making a first attempt at writing fluent builders in C# so I can simplify the logic for creating object graphs.
My first thought was to create a fluent builder for each class, then nest them hierarchically like this:
School school = SchoolBuilder.New()
.WithName("Whatchamatta U")
.AddClass(
ClassBuilder.New()
.WithName("Arithmetic")
.WithClassNumber("101")
.AddStudent(
StudentBuilder.New()
.WithName("John Smith")
)
.AddStudent(
StudentBuilder.New()
.WithName("Jane Smith")
)
)
.Save()
This is straightforward to implement as a state machine using C# interfaces, and fairly easy to understand, both as a developer trying to read the code AND as a developer trying to create new code ("With*"=set a property, "Add*"=add a child object, "Save" does whatever needs to be done to create the graph), but I thought it might be easier for a developer to read and write a DSL that looks something like this:
School school = SchoolBuilder.New()
.WithName("Whatchamatta U")
.AddClass()
.WithClassName("Arithmetic")
.WithClassNumber("101")
.AddStudent()
.WithStudentName("John Smith")
.AddStudent()
.WithStudentName("Jane Smith")
.Save()
Since the messages for all builders are going through the topmost parent builder object, I think I need to find some way to route them down the hierarchy to the appropriate child builder object. In Ruby this can be done with method_missing, but I can't see an smart way to implement this in C#.
An ugly solution would be to have each parent implement the interface of every potential descendent object, where each method routes its invocation to the child. Obviously this is not desirable, especially as I add more builder classes to the hierarchy, but I don't know of a concise way to "virtually" implement these interfaces at compile time but intercept all calls to that interface and proxy them down the tree, à la method_missing. I can see that there are ways to dynamically implement interfaces at runtime in C# using a dynamic proxy but I don't think any of these will support intellisense.
My requirements are that a) this works with intellisense and b) any builder can be used to build objects without reference to its parent---meaning I want all builders to be able to handle the construction of its child-builders (meaning the the ugly solution would be really ugly)
Is there a way to take this second route in C#? Or is this the wrong way to think about it?
回答1:
An alternative might be to use the object initializer syntax instead. So something like:
School school = new School {
Name = "Watchamatta U",
Classes = new[] {
new Class {
Name = "Arithmetic",
Number = "101",
Students = new[] {
// etc.
}
}
}
};
This initializes properties, so you can change the arrays into objects.
来源:https://stackoverflow.com/questions/8072492/designing-fluent-builders-to-create-an-object-graph-in-c-sharp