Advertisement
FilipMalczak

lombok feature

Aug 15th, 2017
104
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.08 KB | None | 0 0
  1. The point of this issue is that internal static builder classes (results of @Builder annotation) should implement some common interface for easier generic handling.
  2.  
  3. # Use case
  4.  
  5. ## Context
  6.  
  7. Imagine that you have a async job that you want to configure with Properties file.
  8.  
  9. You'll probably want to have `Config` class with proper field:
  10.  
  11. ```
  12. // visibility modifiers, etc ommited for brevity
  13. @Builder
  14. class JobXConfig {
  15. long pollingTime;
  16. long maxPollingTime;
  17.  
  18. int maxRetries;
  19. }
  20. ```
  21.  
  22. You can implement it in a single class:
  23.  
  24. ```
  25. class JobXConfigurator {
  26. JobXConfig configure(Properties properties){
  27. JobXConfig.JobXConfigBuilder builder = JobXConfig.builder();
  28. builder.
  29. pollingTime(Long.parseLong(properties.getProperty("polling.time")).
  30. maxPollingTime(Long.parseLong(properties.getProperty("polling.time.max")).
  31. maxRetries(Long.parseLong(properties.getProperty("retries.max"));
  32. JobXConfig result = builder.build();
  33. return result;
  34. }
  35. }
  36. ```
  37.  
  38. But pretty soon this will be unmaintanable, so you're gonna split building logic - probably to methods.
  39.  
  40. 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.
  41.  
  42. ```
  43. // visibility modifiers, etc ommited for brevity
  44. @Builder
  45. class JobYConfig {
  46. String name;
  47.  
  48. int maxRetries;
  49. }
  50. ```
  51.  
  52. And here goes configurators (after refactor):
  53.  
  54. ```
  55. class JobXConfigurator {
  56. JobXConfig configure(Properties properties){
  57. JobXConfig.JobXConfigBuilder builder = JobXConfig.builder();
  58. configurePolling(builder, properties);
  59. configureRetries(builder, properties);
  60. JobXConfig result = builder.build();
  61. return result;
  62. }
  63.  
  64. void configurePolling(JobXConfig.JobXConfigBuilder builder, Properties properties){
  65. builder.
  66. pollingTime(Long.parseLong(properties.getProperty("polling.time")).
  67. maxPollingTime(Long.parseLong(properties.getProperty("polling.time.max"));
  68. }
  69.  
  70. void configureRetries(JobXConfig.JobXConfigBuilder builder, Properties properties){
  71. builder.maxRetries(Long.parseLong(properties.getProperty("retries.max"));
  72. }
  73. }
  74. ```
  75.  
  76. ```
  77. class JobYConfigurator {
  78. JobYConfig configure(Properties properties){
  79. JobYConfig.JobYConfigBuilder builder = JobYConfig.builder();
  80. configureName(builder, properties);
  81. configureRetries(builder, properties);
  82. JobYConfig result = builder.build();
  83. return result;
  84. }
  85.  
  86. void configureName(JobYConfig.JobYConfigBuilder builder, Properties properties){
  87. builder.name(properties.get("name"));
  88. }
  89.  
  90. void configureRetries(JobYConfig.JobYConfigBuilder builder, Properties properties){
  91. builder.maxRetries(Long.parseLong(properties.getProperty("retries.max"));
  92. }
  93. }
  94. ```
  95.  
  96. At some point there will be more common parts, so we either gonna extract some abstract class, or change it to responsibility chain:
  97.  
  98. ```
  99. interface SubConfigurator<BuilderClass, ConfigSource> {
  100. void configure(BuilderClass builder, ConfigSource source);
  101. }
  102.  
  103. @AllArgsConstructor
  104. class Configurator<ConfiguredType, ConfigSource>{
  105. @NonNull List<SubConfigurator<[BUILDER CLASS], ConfigSource>> subconfigurators;
  106.  
  107. ConfiguredType configure(ConfigSource source){
  108. [BUILDER CLASS] builder = [HOW DO WE DO THAT?];
  109. subconfigurators.forEach(subconfigurator -> subconfigurator.configure(builder, source));
  110. return builder.build();
  111. }
  112. }
  113. ```
  114.  
  115. As you see, it is impossible to keep type-safety in generics here.
  116.  
  117. I propose introducing `Builder` interface:
  118.  
  119. ```
  120. interface Builder<BuiltType> {
  121. BuiltType build();
  122. }
  123. ```
  124.  
  125. and `@BuilderOf (Class<?> clazz)` annotation that would be replaced in the same way as `val` and `var`.
  126.  
  127. This way abstraction above could become:
  128.  
  129. ```
  130. interface SubConfigurator<ConfiguredType, BuilderClass extends Builder<ConfiguredType>, ConfigSource> {
  131. void configure(BuilderClass builder, ConfigSource source);
  132. }
  133.  
  134. @AllArgsConstructor
  135. class Configurator<ConfiguredType, BuilderClass extends Builder<ConfiguredType>, ConfigSource>{
  136. @NonNull List<SubConfigurator<ConfiguredType, BuilderClass, ConfigSource>> subconfigurators;
  137.  
  138. ConfiguredType configure(ConfigSource source){
  139. BuilderClass builder = BuilderOf(ConfiguredType.class);
  140. subconfigurators.forEach(subconfigurator -> subconfigurator.configure(builder, source));
  141. return builder.build();
  142. }
  143. }
  144. ```
  145.  
  146. > 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.
  147.  
  148. # Lombok vs vanilla Java
  149.  
  150. On example of JobX and JobY configs:
  151.  
  152. ```
  153. // visibility modifiers, etc ommited for brevity
  154. @Builder
  155. class JobXConfig {
  156. long pollingTime;
  157. long maxPollingTime;
  158.  
  159. int maxRetries;
  160. }
  161. ```
  162.  
  163. ```
  164. // visibility modifiers, etc ommited for brevity
  165. @Builder
  166. class JobYConfig {
  167. String name;
  168.  
  169. int maxRetries;
  170. }
  171. ```
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement