I'd like to know whether my implementation of QuestionBuilder violates mutability.
public class Question<T extends Serializable> implements Serializable {
private QuestionHolder<T> questionHolder;
private Question(QuestionHolder<T> questionHolder) {
this.questionHolder = questionHolder;
}
public String getId() {
return questionHolder.id;
}
public int getOrder() {
return questionHolder.order;
}
public QuestionType getType() {
return questionHolder.type;
}
public boolean isImmediate() {
return questionHolder.immediate;
}
public boolean isMandatory() {
return questionHolder.mandatory;
}
public List<T> getSelectedValues() {
return questionHolder.selectedValues;
}
public List<T> getPossibleValues() {
return questionHolder.possibleValues;
}
private static final class QuestionHolder<T extends Serializable> {
private String id;
private int order = 0;
private QuestionType type;
private boolean immediate;
private boolean mandatory;
private List<T> selectedValues;
private List<T> possibleValues;
}
public static final class QuestionBuilder<T extends Serializable> implements Builder<Question<T>> {
private QuestionHolder<T> questionHolder;
public QuestionBuilder(String id) {
questionHolder = new QuestionHolder<>();
questionHolder.id = id;
}
public QuestionBuilder withOrder(int order) {
questionHolder.order = order;
return this;
}
public QuestionBuilder withType(QuestionType questionType) {
questionHolder.type = questionType;
return this;
}
public QuestionBuilder withImmediate(boolean immediate) {
questionHolder.immediate = immediate;
return this;
}
public QuestionBuilder withMandatory(boolean mandatory) {
questionHolder.mandatory = mandatory;
return this;
}
public QuestionBuilder withSelectedValues(List<T> selectedValues) {
questionHolder.selectedValues = selectedValues;
return this;
}
public QuestionBuilder withPossibleValues(List<T> possibleValues) {
questionHolder.possibleValues = possibleValues;
return this;
}
public Question<T> build() {
Question<T> question = new Question<>(questionHolder);
questionHolder = null;
return question;
}
}
}
Or what should I adjust in order to resolve mutability issue. Any suggestions?
If you're worried about thread safety, then your code here is not necessarily thread safe.
It is possible that one thread calls build() and returns a Question pointing to a QuestionHolder. Even though build() sets the holder to null, another thread might not see that null, but instead see the old value of the field. If that other thread called any of your setters, it would potentially mutate the Holder that the Question class had already accessed.
In a single threaded application you would be fine.
As far as I can see, you are mutating the QuestionHolder with each builder call.
What I would do is:
1) Make all properties inside QuestionHolder private and don't create any setters at all.
2) Store each property inside the builder instance and create a new instance of QuestionHolder in the build method of the builder.
For example:
public Question<T> build() {
// DO ALL THE VALIDATIONS NEEDED
QuestionHolder holder = new QuestionHolder(id, order, type, inmediate, mandatory, selectedValues, possibleValues);
return new Question<>(questionHolder);
}
With these approach, you will be mutating the Builder, but that's ok for the Builder Pattern. You will obviously need to create a new Builder instance each time you want to create a Question. If you want to use the same Builder over and over again you will probably need to store some kind of structure inside it (a Map identified by Id, for example).
Related
I'm currently doing dev on an inherited Spring boot app, part of it is sending an API POST request with the boolean of whether a soccer match is finished or not (resulted). I noticed that the design of the class was such:
//parent class
public class Fixture {
private final FixtureType type;
private final Team homeTeam;
private final Team awayTeam;
public Fixture(#JsonProperty("type") final FixtureType type,
#JsonProperty("homeTeam") final Team homeTeam,
#JsonProperty("awayTeam") final Team awayTeam
) {
this.type = type;
this.homeTeam = homeTeam;
this.awayTeam = awayTeam;
}
public boolean isResulted() {
return false;
}
/*
other methods
*/
}
//child class
public class Result extends Fixture {
private final Outcome outcome;
public Result(#JsonProperty("type") final FixtureType type,
#JsonProperty("homeTeam") final Team homeTeam,
#JsonProperty("awayTeam") final Team awayTeam,
#JsonProperty("outcome") final Outcome outcome) {
super(type, homeTeam, awayTeam);
this.outcome = outcome;
}
#Override
public boolean isResulted() {
return true;
}
/*
other methods
*/
}
In the Swagger documentation, the request specifies that "resulted": true needs to be a field in the JSON POST request. Now I can add that field into the constructor, but that would mean changing a load of tests and code that calls this constructor. My solution was to call the isResulted() method in the constructor itself. I've never done this before, but this works. Is there any reason that this design below would create issues in the long run?
public class Result extends Fixture {
private final boolean resulted;
public Result (){
super();
resulted = isResulted();
}
#Override
#JsonProperty("resulted")
public boolean isResulted() {
return true;
}
}
I don't understand what's the purpose of having a private field that is not used anywhere. I'm also not sure I understand the problem you'd like to have solved.
There's a possible approach that is both more flexible and compatible with your previous code:
public class Result extends Fixture {
private final boolean resulted;
public Result (boolean resulted){
super();
this.resulted = resulted;
}
public Result (){
this(true); // sets the default value
}
#Override
#JsonProperty("resulted")
public boolean isResulted() {
return resulted;
}
}
Room is not finding setType method defined in the parent class. Gives cannot find setter for field error during compilation.
Parent class
public class Data {
private int type = -1;
public Data() {
}
public int getType() {
return type;
}
public Data setType(int type) {
this.type = type;
return this;
}
}
Child class
#Entity
public class Log extends Data {
#PrimaryKey(autoGenerate = true)
public int id;
public Log() {
}
}
Usually setters do not return values.
Change your setType() method to:
public void setType(int type) {
this.type = type;
}
P.S. Obviously, returning the same instance of Data object is useless here, since you're invoking the method on that object and already have it.
If you want to keep the builder pattern, you could consider using an internal static class for that matter as follows (you don't need an empty constructor, that's added implicitly):
public class Data {
private int type = -1;
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public static class Builder {
private Data data = new Data();
public Builder setType(int type) {
data.setType(type);
return this;
}
public Data build() {
return data;
}
}
}
Now, for creating a data class you could do:
Data data = new Data.Builder()
.setType(10)
.build();
I'm using spring data (mongoDb) and I've got my repository:
public interface StoriesRepository extends PagingAndSortingRepository<Story, String> {}
Then i have a controller:
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Page<StoryResponse>> getStories(Pageable pageable) {
Page<StoryResponse> stories = storiesRepository.findAll(pageable).map(StoryResponseMapper::toStoryResponse);
return ResponseEntity.ok(stories);
}
Everything works fine, but I can't consume my endpoint using RestTemplate getForEntity method:
def entity = restTemplate.getForEntity(getLocalhost("/story"), new TypeReference<Page<StoryResponse>>(){}.class)
What class should I provide to successfully deserialize my Page of entities?
new TypeReference<Page<StoryResponse>>() {}
The problem with this statement is that Jackson cannot instantiate an abstract type. You should give Jackson the information on how to instantiate Page with a concrete type. But its concrete type, PageImpl, has no default constructor or any #JsonCreators for that matter, so you can not use the following code either:
new TypeReference<PageImpl<StoryResponse>>() {}
Since you can't add the required information to the Page class, It's better to create a custom implementation for Page interface which has a default no-arg constructor, as in this answer. Then use that custom implementation in type reference, like following:
new TypeReference<CustomPageImpl<StoryResponse>>() {}
Here are the custom implementation, copied from linked question:
public class CustomPageImpl<T> extends PageImpl<T> {
private static final long serialVersionUID = 1L;
private int number;
private int size;
private int totalPages;
private int numberOfElements;
private long totalElements;
private boolean previousPage;
private boolean firstPage;
private boolean nextPage;
private boolean lastPage;
private List<T> content;
private Sort sort;
public CustomPageImpl() {
super(new ArrayList<>());
}
#Override
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
#Override
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
#Override
public int getTotalPages() {
return totalPages;
}
public void setTotalPages(int totalPages) {
this.totalPages = totalPages;
}
#Override
public int getNumberOfElements() {
return numberOfElements;
}
public void setNumberOfElements(int numberOfElements) {
this.numberOfElements = numberOfElements;
}
#Override
public long getTotalElements() {
return totalElements;
}
public void setTotalElements(long totalElements) {
this.totalElements = totalElements;
}
public boolean isPreviousPage() {
return previousPage;
}
public void setPreviousPage(boolean previousPage) {
this.previousPage = previousPage;
}
public boolean isFirstPage() {
return firstPage;
}
public void setFirstPage(boolean firstPage) {
this.firstPage = firstPage;
}
public boolean isNextPage() {
return nextPage;
}
public void setNextPage(boolean nextPage) {
this.nextPage = nextPage;
}
public boolean isLastPage() {
return lastPage;
}
public void setLastPage(boolean lastPage) {
this.lastPage = lastPage;
}
#Override
public List<T> getContent() {
return content;
}
public void setContent(List<T> content) {
this.content = content;
}
#Override
public Sort getSort() {
return sort;
}
public void setSort(Sort sort) {
this.sort = sort;
}
public Page<T> pageImpl() {
return new PageImpl<>(getContent(), new PageRequest(getNumber(),
getSize(), getSort()), getTotalElements());
}
}
I know this thread is a little old, but hopefully someone will benefit from this.
#Ali Dehghani's answer is good, except that it re-implements what PageImpl<T> has already done. I considered this to be rather needless. I found a better solution by creating a class that extends PageImpl<T> and specifies a #JsonCreator constructor:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.company.model.HelperModel;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import java.util.List;
public class HelperPage extends PageImpl<HelperModel> {
#JsonCreator
// Note: I don't need a sort, so I'm not including one here.
// It shouldn't be too hard to add it in tho.
public HelperPage(#JsonProperty("content") List<HelperModel> content,
#JsonProperty("number") int number,
#JsonProperty("size") int size,
#JsonProperty("totalElements") Long totalElements) {
super(content, new PageRequest(number, size), totalElements);
}
}
Then:
HelperPage page = restTemplate.getForObject(url, HelperPage.class);
This is the same as creating a CustomPageImpl<T> class but allows us to take advantage of all the code that's already in PageImpl<T>.
As "pathfinder" mentioned you can use exchange method of RestTemplate. However instead of passing ParameterizedTypeReference<Page<StoryResponse>>() you should pass ParameterizedTypeReference<PagedResources<StoryResponse>>(). When you get the response you could retrieve the content - Collection<StoryResponse>.
The code should look like this:
ResponseEntity<PagedResources<StoryResponse>> response = restTemplate.exchange(getLocalhost("/story"),
HttpMethod.GET, null, new ParameterizedTypeReference<PagedResources<StoryResponse>>() {});
PagedResources<StoryResponse> storiesResources = response.getBody();
Collection<StoryResponse> stories = storiesResources.getContent();
Apart from the content storiesResources holds page metadata and links too.
A more step-by-step explanation is available here: https://stackoverflow.com/a/46847429/8805916
If you use spring-cloud-openfeign you can use PageJacksonModule.
Just register PageJacksonModule in your object mapper:
final ObjectMapper mapper = new ObjectMapper()
mapper.registerModule(new PageJacksonModule());
If you looking at this thread, and if you try this answer
https://stackoverflow.com/a/44895867/8268335
You will meet the 2nd problem:
Can not construct instance of org.springframework.data.domain.Pageable
Then I find the perfect solution from here:
https://stackoverflow.com/a/42002709/8268335
I create the class RestPageImpl from the answer above and problem solved.
You can probably use exchange method of restTemplate and get the body from it..
Check the following answer https://stackoverflow.com/a/31947188/3800576.
This might help you
I'm implementing a Builder constructor as documented in Joshua Bloch's "Effective Java 2nd Edition. However, I'm running into a few complications when I try to extend the class and its builder. Essentially, the extended Builder in the extended child class has set field methods that return the parent Builder type, not the child builder type.
Of course, I can cast back to the ChildBuilder in the property build chain (as shown in my main method) but it is not seamless which defeats the purpose of the Builder, and it also forces me to segregate the parent setters and child setters.
I tried to use generics but it ended up becoming more verbose than the cast.
Is there a way I can consistently make the set methods on the builders return the builder type that was actually instantiated?
public class ParentObj {
public static void main(String[] args) {
ChildObj childObj = ((ChildObj.ChildBuilder) (new ChildObj.ChildBuilder())
.prop1(11)
.prop2(21)
.prop3(14))
.prop4(12)
.prop5(33)
.build();
}
private int prop1;
private int prop2;
private int prop3;
protected ParentObj(Builder builder) {
this.prop1 = builder.prop1;
this.prop2 = builder.prop2;
this.prop3 = builder.prop3;
}
public class Builder {
private int prop1;
private int prop2;
private int prop3;
public Builder prop1(int prop1) { this.prop1 = prop1; return this; }
public Builder prop2(int prop2) { this.prop2 = prop2; return this; }
public Builder prop3(int prop3) { this.prop3 = prop3; return this; }
public ParentObj build()
{
return new ParentObj(this);
}
}
}
private class ChildObj extends ParentObj {
private final int prop4;
private final int prop5;
private ChildObj(ChildBuilder childBuilder) {
super(childBuilder);
}
public class ChildBuilder extends Builder {
private int prop4;
private int prop5;
public ChildBuilder prop4(int prop4) { this.prop4 = prop4; return this; }
public ChildBuilder prop5(int prop5) { this.prop5 = prop5; return this; }
public ChildObj build() {
return new ChildObj(this);
}
}
}
Probably the best way would be to Override the parent builder methods.
class ChildBuilder {
public ChildBuilder prop1(int prop1){
return (ChildBuilder) super.prop1(prop1);
}
}
While this isn't exactly clean it will work for what you're trying to do.
How can I use generics propery in my particular case? The code first, then the explanation:
AbstractConstraint.java
public abstract class AbstractConstraint {
public abstract Constraint[] getConstraints();
}
AccountConstraint.java
public class AccountConstraint extends AbstractConstraint {
private Constraint<Range<Integer>> accountIdConstraint;
private Constraint<String> usernameConstraint;
private Constraint<String> passwordConstraint;
private Constraint<String> emailConstraint;
private AccountConstraint(Builder builder) {
this.accountIdConstraint = builder.accountIdConstraint;
this.usernameConstraint = builder.usernameConstraint;
this.passwordConstraint = builder.passwordConstraint;
this.emailConstraint = builder.emailConstraint;
}
#Override
public Constraint[] getConstraints() {
return new Constraint[] {
this.accountIdConstraint,
this.usernameConstraint,
this.passwordConstraint,
this.emailConstraint
};
}
public static class Builder extends ConstraintBuilder<AccountConstraint> {
private Constraint<Range<Integer>> accountIdConstraint;
private Constraint<String> usernameConstraint;
private Constraint<String> passwordConstraint;
private Constraint<String> emailConstraint;
public Builder() {
this.accountIdConstraint = null;
this.usernameConstraint = null;
this.passwordConstraint = null;
this.emailConstraint = null;
init();
}
public Builder accountId(final int val) {
this.accountIdConstraint = new Constraint<>(operation, truthed, new Range<>(val), "accountId");
return this;
}
public Builder accountId(final int min, final int max) {
this.accountIdConstraint = new Constraint<>(operation, truthed, new Range<>(min, max), "accountId");
return this;
}
public Builder accountId(final Range<Integer> accountId) {
this.accountIdConstraint = new Constraint<>(operation, truthed, accountId, "accountId");
return this;
}
public Builder username(final String username) {
this.usernameConstraint = new Constraint<>(operation, truthed, username, "username");
return this;
}
public Builder email(final String email) {
this.emailConstraint = new Constraint<>(operation, truthed, email, "email");
return this;
}
#Override
public AccountConstraint build() {
return new AccountConstraint(this);
}
}
}
ConstraintBuilder.java
public abstract class ConstraintBuilder<T> {
protected boolean truthed;
protected Operation operation;
protected void init() {
truthed = true;
operation = Operation.IS;
}
public ConstraintBuilder not() {
truthed = false;
return this;
}
public ConstraintBuilder like() {
operation = Operation.LIKE;
return this;
}
public abstract T build();
}
I want to be able to call new AccountConstraint.Builder().not().username("test"); but this is not possible as I lose the 'reference to the builder' at new AccountConstraint.Builder().not()., ie. I cannot select username("test") anymore.
In what ways could I fix this? I do want that the AccountBuilder.Builder extends ConstraintBuilder<AccountConstraint.Builder> such that I do not have to duplicate the commonly shared methods then.
Regards.
EDIT: I managed to get it working:
See the answer below for the changes.
I hope I haven't broken any Java fundamentals with this solution, I hope it is more of a solution than a dirty hack.
I would be pleased if someone could review this edit.
I think this should work:
Builder builder = (Builder) new AccountConstraint.Builder().not();
builder = builder.username("test");
Your issue is that:
new AccountConstraint.Builder().not()
returns a ConstrainBuilder<T>, which doesn't necessarily have access to username(final String). So, you cast it to a Builder builder, and then call username(final String) on builder.
EDIT:
You can turn this into one line:
((Builder) (new AccountConstraint.Builder().not())).username("test");
EDIT 2:
You could override not() in Builder: make it call super.not() and cast the return to a Builder. As in:
public Builder not()
{
return (Builder) super.not();
}
If casting is acceptable, an alternative to Steve's answer would be to override methods like not() in Builder and narrow the type like this:
public Builder not() {
return (Builder) super.not();
}
That way the caller doesn't have to cast each time.
You probably need recursive generics.
Something like this should work:
public abstract class ConstraintBuilder<T, B extends ConstraintBuilder<T,B>> {
private final Class<B> concreteBuilderType;
public ConstraintBuilder(Class<B> concreteBuilderType) {
if (!concreteBuilderType.isInstance(this)) {
throw new IllegalArgumentException("Wrong type");
}
this.concreteBuilderType = concreteBuilderType;
}
...
public B not() {
truthed = false;
return concreteBuilderType.cast(this);
}
}
The concrete Builder() constructor would have to call super(Builder.class).