Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I extend ArrayList to add attributes that isn't null?

I would like to add a collection of objects to an arrayList ,only if the particular attribute is not null.

I am thinking of extending the ArrayList and implementing the check inside the child class.

One alternate way is to check for the the attribute before putting it in a Arraylist, but that would mean , i will have to scatter the if checks every where if i need to add the objects to the arraylist based on the logic.

I would like to know your thoughts on it ... on a second thought is it a overkill ?

like image 496
Sudhakar Avatar asked Feb 20 '12 09:02

Sudhakar


People also ask

What happens when you add an element that exceeds the ArrayList's capacity?

Capacity is always greater than or equal to Count. If Count exceeds Capacity while adding elements, the capacity is automatically increased by reallocating the internal array before copying the old elements and adding the new elements.

What does ArrayList extend?

The ArrayList class extends AbstractList and implements the List interface. ArrayList supports dynamic arrays that can grow as needed. Standard Java arrays are of a fixed length. After arrays are created, they cannot grow or shrink, which means that you must know in advance how many elements an array will hold.

Does ArrayList allow null values?

In ArrayList, any number of null elements can be stored. While in HashMap, only one null key is allowed, but the values can be of any number.

How does ArrayList grow dynamically?

The grow method in the ArrayList class gives the new size array. In Java 8 and later The new capacity is calculated which is 50% more than the old capacity and the array is increased by that capacity. It uses Arrays.


2 Answers

Decorator pattern

I would actually recommend wrapping ArrayList using well-documented Decorator pattern. You simply wrap your ArrayList with another List implementation that delegates most of the methods but adds validation logic:

public class ValidatingListDecorator extends AbstractList<MyBusinessObject>
{

    private final List<MyBusinessObject> target;

    public ValidatingListDecorator(List<MyBusinessObject> target) {
        this.target = target;
    }

    @Override
    public MyBusinessObject set(int index, MyBusinessObject element)
    {
        validate(element);
        return target.set(index, element);
    }

    @Override
    public boolean add(MyBusinessObject o)
    {
        validate(o);
        return target.add(o);
    }

    //few more to implement
    
}

Advantages:

  • You can still access raw list without validation if you want (but you can restrict this)
  • Easier to stack different validations, turn them on and off selectively.
  • Promotes composition over inheritance as noted by @helios
  • Improves testability
  • Does not tie you to a specific List implementation, you can add validation to LinkedList or Hibernate-backed persistent lists. You can even think about generic Collection decorator to validate any collection.

Implementation notes

Despite the implementation remember there are quite a lot of methods you have to remember about while overriding: add(), addAll(), set(), subList() (?), etc.

Also your object must be immutable, otherwise the user can add/set valid object and modify it afterwards to violate the contract.

Good OO design

Finaly I wrote:

validate(element)

but consider:

element.validate()

which is a better design.

Stacking validations

As noted before if you want to stack validations, validating each proprty/apsect in a single, separate class, consider the following idiom:

public abstract class ValidatingListDecorator extends AbstractList<MyBusinessObject>
{

    private final List<MyBusinessObject> target;

    public ValidatingListDecorator(List<MyBusinessObject> target) {
        this.target = target;
    }

    @Override
    public MyBusinessObject set(int index, MyBusinessObject element)
    {
        validate(element);
        return target.set(index, element);
    }

    protected abstract void validate(MyBusinessObject element);

}

...and few implementations:

class FooValidatingDecorator extends ValidatingListDecorator {

    public FooValidatingDecorator(List<MyBusinessObject> target)
    {
        super(target);
    }

    @Override
    protected void validate(MyBusinessObject element)
    {
        //throw if "foo" not met
    }
}

class BarValidatingDecorator extends ValidatingListDecorator {

    public BarValidatingDecorator(List<MyBusinessObject> target)
    {
        super(target);
    }

    @Override
    protected void validate(MyBusinessObject element)
    {
        //throw if "bar" not met
    }
}

Want to only validate foo?

List<MyBusinessObject> list = new FooValidatingDecorator(rawArrayList);

Want to validate both foo and bar?

List<MyBusinessObject> list = 
  new BarValidatingDecorator(new FooValidatingDecorator(rawArrayList));
like image 187
Tomasz Nurkiewicz Avatar answered Nov 11 '22 13:11

Tomasz Nurkiewicz


If you would like to enforce this I don't see why not (although you should check the return value of the add method whenever you do add to make sure that it succeeded).

This is a good way to get rid of that redundant logic which may or may not stick around in later software iterations.

like image 37
Jesus Ramos Avatar answered Nov 11 '22 12:11

Jesus Ramos