In so many articles on Java's builder design pattern, it is implemented as follows:
public class YourModel {
// your fields here
private final long id;
//...
private YourModel(YourModelBuilder builder) {
// set everything from the builder
}
public static class YourModelBuilder {
// same fields from the model it is trying to build
private final long id;
//...
public YourModelBuilder(long id/* , .... */) {
// the normal construction pattern here...
this.id = id;
//...
}
// some builder methods for setting individual fields while allowing for chaining
public YourModel build() {
YourModel model = new YourModel(this);
// do validation here
return model;
}
}
}
or something similar.
This implementation of the design pattern seem to satisfy my use case, quickly and easily creating models manually for my Katalon Studio tests, in a way that is easy to understand, but it seems like it could end up a nightmare to maintain, especially given that the AUT these models are being created for, is constantly changing.
How can we abstract out the field declaration being copied from the model to the model builder?
CodePudding user response:
Let's specifically name the problem:
You'd want to be able to change the model class, by way of either:
- Adding a new field
- Removing an existing field
- Renaming an existing field
- Changing the type of an existing field
and do this in a way that as much of the infrastructure surrounding your model class (from the builder's 'setters' to your toString implementation) just automatically adapts without having to explicitly go in and fix things.
Java isn't that kind of flexible, resulting in drastic measures being required to do this properly. You have a few options here:
IDE tooling. Instead of just editing the java source file in what amounts to a 'dumb' editor, use tooling. For example, many IDEs support the notion of 'refactor -> rename' on a field which would also change the
this.foo = foo;
in your setter (if you had one). If you've never seen this, it's a bit magical so I better describe it: You select any identifier, hit the shortcut for 'refactor -> rename', and then a little highlight box appears around that identifier and all other places that identifier comes up. Your IDE is smart enough to know what scope is, and won't "select" different variables that so happen to share a name; this is not some sort of 'global search/replace'. You then start typing and what you type as if by magic appears in ALL those little boxes. You're doing a live search/replace on just those identifiers that actually refer to the thing you selected! Pretty nice, this is what IDEs are all about. They understand java and thus can do things like 'modify only those identifier nodes which actually refer to this thing'. Most do not change thesetFoo
method name tosetBar
, but some offer a popup asking if that's your intent. If an IDE tooling/refactor system is aware of builders, it could feasibly be written such that e.g. adding a field via a refactor action adds the field, updates the equals and hashCode method, the toString method, and fixes up the builder, all as-you-type, in one go. Let's call this explicit code generation. Note that at least for eclipse I'm not aware of refactor tools that go quite this far. More usually you'd just delete ALL the infra, update your model class, and then regenerate ALL the infra. If you forget, your code is broken. Point is: Such an IDE plugin could exist and isn't even that hard to build, so that is an option. I'm just not aware of any that actually do exist.Build tooling and templating. Have a system where you just write the model class and some hints as to what you want from it, and then have some aspect of the build tooling run with it: It takes your model class and the templating hints and generates all the infrastructure surrounding your needs automatically. This ensures that the infrastructure is always in sync with your model class and unlike the previous option, keeps your actual (non-generated) codebase nice and crisp. There are a few tools for this, but Project Lombok is the only one that works 'as you type', integrated directly into your eclipse process (e.g. the outline view updates as you type with e.g. the new builder setter method). The rest tend to work as annotation processors - you need a build cycle. Let's call this one implicit code generation.
Reflective bonanza is a third option: You make an actual model class and the builder appears at runtime. This, naturally, is not an option for java which is static and explicitly typed, but you can make this work if all code that interacts with your models is dynamic, for example because that's written in javascript or groovy or some such. I'm assuming this doesn't interest you so I won't go into further detail as to how to set this up. This solution is very common in dynamic languages. One way or another, you lose as-you-write introspection unless the reflective tool ships with IDE plugins at which point it's as complex as the first 2 solutions.
Pick your poison. I'd pick the implicit code gen option, and then I'd use the tool that most tightly integrates to keep productivity up by keeping 'waiting around for the build!' down to a minimum. But then, I am one of the core contributors to Project Lombok so I might be a tad biased :)
CodePudding user response:
Using reflection & Generic HashMap (field name & field value entries) in the Builder class but I don't think that solution will be much easier to maintain your code later because, in the builder class, you still have to expose setter methods for each model field so if there is any change on the model field, you still have to modify the builder class.
So keep it simple, follow the Builder pattern you already know.