Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What to do with a class where one particular variable is sometimes not used

Tags:

java

Beginner question here. I'm writing a Java program that queries a public API on the internet to retrieve details of discussions on a forum. The data comes back as JSON and I'm parsing that into Java objects to be used in my program.

A discussion normally contains five attributes, i.e. the five public variables. However, in response to a limited number of specific search types, the number of comments is not returned.

Is there a 'best' (in terms of object oriented programming) way to deal with this sort of scenario? My first attempt is below, where I have simply written two constructors, one that assigns a value to numberOfComments, and one that does not.

This doesn't seem like a great solution - what happens if another class creates a DiscussionDetails object, uses the constructor that does not populate numberOfComments, but then later tries to use the numberOfComments field?

I thought that maybe it should be split into two classes, where DiscussionDetails has no numberOfComments field, and DiscussionDetailsSpecialised is a subclass with an additional numberOfComments field. This feels a bit like overkill to me, for the sake of one single field.

Or maybe there's a convention that such a variable is initialised with a particular value like 'false' or '-1' or something?

Are there other, better approaches that an experienced programmer would use for this kind of situation?

I know the example is trivial, but I'm using it to try to illustrate my question as simply as possible.

/**
 * Wrapper for a set of JSON data returned from an API
 */
public class DiscussionDetails 
    {
    public String discussionID;
    public String discussionName;
    public String discussionURL;
    public String submittedDate;
    public String numberOfComments;

    /**
     * Constructor that populates all fields
     */
    public DiscussionDetails(String discussionID, String discussionName, String discussionURL, String submittedDate, String numberOfComments)
        {
        this.discussionID = discussionID;
        this.discussionName = discussionName;
        this.discussionURL = discussionURL;
        this.submittedDate = submittedDate;
        this.numberOfComments = numberOfComments;
        }

    /**
     * Constructor method to use when the number of comments is unknown, which happens in certain specific cases
     */
    public DiscussionDetails(String discussionID, String discussionName, String discussionURL, String submittedDate)
        {
        this.discussionID = discussionID;
        this.discussionName = discussionName;
        this.discussionURL = discussionURL;
        this.submittedDate = submittedDate;
        } 
    }
like image 796
Hoof-Hearted Avatar asked Jan 28 '26 10:01

Hoof-Hearted


2 Answers

This has been traditionally solved with "special" values (values that obviously make no sense, eg: -1 for a count) or null (which in a sense is the most special value).

The "best" way to deal with this is, IMHO, java.util.Optional: clients have to check if a value is present when they wish to use it and Optional makes this explicit, avoiding the common source of bugs of a client forgetting to check.

like image 57
giorgiga Avatar answered Jan 30 '26 02:01

giorgiga


One way to solve this is with a builder. Your example is good, but a builder can help make it more obvious what's going on.

/**
 * Wrapper for a set of JSON data returned from an API
 */
public class DiscussionDetails 
    {
    public String discussionID;
    public String discussionName;
    public String discussionURL;
    public String submittedDate;
    public String numberOfComments;

   public static class Builder{
      private DiscussionDetails dd = new DiscussionDetails();

      public discussionID(String discussionID) {
        dd.discussionID = discussionID;
        return this;
      }

      public discussionName(String discussionName) {
        dd.discussionName= discussionName;
        return this;
      }

      public discussionURL(String discussionURL) {
        dd.discussionURL= discussionURL;
        return this;
      }

      public submittedDate(String submittedDate) {
        dd.submittedDate= submittedDate;
        return this;
      }

      public numberOfComments(String numberOfComments) {
        dd.numberOfComments= numberOfComments;
        return this;
      }

      public DiscussionDetails build() {
        return dd;
      }

   }


 }

This can make your instantiation a little cleaner, especially with optional fields or a lot of fields.

You would use this like:

DiscussionDetails details = 
    new DiscussionDetails.Builder()
         .discussionID("1")
         .discussionName("Name")
         .build();

In this particular case, I've set 2 of the fields. The other fields would be null, or the default value. With some extra code, this gives you a lot of flexibility and arguably makes the code easier to read.

If you need to enforce certain fields being set, you can add more methods in the Builder class itself, or throw an error from the build method.

like image 26
Brian Hoover Avatar answered Jan 30 '26 02:01

Brian Hoover



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!