> For Piotr's comment 4. I. I agree with Klou that this sounds rather like a > problem of the builder's usage than a builder problem. I think such a > scenario can easily be solved by introducing a transfer object.
It could be solved, but that would mean we have some kind of builder/factory/descriptor that creates a factory/builder that creates a final object. I think in this specific example I would prefer to add missing runtime parameters in the build/create() method instead of asking people implementing an operator to wrap it into two layers of indirection. However let’s not clutter this discussion, I might be missing something. Feel free to open a Jira tickets/start a new design discussion about this pattern (it’s currently partially implemented in the form of `StreamOperatorFactory` [1]). Piotrek [1] https://issues.apache.org/jira/browse/FLINK-11974 <https://issues.apache.org/jira/browse/FLINK-11974> > On 27 Aug 2019, at 14:43, Till Rohrmann <trohrm...@apache.org> wrote: > > Hi all, > > I would be in favour of the following convention > > 1. a) static method for builder creation > 2. No strict rule because especially for boolean flags it might make sense > to have something like `enableX()` or `withY()` where one doesn't specify a > concrete value. > 3. Mutable builders but if there is a good reason to follow the immutable > approach then I wouldn't forbid it. > 4. Private constructor if there is a builder and no backwards compatibility > constraint > 5. No setX methods on the instance to create > > For Piotr's comment 4. I. I agree with Klou that this sounds rather like a > problem of the builder's usage than a builder problem. I think such a > scenario can easily be solved by introducing a transfer object. > > Cheers, > Till > > On Tue, Aug 27, 2019 at 1:46 PM Timo Walther <twal...@apache.org> wrote: > >> Hi all, >> >> great to put this code style discussion on the mailing list because I >> also have found this style inconsistent in the past. >> >> Regarding Gyula's suggestions: >> 1. a static method `builder()` I think IDEs are also hightlight methods >> with this name >> 2. I would vote for a more declarative `propertyA(...).propertyB(...)` >> approach instead of setters because most methods should be setters. >> However, if implementers want to add methods such as `addField(..)`, >> `useProcessingTime()` this sounds also fine to me. >> 3. mutable >> Regarding Dawid's suggestions: >> 4. regarding required and optional parameters, I would allow both options >> 5. always end with `build()` >> >> Thanks, >> Timo >> >> On 27.08.19 10:04, Kostas Kloudas wrote: >>> Hi all, >>> >>> I agree with Arvid, although for point 2 I would be less strict. >>> >>> @Piotr, for the side note you mentioned, and from the description you >>> mention in the mail for example I, >>> it seems that the need to pass parameters in the build() is not an >>> inherent need of the build pattern but it >>> can be mitigated by just creating sth like a StreamOperatorConfig (and >>> not the operator builder itself) on the >>> client, serialize it, and then at the TM, use the actual >>> StreamOperator builder with that config to create the >>> operator. There you can have all the needed parameters and also >>> perform the validation that >>> Dawid mention. >>> >>> Again, this is just from the summary you provided, not from looking at >>> the code, so I may be missing something. >>> >>> On the validation, I think that it should happen at the build(), as >>> this is the only place where >>> we know all the parameters. >>> >>> Cheers, >>> Kostas >>> >>> On Tue, Aug 27, 2019 at 9:47 AM Arvid Heise <ar...@data-artisans.com> >> wrote: >>>> Hi all, >>>> >>>> I'd like to differentiate between API level builder usage and "internal" >>>> builder usage (for example, test harness). >>>> >>>> For API level builder, in general everything goes, as long as it aligns >>>> with user expectations. API level usages are also much more discussed, >> such >>>> that I'd expect them to be consistent within one API. This freedom is >>>> especially required when considering APIs for non-java languages. >>>> >>>> Now for "internal" usages (which may or may not align with Java >> Datastream >>>> etc. usage). I'd like to get a style that is well supported by the >>>> primarily used IDEs. I don't want to write a new builder from scratch >> and >>>> by the looks of it, we will get many more builders. >>>> >>>> Furthermore, I'd like to emphasize that the primary use case for using >>>> builders for me is to mitigate the lack of named arguments in Java, >> which >>>> is especially painful for immutable types. In an ideal world, I'd like >> to >>>> have all classes immutable and use builders to create new instances if >> we >>>> have >>>> a) too many parameters to be passed (which should become many more once >> we >>>> commit even more to DI), >>>> b) we have meaningful default values, such that we can omit a >> significant >>>> amount parameters by using a buidler, or >>>> c) we have a good amount of optional (=nullable) parameters. >>>> Obviously, we deviate from that whenever performance considerations >> demand >>>> it. >>>> >>>> With that my votes for the questions: >>>> 1. static method only, hide builder ctor >>>> 2. Intellij and avro use setX() for property X, so I'd go with that. >> Lombok >>>> just uses X(), so I wouldn't mind it. >>>> 3. Mutable approach. Immutable doesn't make much sense to me. Then I can >>>> directly go with a Wither pattern on the immutable class without >> builder. >>>> 4. Private ctor. If it has a builder, it should be used. Exception: >>>> migration support for some intermediate versions (if we added a builder >> to >>>> a class, keep the ctor deprecated public for 1,2 versions). >>>> 5. no setX in general, we want to have an immutable class. Exceptions >> where >>>> due (should be mostly for performance reasons). >>>> >>>> Best, >>>> >>>> Arvid >>>> >>>> On Mon, Aug 26, 2019 at 4:40 PM Piotr Nowojski <pi...@ververica.com> >> wrote: >>>> >>>>> Hi, >>>>> >>>>> I agree with Dawid, modulo that I don’t have any preference about >> point 2 >>>>> - I’m ok even with not enforcing this. >>>>> >>>>> One side note about point 4. There are use cases where passing >> obligatory >>>>> parameters in the build method itself might make sense: >>>>> >>>>> I. - when those parameters can not be or can not be easily passed via >> the >>>>> constructor. Good example of that is “builder” pattern for the >>>>> StreamOperators (StreamOperatorFactory), where factory is constructed >> on >>>>> the API level in the client, then it’s being serialised and sent over >> the >>>>> network and reconstructed on the TaskManager, where StreamOperator is >>>>> finally constructed. The issue is that some of the obligatory >> parameters >>>>> are only available on the TaskManager, so they can not be passed on a >>>>> DataStream level in the client. >>>>> II. - when builder might be used to create multiple instances of the >>>>> object with different values. >>>>> >>>>> Piotrek >>>>> >>>>>> On 26 Aug 2019, at 15:12, Jark Wu <imj...@gmail.com> wrote: >>>>>> >>>>>> Hi Gyula, >>>>>> >>>>>> Thanks for bringing this. I think it would be nice if we have a common >>>>>> approach to create builder pattern. >>>>>> Currently, we have a lot of builders but with different tastes. >>>>>> >>>>>>> 1. Creating the builder objects: >>>>>> I prefer option a) too. It would be easier for users to get the >> builder >>>>>> instance. >>>>>> >>>>>>> 2. Setting properties on the builder: >>>>>> I don't have a preference for it. But I think there is another option >>>>> might >>>>>> be more concise, i.e. "something()" without `with` or `set` prefix. >>>>>> For example: >>>>>> >>>>>> CsvTableSource source = new CsvTableSource.builder() >>>>>> .path("/path/to/your/file.csv") >>>>>> .field("myfield", Types.STRING) >>>>>> .field("myfield2", Types.INT) >>>>>> .build(); >>>>>> >>>>>> This pattern is heavily used in flink-table, e.g. `TableSchema`, >>>>>> `TypeInference`, `BuiltInFunctionDefinition`. >>>>>> >>>>>>> 3. Implementing the builder object: >>>>>> I prefer b) Mutable approach which is simpler from the implementation >>>>> part. >>>>>> >>>>>> Besides that, I think maybe we can add some other aspects: >>>>>> >>>>>> 4. Constructor of the main class. >>>>>> a) private constructor >>>>>> b) public constructor >>>>>> >>>>>> 5. setXXX methods of the main class >>>>>> a) setXXX methods are not allowed >>>>>> b) setXXX methods are allowed. >>>>>> >>>>>> I prefer both option a). Because I think one of the reason to have the >>>>>> builder is that we don't want the constructor public. >>>>>> A public constructor makes it hard to maintain and evolve compatibly >> when >>>>>> adding new parameters, FlinkKafkaProducer is a good example. >>>>>> For set methods, I think in most cases, we want users to set the >> fields >>>>>> eagerly (through the builder) and `setXXX` methods on the main class >>>>>> is duplicate with the methods on the builder. We should avoid that. >>>>>> >>>>>> >>>>>> Regards, >>>>>> Jark >>>>>> >>>>>> >>>>>> On Mon, 26 Aug 2019 at 20:18, Gyula Fóra <gyula.f...@gmail.com> >> wrote: >>>>>> >>>>>>> Hi All! >>>>>>> >>>>>>> I would like to start a code-style related discussion regarding how >> we >>>>>>> implement the builder pattern in the Flink project. >>>>>>> >>>>>>> It would be the best to have a common approach, there are some >> aspects >>>>> of >>>>>>> the pattern that come to my mind please feel free to add anything I >>>>> missed: >>>>>>> 1. Creating the builder objects: >>>>>>> >>>>>>> a) Always create using static method in "built" class: >>>>>>> Here we should have naming guidelines: .builder(..) or >>>>>>> .xyzBuilder(...) >>>>>>> b) Always use builder class constructor >>>>>>> c) Mix: Again we should have some guidelines when to use which >>>>>>> >>>>>>> I personally prefer option a) to always have a static method to >> create >>>>> the >>>>>>> builder with static method names that end in builder. >>>>>>> >>>>>>> 2. Setting properties on the builder: >>>>>>> >>>>>>> a) withSomething(...) >>>>>>> b) setSomething(...) >>>>>>> c) other >>>>>>> >>>>>>> I don't really have a preference but either a or b for consistency. >>>>>>> >>>>>>> >>>>>>> 3. Implementing the builder object: >>>>>>> >>>>>>> a) Immutable -> Creates a new builder object after setting a property >>>>>>> b) Mutable -> Returns (this) after setting the property >>>>>>> >>>>>>> I personally prefer the mutable version as it keeps the builder >>>>>>> implementation much simpler and it seems to be a very common way of >>>>> doing >>>>>>> it. >>>>>>> >>>>>>> What do you all think? >>>>>>> >>>>>>> Regards, >>>>>>> Gyula >>>>>>> >>>>> >>>> -- >>>> >>>> Arvid Heise | Senior Software Engineer >>>> >>>> <https://www.ververica.com/> >>>> >>>> Follow us @VervericaData >>>> >>>> -- >>>> >>>> Join Flink Forward <https://flink-forward.org/> - The Apache Flink >>>> Conference >>>> >>>> Stream Processing | Event Driven | Real Time >>>> >>>> -- >>>> >>>> Ververica GmbH | Invalidenstrasse 115, 10115 Berlin, Germany >>>> >>>> -- >>>> Ververica GmbH >>>> Registered at Amtsgericht Charlottenburg: HRB 158244 B >>>> Managing Directors: Dr. Kostas Tzoumas, Dr. Stephan Ewen >> >> >>