1

I am writing a simple inventory system for practice and I have an item class that holds these values:

private String name;
private int quantity;
private Integer ID;
private Double pricePerUnit;
private Double totalPrice;

I am writing the constructors for this class and I want everything except the name and quantity to be optional, as in the user can opt whether or not to input any data for those fields. Currently I have two constructors that look like this:

public Item(String name, int quantity)
{
    this.name=name;
    this.quantity=quantity;
}

public Item(String name, int quantity, Integer ID, Double pricePerUnit, Double totalPrice)
{
    this(name, quantity);
    this.ID=ID;
    this.pricePerUnit=pricePerUnit;
    this.totalPrice=totalPrice;
}

Is there any way I can make some of the arguments in the second constructor optional, or non-mandatory?

Thanks!

Nick5741
  • 11
  • 2
  • 3
    You can use builder design pattern to handle object initialization. https://stackoverflow.com/a/5788294/7505731 – Hemant Mar 31 '20 at 04:03

3 Answers3

1

Naturally, in such cases I'd would think of two possibilities to get what you need, Constructor Overloading and Builder Pattern

Although when you have the same data formats, You can't naturally depend on the former. In such cases (as OP's Question) the best alternative is to go for a builder design pattern.

You can build an instance of Item.class as the following


public class Item {



\\... other public functions.. etc


 static class ItemBuilder{

    private Item item;

    public ItemBuilder withNameAndQuantity(String name, int quantity){
      item = new Item(); //default constructor or as per your usecase a private constructor
      item.setName(name);
      item.setQuantity(quantity);
      return this;
    }

    public ItemBuilder withPricePerUnit(Double pricePerUnit){
      if(item!=null){
       item.setPriceUnit(pricePerUnit);
      }
      return this;
    }

    public ItemBuilder withTotalPrice(Double totalPrice){
      if(item!=null){
        item.setTotalPrice(totalPrice);
      }
      return this;
    }

   public Item build(){
    if(item!=null){
      return item;
    }else{
      throw new IllegalStateException("item is null, no name or id set");
    }
   }
 }
}


Finally, you could build a new Item by doing the following :

Item item = new Item.ItemBuilder(). withNameAndQuantity("apple",10). withTotalPrice(100).build();

papaya
  • 1,505
  • 1
  • 13
  • 26
0

Ideally you would decompose the class into coherent pieces, in much the same way as you might normalise a database schema.

There is the Builder Pattern.

You code could be written better by making the constructor will all the attributes the "canonical constructor* and forward to that. This will also make it easier to switch over to records when they become available.

public Item(String name, int quantity) {
    this(name, quantity, null, null, null);
}

public Item(String name, int quantity, Integer id, Double pricePerUnit, Double totalPrice) {
    this.name = name;
    this.quantity = quantity;
    this.ID = ID;
    this.pricePerUnit = pricePerUnit;
    this.totalPrice = totalPrice; // Shouldn't this be calculated?
}

(Not that nulls are ideal.)

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
0

One way is to create more constructors and another is to loose the immutability and introduce setter methods.

Hence, you can use Builder Pattern as Builder Pattern will help you to consume additional attributes while retaining the immutability of Item class.

Below is the coded solution. This uses a additional class ItemBuilder which helps us in building desired Item object with all mandatory attributes and combination of optional attributes, without loosing the immutability.

public class Item {
    //All final attributes
    private String name; // required
    private int quantity; // required
    private Integer ID; // optional
    private Double pricePerUnit; // optional
    private Double totalPrice; // optional
 
    private Item(ItemBuilder builder) {
        this.name = builder.name;
        this.quantity = builder.quantity;
        this.ID = builder.ID;
        this.pricePerUnit = builder.pricePerUnit;
        this.totalPrice = builder.totalPrice;
    }
 
    //All getter, and NO setter to provide immutability
    public String getName() {
        return name;
    }
    public int getQuantity() {
        return quantity;
    }
    public Integer getID() {
        return ID;
    }
    public Double getPricePerUnit() {
        return pricePerUnit;
    }
    public Double getTotalPrice() {
        return totalPrice;
    }
 
    @Override
    public String toString() {
        return "User: "+this.name+", "+this.quantity+", "+this.ID+", "+this.pricePerUnit+", "+this.totalPrice;
    }
 
    public static class ItemBuilder
    {
        private String name; // required
        private int quantity; // required
        private Integer ID; // optional
        private Double pricePerUnit; // optional
        private Double totalPrice; // optional
 
        public ItemBuilder(String name, int quantity) {
            this.name = name;
            this.quantity = quantity;
        }
        public ItemBuilder ID(Integer ID) {
            this.ID = ID;
            return this;
        }
        public ItemBuilder pricePerUnit(Double pricePerUnit) {
            this.pricePerUnit = pricePerUnit;
            return this;
        }
        public ItemBuilder totalPrice(Double totalPrice) {
            this.totalPrice = totalPrice;
            return this;
        }
        //Return the finally constructed Item object
        public Item build() {
            Item item =  new Item(this);
            validateUserObject(item);
            return item;
        }
        private void validateUserObject(Item item) {
            //Do some basic validations to check 
            //if item object does not break any assumption of system
        }
    }
}

OR

You can also make use of JsonProperties.

@JsonIgnoreProperties(ignoreUnknown = true)
public record Item(
        String name,
        Integer quantity,
        @JsonInclude(JsonInclude.Include.NON_NULL) Integer ID,
        @JsonInclude(JsonInclude.Include.NON_NULL) Double pricePerUnit,
        @JsonInclude(JsonInclude.Include.NON_NULL) Double totalPrice) {}

I hope this gives you clarity on how to resolve your problem.

Ela Singh
  • 36
  • 1
  • 7