Return immutable list

I am making a class called Question . This class has answers, so I want to be able to return a list of the answers that come with it.

However, if the user makes changes to the response, I want the user to call the update method so that I can perform additional checks, etc. Right now, if the user gets a list of answers, he can still change the answer by saying question.getAnswers().get(0).setDescription("BLAH BLAH").

So, I thought about returning a copy of each answer, and let the user change this and he will have to merge / update it to the question. With this approach, I can guarantee that the answer is valid, but the equals method of the response is based on the description and correct field, and not in the id field, because I use JPA. If the user modifies the answer using this approach, the update method will not find the answer because the description field has changed and it is no longer equal, so it does not find it in the list.

Any tips?

 public void updateAnswer(Answer answer) { int index = answers.indexOf(answer); answers.set(index, answer); } public List<Answer> getAnswers() { return Collections.unmodifiableList(answers); } @Test public void shouldUpdateAnswerInQuestion() { // Get first answer, make an update on the description // and then update answer on question. Answer answerThatWillBeUpdated = question.getAnswers().get(0); String updatedAnswerDescription = "Hey, it is now updated!"; answerThatWillBeUpdated.setDescription(updatedAnswerDescription); question.updateAnswer(answerThatWillBeUpdated); // After updating check that the answer in the list is equal // to the answer updated. Answer answerFromList = question.getAnswers().get(0); assertEquals(answerThatWillBeUpdated, answerFromList); } 

Answer class:

 public class Answer { private long id; private String description; private Boolean correct; ... } 
+4
source share
4 answers

You should think about your application design, but since I'm not quite sure what the limitations are for your domain as a whole, I cannot offer a small redesigned design or something like that.

A simple and clear answer: the program is for the interface, not for implementation. If you want to force the call of the update method after changing the setter, consider using the Decorator pattern.

  • create interface
  • your particular class implements this interface (answer)
  • add a specific class (choose a better name but AnswerDecorator) that implements an interface that accepts a particular class in the constructor of the class mentioned in paragraph above

Then you simply delegate all the methods to the internal instance and for the methods you want to call the update, follow these steps:

public void setField (int a) {innerInstance.setField (a); Refresh (...); }

+1
source

Why not use the id field for comparison in the update method? When you give the user a copy of your response object, make sure that your copy has the same identifier as the current response object. Therefore, when the user changes the answer, your verification code can still find out which response was changed based on the id field.

0
source

As a rule, I know 2 solutions.

  • Return a deep copy of the list instead of the original list. In this case, any changes made by the user to the description of the response will have no effect. This solution is simple, but very inefficient in terms of performance and memory usage. It is also not user friendly. A good API should throw an exception when a user tries to perform a limited action.

  • Another solution is to return a list of non-modifiable objects, that is, objects that throw an exception when the user tries to call their setters.

There are several possible options for implementing this solution.

2.1. Change model class settings. All setters should be able to throw an exception if the object is created in read-only mode.

2.2. User wrapper pattern (decorator).

2.2.1. Create interfaces for all value objects and implement a wrapper for each class. For example, AnswerWrapper implements the Response interface, holds the Response type inside the wrapped object, delegates all methods except setters to the corresponding methods of this object, and throws an exception from each setter.

2.2.2. Use a dynamic proxy technique. You still need an interface, but you don't need to implement all setters for all classes. This solution is similar to aspects.

2.3. Use aspects. For example, AspectJ.

0
source

Client code has a list so that it knows the index of the list, tell me which one needs to be updated:

 public void updateAnswer(int index, Answer newAnswer) { answers.set(index, answer); } // ... Answer answerThatWillBeUpdated = question.getAnswers().get(0); String updatedAnswerDescription = "Hey, it is now updated!"; answerThatWillBeUpdated.setDescription(updatedAnswerDescription); question.updateAnswer(0,answerThatWillBeUpdated); 
0
source

Source: https://habr.com/ru/post/1397653/


All Articles