Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- The point of this issue is that internal static builder classes (results of @Builder annotation) should implement some common interface for easier generic handling.
- # Use case
- ## Context
- Imagine that you have a async job that you want to configure with Properties file.
- You'll probably want to have `Config` class with proper field:
- ```
- // visibility modifiers, etc ommited for brevity
- @Builder
- class JobXConfig {
- long pollingTime;
- long maxPollingTime;
- int maxRetries;
- }
- ```
- You can implement it in a single class:
- ```
- class JobXConfigurator {
- JobXConfig configure(Properties properties){
- JobXConfig.JobXConfigBuilder builder = JobXConfig.builder();
- builder.
- pollingTime(Long.parseLong(properties.getProperty("polling.time")).
- maxPollingTime(Long.parseLong(properties.getProperty("polling.time.max")).
- maxRetries(Long.parseLong(properties.getProperty("retries.max"));
- JobXConfig result = builder.build();
- return result;
- }
- }
- ```
- But pretty soon this will be unmaintanable, so you're gonna split building logic - probably to methods.
- Anyway, now it happens so that we have 2 jobs - both are retriable, but one uses polling and another one has a name, but are retriable.
- ```
- // visibility modifiers, etc ommited for brevity
- @Builder
- class JobYConfig {
- String name;
- int maxRetries;
- }
- ```
- And here goes configurators (after refactor):
- ```
- class JobXConfigurator {
- JobXConfig configure(Properties properties){
- JobXConfig.JobXConfigBuilder builder = JobXConfig.builder();
- configurePolling(builder, properties);
- configureRetries(builder, properties);
- JobXConfig result = builder.build();
- return result;
- }
- void configurePolling(JobXConfig.JobXConfigBuilder builder, Properties properties){
- builder.
- pollingTime(Long.parseLong(properties.getProperty("polling.time")).
- maxPollingTime(Long.parseLong(properties.getProperty("polling.time.max"));
- }
- void configureRetries(JobXConfig.JobXConfigBuilder builder, Properties properties){
- builder.maxRetries(Long.parseLong(properties.getProperty("retries.max"));
- }
- }
- ```
- ```
- class JobYConfigurator {
- JobYConfig configure(Properties properties){
- JobYConfig.JobYConfigBuilder builder = JobYConfig.builder();
- configureName(builder, properties);
- configureRetries(builder, properties);
- JobYConfig result = builder.build();
- return result;
- }
- void configureName(JobYConfig.JobYConfigBuilder builder, Properties properties){
- builder.name(properties.get("name"));
- }
- void configureRetries(JobYConfig.JobYConfigBuilder builder, Properties properties){
- builder.maxRetries(Long.parseLong(properties.getProperty("retries.max"));
- }
- }
- ```
- At some point there will be more common parts, so we either gonna extract some abstract class, or change it to responsibility chain:
- ```
- interface SubConfigurator<BuilderClass, ConfigSource> {
- void configure(BuilderClass builder, ConfigSource source);
- }
- @AllArgsConstructor
- class Configurator<ConfiguredType, ConfigSource>{
- @NonNull List<SubConfigurator<[BUILDER CLASS], ConfigSource>> subconfigurators;
- ConfiguredType configure(ConfigSource source){
- [BUILDER CLASS] builder = [HOW DO WE DO THAT?];
- subconfigurators.forEach(subconfigurator -> subconfigurator.configure(builder, source));
- return builder.build();
- }
- }
- ```
- As you see, it is impossible to keep type-safety in generics here.
- I propose introducing `Builder` interface:
- ```
- interface Builder<BuiltType> {
- BuiltType build();
- }
- ```
- and `@BuilderOf (Class<?> clazz)` annotation that would be replaced in the same way as `val` and `var`.
- This way abstraction above could become:
- ```
- interface SubConfigurator<ConfiguredType, BuilderClass extends Builder<ConfiguredType>, ConfigSource> {
- void configure(BuilderClass builder, ConfigSource source);
- }
- @AllArgsConstructor
- class Configurator<ConfiguredType, BuilderClass extends Builder<ConfiguredType>, ConfigSource>{
- @NonNull List<SubConfigurator<ConfiguredType, BuilderClass, ConfigSource>> subconfigurators;
- ConfiguredType configure(ConfigSource source){
- BuilderClass builder = BuilderOf(ConfiguredType.class);
- subconfigurators.forEach(subconfigurator -> subconfigurator.configure(builder, source));
- return builder.build();
- }
- }
- ```
- > I am not sure whether it's possible to replace right-hand-side of assignment statement the way that val and var work, so we may need to add `Supplier<Builder<T>>` field to `Configurator` class.
- # Lombok vs vanilla Java
- On example of JobX and JobY configs:
- ```
- // visibility modifiers, etc ommited for brevity
- @Builder
- class JobXConfig {
- long pollingTime;
- long maxPollingTime;
- int maxRetries;
- }
- ```
- ```
- // visibility modifiers, etc ommited for brevity
- @Builder
- class JobYConfig {
- String name;
- int maxRetries;
- }
- ```
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement