LGTM +1 for a fluent api.

I think it is worth to keep the original api there for a while, because
they provide some simplicity for users to write code.

Another question, does this new api also apply to c++?

On Mon, Jan 22, 2018 at 1:29 PM, Matteo Merli <mme...@apache.org> wrote:

> https://github.com/apache/incubator-pulsar/wiki/PIP-12:-
> Introduce-builder-for-creating-Producer-Consumer-Reader
>
> -------
>
>
> * **Status**: Proposal
> * **Author**: Matteo Merli
> * **Pull Request**: [ ]
> * **Mailing List discussion**:
>
> ## Motivation
>
> The current client API is using a mix of configuration object and "builder"
> pattern.
>
> `Client`, `Producer`, `Consumer` and `Reader` objects are created by
> passing
> an optional `ClientConfiguration` (`ProducerConfiguration`, ..) object. If
> that
> is not passed, all the default options are being used (same as passing
> `new ClientConfiguration()`).
>
> For example:
> ```java
> ClientConfiguration conf = new ClientConfiguration();
> conf.setIoThreads(8);
> PulsarClient client = PulsarClient.create("pulsar://localhost:6650",
> conf);
> ```
>
> On the contrary, when building a message to be published, we are following
> the
> builder pattern:
>
> ```java
> Message msg = MessageBuilder.create().setPayload(myPayload).build();
> producer.send(msg);
> ```
>
> The main problem with the configuration objects is that they need a lot of
> methods to be used. For example, we have multiple methods for creating a
> producer, depending on wether they accept a `conf` parameter or wether they
> are sync or async:
>
> ```java
> Producer createProducer(String topic);
> CompletableFuture<Producer> createProducerAsync(String topic);
>
> Producer createProducer(String topic,
>               ProducerConfiguration conf);
> CompletableFuture<Producer> createProducerAsync(
>               String topic, ProducerConfiguration conf);
> ```
>
> With that, we have 4 variations for creating a `Producer`, 4 for a
> `Consumer`
> and 4 for a `Reader`.
>
> If we want to add different ways to subscribe, for example subscribing to
> a set of topics at once, or by specifying a regular expression matching
> multiple topics, the number of methods in the `PulsarClient` API will just
> explode, making it very difficult for people to navigate.
>
> The same problem we would have for adding a type information at the API
> level, since some of the information included in the configuration might
> be typed as well (eg: the `MessageListener` on a `Consumer` is set on the
> `ConsumerConfiguration` but that would be depending on the "type" of the
> message).
>
> ## Proposal
>
> This proposal is to introduce builder style constructors for `Producer`,
> `Consumer`, `Reader` and also `PulsarClient` to have consistency across the
> board.
>
> ```java
> interface PulsarClient {
>     public static ClientBuilder builder() {
>        return new ClientBuilderImpl();
>    }
>
>     ProducerBuilder newProducer();
>     ConsumerBuilder newConsumer();
>     ReaderBuilder newReader();
>
>     // All other methods will be deprecated in 2.0
>     // and possibly removed in following versions
> }
> ```
>
> Another design choice made here is not treat the required arguments in a
> a special way.
>
> For example, when creating a `Producer`, the "topic" name is a required
> information and that's why the previous API was differentiating it from
> the optional configs:
>
>
> ```java
> client.createProducer(topic, conf);
> ```
>
> with the builder, topic will have to be set on the builder and its presence
> will be validated when we try to convert the builder into a proper
> `Producer`.
>
> ## Examples
>
> ```java
> Producer producer = client.newProducer()
>         .topic("my-topic")
>         .sendTimeout(30, TimeUnit.SECONDS)
>         .create();
> ```
>
> Similarly,
>
> ```java
> CompletableFuture<Producer> future = client.newProducer()
>         .topic("my-topic")
>         .createAsync();
> ```
>
>
> ### Consumers
>
> ```java
> Consumer consumer = client.newConsumer()
>         .topic("my-topic")
>         .subscriptionName("my-subscription")
>         .subscribe();
> ```
>
> or asynchronously:
>
> ```java
> CompletableFuture<Consumer> consumer = client.newConsumer()
>         .topic("my-topic")
>         .subscriptionName("my-subscription")
>         .subscribeAsync();
> ```
>
> When subscribing to a set of topics, this could be expressed
> as:
>
> ```java
> Consumer consumer = client.newConsumer()
>         .topicSet(myListOfTopics)
>         .subscriptionName("my-subscription")
>         .subscribe();
> ```
>
> or to a regex matching multiple topics:
>
> ```java
> Consumer consumer = client.newConsumer()
>         .topicPattern(myRegex)
>         .subscriptionName("my-subscription")
>         .subscribe();
> ```
>
> ## Type information
>
> When augmenting the API with the schema information, we could add the
> `Schema` before getting the builder instance.
>
> The reason for that is to have a "typed" builder that will create a
> typed `Producer`/`Consumer`.
>
> For that, the end result might look like:
>
> ```java
> // No type info specified
> Producer<byte[]> producer = client.newProducer()
>         .topic("my-topic")
>         .create();
>
>
> // Schema provided
> Producer<MyClass> producer = client
>         .newProducer(Schema.Json.of(MyClass.class))
>         .topic("my-topic")
>         .create();
> ```
>
> ## Concerns
>
> The only disadvantage with the new notation is that it makes the minimal
> example of code sligtly longer.
>
> ```java
> PulsarClient pulsarClient = PulsarClient.create("http://localhost:6650";);
>
> Producer producer =
> pulsarClient.createProducer("persistent://my-property/use/
> my-ns/my-topic");
> ```
>
> With this proposal, it would become:
>
> ```java
> PulsarClient client = PulsarClient.builder()
>         .serviceUrl("http://localhost:6650";)
>         .build();
>
> Producer producer = client.newProducer()
>         .topic("persistent://my-property/use/my-ns/my-topic")
>         .create();
> ```
>
> The positive side is that it will make it more intuitive to switch from
> the minimal example code to changing configuration options.
>
>
>
> --
> Matteo Merli
> <mme...@apache.org>
>

Reply via email to