0

I have a class Data with list and two variable (id and name). I am replacing the variable with list values. First time in the list will be the new values of the id and name. After removing the id and name, the request object will contain only dataList. But the issue is the variable is being used in many places. I am thinking of creating the setter of the id and name properties in the Request object.

I created the getter and but not sure how to create the setter such that I add items to list for the same object. Also, how to override builder for those two fields alone. Assume the request object is big as well.

Class Request

    @Builder
    @Getter
    public class Request {
        private String id; //need to removed
        private String name; //need to removed
        private List<Data> dataList; 

        public String getId() { 
            return (dataList != null) ? dataList.get(0).getId() : null;
        }

        public String getName() {
            return (dataList != null) ? dataList.get(0).getName() : null;
        }
    }

Class Data:

public class Data {
    private String id;
    private String name;
}

When someone calls setId or setName, then the item at index 0 in the dataList should be updated.

How do I do the same for builder ?

user3407267
  • 1,524
  • 9
  • 30
  • 57
  • Are you saying that when someone calls `setId` or `setName`, then the item at index `0` in the `dataList` should be updated? – Gautham M Aug 21 '21 at 16:56
  • @GauthamM Yes!! – user3407267 Aug 23 '21 at 05:38
  • *but the issue is the variable is being used in many places.* - which variable is used in many places?; *After removing the id and name* - you probably want to remove `Data` instance, and not `id` and `name`, per se. It's not clear what is the issue and what you want to achieve.. – Giorgi Tsiklauri Aug 23 '21 at 06:33
  • Id and name from request object is being used in many places. I want to make it use the dataList – user3407267 Aug 23 '21 at 06:38
  • Why not to create their copies then? also, if you're worried about removing `Data` instance from the list, it won't dereference (remove) `id` and `name`, as the latter exist es separate objects. It's still unclear what you want to achieve.. – Giorgi Tsiklauri Aug 23 '21 at 06:39
  • I am not trying to remove Data instance, I am just trying to remove id and name from Request obj. – user3407267 Aug 23 '21 at 06:42
  • @user3407267 You can see [this](https://stackoverflow.com/a/67401875/7804477) answer to override the default behaviour of builder methods – Gautham M Aug 23 '21 at 09:42

3 Answers3

2

Overriding Lombok's setter doesn't change the builder's behavior, you have to override them both. Let's start with the harder one:

Builder

Lombok's builder is easy to be overridden by defining the skeleton of the builder. The existing parts won't be generated and Lombok only completes the builder. Create athisstatic nested class in Request:

public static class RequestBuilder {

    public final RequestBuilder id(final String id) {
        this.id = id;
        updateData(id, name, dataList);
        return this;
    }

    public final RequestBuilder name(final String name) {
        this.name = name;
        updateData(id, name, dataList);
        return this;
    }
}

What is updateData? You need to update the dataList on each builder's method call. The method must be static otherwise the static builder cannot access it. Define it right in the Request class:

private static void updateData(final String id, final String name, List<Data> dataList) {

    if (dataList == null) {
        dataList = new ArrayList<>();
    }
    if (dataList.isEmpty()) {
        dataList.add(new Data(id, name));
    } else {
        var data = dataList.get(0);
        data.setId(id);
        data.setName(name);
    }
}

Your case when dataList is null is not handled, so I rather initialize it here for sure (hence the field cannot be final among the formal parameters of the method).

Setter

This is easy, you need to do basically the same thing like in the builder - just override the correct methods:

public final void setId(final String id) {
    this.id = id;
    updateData(id, getName(), dataList);
}

public final void setName(final String name) {
    this.name = name;
    updateData(getId(), name, dataList);
}

You are all set. For sake of simplicity, I have annotated the class Data with Lombok annotations @lombok.Data (beware of the name) and @AllArgsConstructor.

Test

It's always a good practice to write at least some units tests to cover and verify the behavior. I needed to annotate Request with @AllArgsConstructor to avoid calling the setters which are subjects of the test. I also would need some helpful methods for assertion and eliminating code duplication:

void assertRequestBeforeTest(final Request request) {
    assertThat(request.getId(), nullValue());
    assertThat(request.getName(), nullValue());
    assertThat(request.getDataList(), hasSize(0));
}

void assertRequestAfterTest(final Request request, final String id, final String name) {
    assertThat(request.getId(), is(id));
    assertThat(request.getName(), is(name));
    assertThat(request.getDataList(), notNullValue());
    var data = request.getDataList().get(0);
    assertThat(data, notNullValue());
    assertThat(data.getId(), is(id));
    assertThat(data.getName(), is(name));
}

And the tests:

@Test
void setter_onNullFields() {
    var request = new Request(null, null, new ArrayList<>());
    assertRequestBeforeTest(request);
    request.setId("id-new");
    request.setName("name-new");
    assertRequestAfterTest(request, "id-new", "name-new");
}

@Test
void setter_onExistingFields() {
    var request = new Request("id", "name", new ArrayList<>());
    assertRequestBeforeTest(request);
    request.setId("id-new");
    request.setName("name-new");
    assertRequestAfterTest(request, "id-new", "name-new");
}

@Test
void builder() {
    var requestBuilder = Request.builder().dataList(new ArrayList<>());
    var request = requestBuilder.id("id-new").name("name-new").build();
    assertRequestAfterTest(request, "id-new", "name-new");
}
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
  • Thank you for the detailed explanation. When you said - "The existing parts won't be generated and Lombok only completes the builder. " Do I need to create similar RequestBuilder for all the fileds ? – user3407267 Aug 23 '21 at 06:51
  • (1/2) The `RequestBuilder` should be defined only once, however, you need to define one method for each param handled in such a way as `id` and `name`. It might become clumsy and error-prone in the case of a large number of parameters. I would say the design of such a class is not good as it relies only on the first item in `dataList`. A better solution IMHO would be to take time to refactor the setter/builder usages (or at least get rid of the builder) or create a single update method using reflection and refactor all the setters to use it instead. – Nikolas Charalambidis Aug 23 '21 at 06:57
  • (2/2) I believe the question is answered as both Lombok setters and builders can be overridden. The design is yet another issue and I would rather take time to refactor the usages than implement such mechanism on top of a large class. – Nikolas Charalambidis Aug 23 '21 at 06:58
1

You only need to override the set method and set values within the method

public void setId(String id) {
    if (dataList == null) {
        dataList = new ArrayList(2);
    }
    if (null == dataList.get(0)) {
        dataList.add(0, new Data());
    }
    dataList.get(0).setId(id);
}
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
xuanzjie
  • 51
  • 7
0

If many places are calling the Request object. You can replace the setter of the id and name properties in the Request object. When rewriting the setter method of the id and name properties in the Request object, you can call the setter method of the Data object to write

Nelson L
  • 31
  • 2
  • That my question is - how to create setter just tat I add items to first item to list. should it be a synchrozied ? so that the id and name should be part of same obect and first list item – user3407267 Aug 20 '21 at 19:50