Helios
Helios

Reputation: 859

How to handle the default value of an ArrayList

I'm creating an application containing a class I'm writing called Person. One of the fields of Person is 'aliases' which is an ArrayList<String>. Ultimately the aliases will be displayed to the user according to the following logic: If Person has aliases then they should be displayed as [Finch, Wren, Admin, etc...], otherwise UNKNOWN should be displayed. So far I've tried implementing this in one of three ways:

  1. Person contains the method getAliases() which simply returns a copy of the ArrayList as is. The caller checks for an empty array to implement the desired behavior.

  2. Person contains the method aliasesToString() which can be called to produce the desired string.

  3. Instead of using ArrayList<String>, aliases is an implementation of DefaultableArrayList<T>. This class extends ArrayList and holds a default value with type T. The toString() method is overriden to produce the desired string. The application calls some_person.getAliases().toString() to produce the desired behavior.

Below is my implementation of option 3:

public class DefaultableArrayList<T> extends ArrayList<T> {

    private static final long serialVersionUID = -6735356930885363889L; // Auto-generated
    private final T defaultElement;


    public DefaultableArrayList(T defaultElement) {
        super();
        this.defaultElement = defaultElement;
    }


    public DefaultableArrayList(Collection<? extends T> c, T defaultElement) {
        super(c);
        this.defaultElement = defaultElement;
    }


    public DefaultableArrayList(int initialCapacity, T defaultElement) {
        super(initialCapacity);
        this.defaultElement = defaultElement;
    }


    public T getDefaultElement() {
        return defaultElement;
    }


    @Override
    public String toString() {
        if (!isEmpty()) {
            return super.toString();

        } else {
            return defaultElement.toString();
        }
    }
}

What concerns me about options 2 and 3 is that I might be adding needless complexity whilst violating OOP guidelines. Should Person really be concerned with what happens if there's no aliases and does it make sense for aliases to define how it's ultimately implemented in the application? I think that I should be letting the caller handle the empty case. Which option should I choose that best meets standard OOP design guidelines? Or is there a fourth option that I haven't considered?

Upvotes: 7

Views: 1138

Answers (4)

Chetan Kinger
Chetan Kinger

Reputation: 15212

Let's walk through your options:

Person contains the method getAliases() which simply returns the ArrayList as is. The caller checks for an empty array to implement the desired behavior.

What would the caller code look like?

if (!person.getAliases().isEmpty()) { //Print aliases }

That looks a bit ugly and unreadable. If you want to go with this option, the least you can do is add a hasAliases method in Person which essentially does the check for you and makes the client code more readable:

if (person.hasAliases()) { //Print aliases }

That's a lot cleaner and lot more readable code.

Person contains the method aliasesToString() which can be called to produce the desired string.

The though of Person being self sufficient is not a bad one. All that the client code has to do is call person.aliasesToString() rather than doing:

if (person.hasAliases()) {
   List<String> aliases = person.getAliases();
   StringBuilder aliaseString = new StringBuilder("");
   for (String alias : aliases) {
        aliasString.append(aliases);
   }
}

Instead of using ArrayList, aliases is an implementation of DefaultableArrayList

That's an overkill for such a simple task.

So which approach do you go with? It depends on your overall requirement. If you have different clients/UI that want to do different things with the aliases, option 1 would be the best. If you have a single client that always wants to print the aliases in a certain way, option 2 would be a better choice.

Upvotes: 3

Eran
Eran

Reputation: 393966

Option 3 is an overkill. I wouldn't extend ArrayList for such a small added value.

Choosing between option 1 and option 2 depends on whether there are other anticipated uses for the List returned by getAliases() other than displaying a String representation of it.

The only downside I see with an aliasesToString() method, is that it makes an assumption that all users of the Person class would display the aliases the same way.

If you go with the getAliases() method, I would prefer that method to return a copy of that List or an array representation of that List. The reason for that is you prevent the user of your Person class from mutating the Person instance by mutating the List returned by getAliases().

Upvotes: 2

Kevin Caravaggio
Kevin Caravaggio

Reputation: 740

I find the simplest way is often good style. That is, the uglier the code get when doing something simple usually means that the style is going astray. Therefore, Option 1 looks like a solid way to implement this. Using getAliases() to return or print the field is pretty standard. Also, you can implement a check for an empty array and return or print UNKNOWN in that instance. Otherwise, and the ArrayList toString() will take care of formating content.

Upvotes: 2

JB Nizet
JB Nizet

Reputation: 692023

The first option is the right one. The model shouldn't be concerned with the way it is displayed.

You won't represent the person and his/her aliases the same way in a rich application, a web application or a console application.

Even in a given application, you'll probably represent the same model in various ways.

If you internationalize your application, you'll have to change "UNKNOWN" to something else.

So, just return the list as is (or an unmodifiable vew of the list), and let the presentation layer deal with presentation logic. BTW, toString() is more a debugging help than a functional method used to represent an object in an application.

Upvotes: 6

Related Questions