This question already has answers here:
When would you use the Builder Pattern? [closed]
(13 answers)
Closed 7 years ago.
Recently I saw some of the developers coding their VOs with nested builder class like
public class User {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public static class UserBuilder {
private String firstName;
private String lastName;
public User build() {
User user = new User();
user.firstName = firstName;
user.lastName = lastName;
return user;
}
public UserBuilder withFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public UserBuilder withLastName(String lastName) {
this.firstName = firstName;
return this;
}
}
}
Now, they claim that this makes code more readable. My point is, this has following disadvantages:
I can't simply add fields and expect my IDE to complete code for me, as now I need to update this inner class too.
Simple POJOs are carrying code which is not relevant for VO.
I am looking for any advice if I am missing something here. Feel free to add your thoughts about the same.
Sample code after this modification looks like,
User user = new User.UserBuilder()
.withFirstName("Name")
.withLastName("surName")
.build();
Here is an article from Joshua Bloch. He explains very well why, when and how to use a builder : http://www.informit.com/articles/article.aspx?p=1216151&seqNum=2
It is one of items in his book called Effective Java. I strongly advise you to read this book if you have a little experience with Java.
Main point :
When you you get a class with a lot of attribute there is several ways create an object and init it.
If you set one by one every attributes it can be wordy and your object could be altered after its creation. With this method it is impossible to make your class immutable and you cannot be sure that your object is in consistent state.
Exemple from the article :
public class NutritionFacts {
// Parameters initialized to default values (if any)
private int servingSize = -1; // Required; no default value
private int servings = -1; // " " " "
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts() { }
// Setters
public void setServingSize(int val) { servingSize = val; }
public void setServings(int val) { servings = val; }
public void setCalories(int val) { calories = val; }
public void setFat(int val) { fat = val; }
public void setSodium(int val) { sodium = val; }
public void setCarbohydrate(int val) { carbohydrate = val; }
}
You can use a telescoping constructor. It can make your object immutable. However if you get many attributes it can be hard to write and read your code. More over when you just want create with one setted attribute, and unfortunately this one is the last parameter of the constructor, you have to set all parameter anyway.
Exemple from the article :
public class NutritionFacts {
private final int servingSize; // (mL) required
private final int servings; // (per container) required
private final int calories; // optional
private final int fat; // (g) optional
private final int sodium; // (mg) optional
private final int carbohydrate; // (g) optional
public NutritionFacts(int servingSize, int servings) {
this(servingSize, servings, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories) {
this(servingSize, servings, calories, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat) {
this(servingSize, servings, calories, fat, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat, int sodium) {
this(servingSize, servings, calories, fat, sodium, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat, int sodium, int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
}
The builder allows to make your code more readable and easy to write. It also allows you to be able to make your class immutable.
Exemple from the article :
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
// Required parameters
private final int servingSize;
private final int servings;
// Optional parameters - initialized to default values
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val)
{ calories = val; return this; }
public Builder fat(int val)
{ fat = val; return this; }
public Builder carbohydrate(int val)
{ carbohydrate = val; return this; }
public Builder sodium(int val)
{ sodium = val; return this; }
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
In your example I'm not sure that it is very useful to do a builder for a class with only two attributes.
I hope this will help you.
IMHO in the given example you gain no value by using Builder pattern. You can create User object without the Builder (because of all the setters). The only thing that Builder gives you in this particular case is Fluent Interface.
You should use Builder Pattern when there are various combinations of creating a valid object. Thanks to this you won't have to implement many constructors or factory methods.
Builder is also helpful when creating a valid object requires many parameters.
Builder should be responsible for build only a valid objects. In your case, if User needs to have first and last name Builder shouldn't allow to create the instance of User that doesn't have those attributes set.
Start with small immutable objects
If all your properties are required then you should use just constructor. By doing this you might create nice small immutable object.
Builders are helpful when you have multiple optional fields
If there are multiple optional fields and different ways to create an object you'd need multiple constructors.
public User (int requiredParameter) { ... }
public User (int reqiredParameter, int optionalParameter) { ... }
public User (int reqiredParameter, int optionalParameter, String optionalParameter2) { ... }
public User (int reqiredParameter, String optionalParameter2) { ... }
It creates messy code. It is hard to determine which constructor you should use.
In this case you could use nested builder to have intuitive way of creating your object.
Related
I am working on my class assignment and I am stuck.
I have two classes: Part, which is abstract and has InHouse and Outsourced classes that extend Part. Then I have Product, which oddly has an observableArrayList of parts called associatedParts.
I am working on my AddProductController, trying to make a call to the method in the Product class addAssociatedPart(). My problem is the compiler doesn't find the method in Part. If I cast to an InHouse, it doesn't find the method in InHouse, and so on. I can't use a static method, because the method addAssociatedPart() is supposed to be non-static per the UML design. So, I can't tell it explicitly to find it in Product.addAssociatedPart(), because it tells me I can't reference a non-static etc.
Here's the code snippets starting with the Product class.
public class Product {
private ObservableList<Part> associatedParts = FXCollections.observableArrayList();
private int id;
private String name;
private double price;
private int stock;
private int min;
private int max;
public void addAssociatedPart(Part part) {
getAllAssociatedParts().add(part);
}
public ObservableList<Part> getAllAssociatedParts() {
return this.associatedParts;
}
And then the AddProductScreenController class:
public class AddProductScreenController implements Initializable {
#FXML
public void onAddProductAddPart(ActionEvent event) {
// this is triggered when the Add button is clicked
Part selectedItem = addProductTableViewAll.getSelectionModel().getSelectedItem();
selectedItem.addAssociatedPart(); // can't find method
Product.selectedItem.addAssociatedPart(); // can't find variable selectedItem (obviously bad formatting)
selectedItem.Product.addAssociatedPart(); // can't find variable Product (again bad formatting)
addAssociatedPart(selectedItem); // can't find method addAssociatedPart()
Product.addAssociatedPart(selectedItem); // non-static method, can't be referenced from a static context
InHouse newPart = new InHouse(1, "test", 1.99, 1, 1, 1, 101);
addAssociatedPart(newPart); // can't find method
Product.addAssociatedPart(newPart); // non-static method
newPart.addAssociatedPart(); // can't find method
addProductTableViewPartial.setItems(associatedParts);
}
}
The part code as requested:
public abstract class Part {
private int id;
private String name;
private double price;
private int stock;
private int min;
private int max;
public ObservableList<Part> allParts = FXCollections.observableArrayList();
public Part(int id, String name, double price, int stock, int min, int max) {
this.id = id;
this.name = name;
this.price = price;
this.stock = stock;
this.min = min;
this.max = max;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setPrice(double price) {
this.price = price;
}
public double getPrice() {
return this.price;
}
public void setStock(int stock) {
this.stock = stock;
}
public int getStock() {
return this.stock;
}
public void setMin(int min) {
this.min = min;
}
public int getMin() {
return this.min;
}
public void setMax(int max) {
this.max = max;
}
public int getMax() {
return this.max;
}
}
This is InHouse
package model;
public class InHouse extends Part {
private int machineId;
public InHouse(int id, String name, double price, int stock, int min, int max, int machineId) {
super(id, name, price, stock, min, max);
this.machineId = machineId;
}
public void setMachineId(int machineId) {
this.machineId = machineId;
}
public int getMachineId() {
return this.machineId;
}
}
And then Outsourced:
package model;
public class Outsourced extends Part {
private String companyName;
public Outsourced(int id, String name, double price, int stock, int min, int max, String companyName) {
super(id, name, price, stock, min, max);
this.companyName = companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public String getCompanyName() {
return this.companyName;
}
}
If there is a particular part of Java you feel I need to brush up on to understand this, I am wide open to that. I want to understand the issue, not just get a fix. I'm not even looking for the answer, just a point in the direction of what the problem is.
Update
#Le and #Jackson pointed me in the right direction with their comments on the response he provided. I need to have a product first:
Product product = new Product(1, "test", 1.99, 1, 1, 1);
product.addAssociatedPart(selectedItem);
I was trying to explain you association of your various classes in comments but thought I would use visual help. I have simplified your scenario into a classic OOP problem.
public class Product {
public void addAssociatedPart(Part part) {
// some stuff
}
}
public abstract class Part {
}
public class InHouse extends Part {
}
public class Outsourced extends Part {
}
public class Assembly {
public static void main(String[] args) {
Product car = new Product();
Part seat = new InHouse();
Part engine = new Outsourced();
Part window = new InHouse();
car.addAssociatedPart(seat);
car.addAssociatedPart(engine);
car.addAssociatedPart(window);
}
}
I do not have any method in my Part or its sub-classes to add themselves to some Product. Was this you trying to achieve?
I am working on my AddProductController, trying to make a call to the
method in the Product class addAssociatedPart().
My problem is the compiler doesn't find the method in Part.
Why should it? Is Part a child of Product? Otherwise, you are calling a Product Method using a Part instance.
To use the methods of Inhouse and Oursourced for parts, you can do something like this
if (selectedItem instanceof InHouse){
Inhouse inhouse = (Inhouse)selectedItem;
//do what you need with inhouse methods
}else{
Outsourced outsourced = (Outsourced)selectedItem;
//do what you need with oursourced method
}
You are confused with static and non static method. You need a Product instance to access AddAssociatedPart(). Visualize your class in class diagram.
public void onAddProductAddPart(ActionEvent event) {
// this is triggered when the Add button is clicked
Part selectedItem = addProductTableViewAll.getSelectionModel().getSelectedItem();
selectedItem.addAssociatedPart(); // addAssociatedPart() is method of Product, not Part
Product.selectedItem.addAssociatedPart(); // Product class has no static member selectedItem
selectedItem.Product.addAssociatedPart(); // syntax error
addAssociatedPart(selectedItem); // addAssociatedPart() is not method of AddProcutController
Product.addAssociatedPart(selectedItem); // if you reference the method start with a class, the method is expected to be a static method. addAssociatedPart() is not a static method, call it with a product instance
InHouse newPart = new InHouse(1, "test", 1.99, 1, 1, 1, 101);
addAssociatedPart(newPart); // addAssociatedPart() is not part of AddProductController
Product.addAssociatedPart(newPart); // dont reference non-static method with a class name
newPart.addAssociatedPart(); // addAssociatedPart() is not part of Part
addProductTableViewPartial.setItems(associatedParts);
}
These are two classes of code that I wrote.. the problem here is I am not sure how to define class fields to represent Grass, fire and water as a Type using static..
Also I am not sure if I had used the super function the right way.. How do I properly call the parent's constructor so that I dont have to re define "knockedOut boolean" and be able to use Fire as the type?
Question could be confusing but I am not sure how to explain it better :( sorry
public abstract class Pokemon {
private String name;
private String type;
private int attack;
private int health;
private boolean knockedOut;
static private String Grass;
static private String Water;
static private String Fire;
public Pokemon (String n, String t, int a, int h) {
name = n;//state
type = t;//state
attack = a;//state
health = h;//state
knockedOut = false;
}
public abstract int takeDamage(Pokemon enemy);
public String toString() {
return "}";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getAttack() {
return attack;
}
public void setAttack(int attack) {
this.attack = attack;
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
this.health = health;
}
public boolean isKnockedOut() {
return knockedOut;
}
public void setKnockedOut(boolean knockedOut) {
this.knockedOut = knockedOut;
}
}
public abstract class Charizard extends Pokemon {
private static String Fire;
private int attackFire;
private int healthFire;
private static String Water;
private static String Grass;
public Charizard(int a, int h) {
super("Charizard", Fire, a, h);
attackFire = a;
healthFire = h;
}
public int takeDamage(Pokemon enemy){
int enemyAttack = enemy.getAttack();
if(enemy.getType().equals(Water)){
enemy.setHealth(enemy.getHealth()-attackFire/2);
healthFire = healthFire-enemy.getAttack()*2;
if(enemy.getHealth()<=0){
enemy.setKnockedOut(true);
}
}
else if(enemy.getType().equals(Fire)){
enemy.setHealth(enemy.getHealth()-attackFire/2);
healthFire = healthFire-enemy.getAttack()*2;
if(enemy.getHealth()<=0){
enemy.setKnockedOut(true);
}
}
else if(enemy.getType().equals(Grass)){
enemy.setHealth(enemy.getHealth()-attackFire/2);
healthFire = healthFire-enemy.getAttack()/2;
if(enemy.getHealth()<=0){
enemy.setKnockedOut(true);
}
if(healthFire <=0){
Charizard.set = true;
}
}
return enemyAttack;
}
}
You want to declare your different types like this:
static public final String GRASS= "Grass";
static public final String WATER = "Water";
static public final String FIRE = "Fire";
(I'm following the established convention here that fields declared static, public, and final should have names in all uppercase letters.)
By declaring these fields public, any other classes (including those that extend Pokemon, such as Charizard) that might need to test the type of a Pokemon can use them. By declaring them final, nobody can change them even though they are public. By giving them initial values, you make them actually useful for distinguishing different types of Pokemon, as well as avoid the inevitable NullPointerException that would happen the first time you executed something like p.getType().equals(Pokemon.FIRE)
As for knockedOut, it looks like you're handling it the right way. The field knockedOut is private in Pokemon but you've provided public getter and setter methods that other classes can (and do) use to access it.
public class Location {
final public static int INITIAL = 0;
final public static int PRISON = 1;
final public static int DEATH = 2;
final public static int SQUARE = 3;
private String name;
private int type;
private int section;
private int damage;
private Square square ;
// this constructor construct a Location from a name and a type.
public Location(String name, int type) {
this.name = name;
this.type = type;
}
// this constructor constructs a Location of type SQUARE from a name, section, and damage.
public Location(String name, int section, int damage) {
this.name = name;
this.section = section;
this.damage = damage;
this.square = new Square(name,section,damage);
}
// Get the square associated with this Location.
public Square getSquare() {
return square;
}
}
I think I'm misunderstanding what the second constructor is doing, as currently the constructor isn't doing anything to the instance variable square.
In your second constructor, you just have to initialize type with Location.SQUARE:
// this constructor constructs a Location
// of type SQUARE from a name, section, and damage.
public Location(String name, int section, int damage) {
this.name = name;
this.section = section;
this.damage = damage;
this.type = Location.SQUARE;
this.square = new Square(name,section,damage);
}
And now your class' attributes will be consistent.
Regarding initialization of the square attribute, it seems to be OK to me. You can create instances of other classes inside a constructor and assign them to attributes if you want.
In your second constructor you are simply initializing your property - private Square square of your Location class from the parameters you provided to the constructor.
Initializing an Object/non-primitive (eg.- square here) type is completely valid in java like initializing other primitive type (eg. - type, section etc here)
You have a problem: Your first constructor does nothing with the Square reference, so it's null.
Try it like this:
/**
* #link http://stackoverflow.com/questions/31523704/how-to-use-a-constructor-to-initialize-an-different-classs-object-in-java
* User: mduffy
* Date: 7/20/2015
* Time: 3:07 PM
*/
public class Location {
final public static int INITIAL = 0;
final public static int PRISON = 1;
final public static int DEATH = 2;
final public static int SQUARE = 3;
private String name;
private int type;
private int section;
private int damage;
private Square square;
// No idea what damage ought to be. Is that your type?
public Location(String name, int type) {
this(name, Location.SQUARE, type);
}
public Location(String name, int section, int damage) {
this.name = name;
this.section = section;
this.damage = damage;
this.square = new Square(name, section, damage);
}
public Square getSquare() {
return square;
}
}
How to solve the following constructor overloading problem? This is an interview question but I am curious to know the solution.
class Player
{
int nationalRank;
int internationalRank;
String name;
Player(String name, int nationalRank)
{
this.name= name;
this.nationalRank = nationalRank;
this.internationalRank=0;
}
Player(String name, int internationalRank)
{
this.name= name;
this.nationalRank = 0;
this.internationalRank=internationalRank;
}
}
Here, the compiler will give an error because argument types are same for both constructor. But logically they both are different. How can I solve this problem without adding any extra arguments? Is there any design pattern specifically for this?
class Player
{
int nationalRank;
int internationalRank;
String name;
private Player(){}
public static Builder builder()
{
return new Builder();
}
public static class Builder
{
int nationalRank = -1;
int internationalRank = -1;
String name;
public Builder nationalRank(int nationalRank)
{
this.nationalRank = nationalRank;
return this;
}
public Builder internationalRank(int internationalRank)
{
this.internationalRank = internationalRank;
return this;
}
public Builder name(String name)
{
this.name = name;
return this;
}
public Player build()
{
if (nationalRank == -1 && internationalRank = -1)
throw new IllegalStateException("both ranks haven't been initialized");
if (null == name)
throw new IllegalStateException("name hasn't been initialized");
Player result = new Player();
result.nationalRank = this.nationalRank;
result.internationalRank = this.internationalRank;
result.name = this.name;
return result;
}
}
}
Usage:
Player player = Player.builder().name("John").internationalRank(522).build();
You've got various options.
The simplest is to add factory methods like this:
public class Player
{
private int nationalRank;
private int internationalRank;
private String name;
private Player()
{
}
public static Player newNationalPlayer(String name, int nationalRank)
{
Player nationalPlayer = new Player();
nationalPlayer.name= name;
nationalPlayer.nationalRank = nationalRank;
nationalPlayer.internationalRank = 0;
return nationalPlayer;
}
public static Player newInternationalPlayer(String name, int internationalRank)
{
Player internationalPlayer = new Player();
internationalPlayer.name= name;
internationalPlayer.nationalRank = 0;
internationalPlayer.internationalRank = internationalRank;
return internationalPlayer;
}
...
}
However, this leaves an unused variable which isn't very nice. A better solution would be to add a PlayerType enum:
public enum PlayerType
{
NATIONAL,
INTERNATIONAL
}
public class Player
{
private int rank;
private String name;
private PlayerType type;
public Player(String name, PlayerType type, int rank)
{
this.name= name;
this.type = type;
this.rank = rank;
}
...
}
Which is best is down to the exact use case.
Just reverse the parameters of one of the constructors and you are good to go.... I made this answer thinking that it's an interview question....perhaps the interviewer has this in mind...
class Player
{
int nationalRank;
int internationalRank;
String name;
Player(String name, int nationalRank)
{
this.name= name;
this.nationalRank = nationalRank;
this.internationalRank=0;
}
Player( int internationalRank,String name)
{
this.name= name;
this.nationalRank = 0;
this.internationalRank=internationalRank;
}
}
As suggested by a comment, just use static factory methods. In fact, this solution goes further than that and uses a builder. You will note a clear advantage: all instance variables are now final.
public class Player
{
private final String name;
private final int nationalRank;
private final int internationalRank;
// Constructor becomes private
private Player(final Builder builder)
{
name = builder.name;
nationalRank = builder.nationalRank;
internationalRank = builder.internationalRank;
}
public static Builder withName(final String name)
{
return new Builder(name);
}
// Inner builder class
public static class Builder
{
private final String name;
private int nationalRank;
private int internationalRank;
private Builder(final String name)
{
this.name = name;
}
public Builder withNationalRank(int rank)
{
nationalRank = rank;
return this;
}
public Builder withInternationalRank(int rank)
{
internationationalRank = rank;
return this;
}
public Player build()
{
return new Player(this);
}
}
}
Usage:
Player player1 = Player.withName("foo").withNationalRank(1).build();
// etc
I am playing around with the Builder pattern and get stuck how to add a new "property" to a new-created object:
public class MsProjectTaskData {
private boolean isAlreadyTransfered;
private String req;
public static class Builder {
private boolean isAlreadyTransfered = false;
public Builder withTransfered(boolean val) {
isAlreadyTransfered = val;
return this;
}
public MsProjectTaskData build() {
return new MsProjectTaskData(this);
}
}
private MsProjectTaskData(Builder builder) {
isAlreadyTransfered = builder.isAlreadyTransfered;
}
public MsProjectTaskData(String req) {
this.req = req;
}
}
I can create a new object with Builder like this:
MsProjectTaskData data = new MsProjectTaskData.Builder().withTransfered(true).build();
But with this approach the req string from a new-created object is lost (of course).
Is there a possibility to create a new object with the new set isAlreadyTransfered variable and with the "old" req string from a "old" object?
Maybe I have to pass the old object reference to the Builder but I do not know how to do this. Maybe the use of Builder pattern is not really usefull for this approach?
EDIT: (After comment from Eugene)
Think, I got it:
public static class Builder {
private boolean isAlreadyTransfered = false;
private MsProjectTaskData data;
public Builder(MsProjectTaskData data) {
this.data = data;
}
public Builder withTransfered(boolean val) {
isAlreadyTransfered = val;
data.setAlreadyTransfered(isAlreadyTransfered);
return this;
}
public MsProjectTaskData build() {
return data;
}
}
Seems to work or is something wrong with the code above? Can I use this approach without consideration?
Make the Builder constructor take as an argument the "old" object and set whatever you want from it to the new one.
Edit
You need to read a bit more about the builder pattern to get a better grasp at what it is and if you really need it.
The general idea is that Builder pattern is used when you have optional elements. Effective Java Item 2 is your best friend here.
For your class, if you want to build one object from another and use a Builder pattern at the same time, you
Either pass the "old" object in the Builder constructor
Create a method from or fromOld, etc.
So how does that looks like? I am going to provide only the first one you can figure out the second on your own.
class MsProjectTaskData {
private final String firstname;
private final String lastname;
private final int age;
private MsProjectTaskData(Builder builder){
this.firstname = builder.firstname;
this.lastname = builder.lastname;
this.age = builder.age;
}
public static final class Builder{
//fields that are REQUIRED must be private final
private final String firstname;
private final String lastname;
//fields that are optional are not final
private int age;
public Builder(String firstname, String lastname){
this.firstname = firstname;
this.lastname = lastname;
}
public Builder(MsProjectTaskData data){
this.firstname = data.firstname;
this.lastname = data.lastname;
}
public Builder age(int val){
this.age = val; return this;
}
public MsProjectTaskData build(){
return new MsProjectTaskData(this);
}
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
public int getAge() {
return age;
}
}
And how you will create one object from another:
MsProjectTaskData.Builder builder = new MsProjectTaskData.Builder("Bob", "Smith");
MsProjectTaskData oldObj = builder.age(23).build();
MsProjectTaskData.Builder newBuilder = new MsProjectTaskData.Builder(oldObj);
MsProjectTaskData newObj = newBuilder.age(57).build();
System.out.println(newObj.getFirstname() + " " + newObj.getLastname() + " " + newObj.getAge()); // Bob Smith 57