I've been Googling around for a while trying find the answer to this question without any success.
The way I understand how to create a class using the Builder style is like this:
public class MyClass {
private String myString;
private int myInt;
public static class Builder {
private String myString;
private int myInt;
public Builder() {}
public Builder setString(String myString) {
this.myString = myString;
return this;
}
public Builder setInteger(int myInt) {
this.myInt = myInt;
return this;
}
public MyClass build() {
return new MyClass(this);
}
private MyClass(Builder build) {
this.myString = build.myString;
this.myInt = build.myInt;
}
}
Which is great! It is a powerful style for class design. However, what I would like to do, is have a utility class where everything is static and public but also have a builder to set up the class. But the only way I seem to be able to do that, is by creating a new instance of a builder class which forces the user to call new on the parent class ... but the parent class isn't supposed to be instantiated, so it seems a bit confusing to have a Builder that has to be instantiated for a class that doesn't... The only way I've been able to offer any kind of a builder for a public utility class is by returning void on the final build statement, where all of the build options affect change on the parent class...
So my question is, is there a way to offer "Builder style" functionality on a public static utility class such that there would be no need to instantiate any class at all in the use of that functionality?
Edit:
I guess I should clarify, that what I am trying to offer to an end user of the class, is the ability to invoke a Builder style interface WITHOUT instantiating ANY classes at all... like this:
MyClass.Builder.option1().option2().build();
Notice I did NOT do this:
MyClass myClass = new MyClass.Builder()...
Because that is exactly what I DONT want to do...
Edit2:
Take the following class for example:
import java.util.*;
class MyClass {
private static List<String> userList = new ArrayList<>();
private static Map<Integer, String> userMap = new HashMap<>();
private static LinkedList<Double> userDoubleList = new LinkedList<>();
private static Map<Integer, SomeClass> someClassMap = new HashMap<>();
public static void method1(String userString) {
userList.add(userString);
}
public static void method2(Integer userNumber, String userString) {
userMap.put(userNumber, userString);
}
public static void method3(Double userDouble) {
userDoubleList.addLast(userDouble);
}
public static void method4(Integer userInteger, SomeClass userClass) {
someClassMap.put(userInteger, userClass);
}
}
I would like for the user to be able to set up the class using a Builder style like this:
MyClass.Builder.option1("My String").option3(25.4).build();
Or however that would end up actually looking ... I want to be able to have them set up the parameters of the class without needing to create a whole bunch of different public static methods that provide the variety of options that are inherent in the class...
I hope that makes sense ...
CodePudding user response:
You can use Lombok to produce easily Builders
Use the annotation @Builder on your class
@Builder
public class MyClass {
private String myString;
private int myInt;
}
Then you can use it like the following code :
public void foo(){
MyClass myClass = MyClass.builder().myInt(0).myString("bar").build();
}
Take a look here for lombok
Add the following dependency to manage it
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
<version>1.18.20</version>
</dependency>
CodePudding user response:
But the only way I seem to be able to do that, is by creating a new instance of a builder class which forces the user to call new on the parent class ... but the parent class isn't supposed to be instantiated
The class you've shown doesn't require you to create an instance of MyInstance
to create an instance of Builder
:
MyClass.Builder builder = new MyClass.Builder();
Note that this isn't invoking the constructor of MyClass
(it's not new MyClass().new Builder()
).
If you don't want an explicit variable for builder
, that's fine:
MyClass instance =
new MyClass.Builder()
.setString("hello")
.setInteger(42)
.build();
You can add a static method to MyClass
which gives you a new instance of the Builder, for example:
class MyClass {
static Builder builder() {
return new Builder();
}
static class Builder { ... }
}
but this is simply hiding the new
inside that method: a new instance is still being created.
MyClass instance =
MyClass.builder()
.setString("hello")
.setInteger(42)
.build();
CodePudding user response:
A new answer, following this edit:
Your MyClass
has no instance variables. This is not an appropriate application of the Builder
pattern (emphasis added):
The builder pattern is a design pattern designed to provide a flexible solution to various object creation problems in object-oriented programming.
If MyClass
has only static variables, it's questionable that you'd ever want to create an instance of it; but if you did, all you need to use is new MyClass()
.
What you perhaps seem to be asking about is the fluent programming idiom which is typically used in the builder pattern, to allow you to write:
new Builder().setThis().setThat().build()
Fluent programming is not the builder pattern: you can write builders without fluent programming; you can use fluent programming without it being a builder.
Fluent programming is simply down to the choice of return type:
class MyClass {
public static MyClass method1(String userString) {
// ...
return null;
}
public static MyClass method2(Integer userNumber, String userString) {
// ...
return null;
}
Now you can write MyClass.method1("hello").method2(0, "user").method1("world").method2(1, "foo")
etc, for essentially as long as you like.
But, this is an abuse: you are invoking static methods on instance expressions. This would be flagged with a warning in an IDE. (The fact that null
is being returned is not problematic: you can invoke static methods on null expressions, e.g. ((Integer) null).parseInt("0")
).
A fix for this would be to introduce an singleton instance:
class MyClass {
public static final MyClass INSTANCE = new MyClass();
private MyClass() {}
public MyClass method1(String userString) {
// ...
return this;
}
public MyClass method2(Integer userNumber, String userString) {
// ...
return this;
}
}
and invoke like MyClass.INSTANCE.method1("hello").method2(0, "user")
, which is now invoking instance methods on an instance.
However, the fundamental design of MyClass
- a holder of global mutable state - is itself ill-advised; and singletons, particularly mutable singletons, are also ill-advised.