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

4

9

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 ?

Sexton answered 20/2, 2012 at 9:30 Comment(0)
S
26

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));
Schonfeld answered 20/2, 2012 at 9:39 Comment(0)
S
1

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.

Shulins answered 20/2, 2012 at 9:33 Comment(2)
I agree with you, but IMO there is one trade-off when extending from a certain list implementation - it's not possible to switch to a different strategy, e.g. replace ArrayList with LinkedList. Delegation would be another option.Legrand
@Legrand Sort of, it's easy enough to switch something that extends arraylist to something that extends something else as long as either the interfaces are exactly the same or somewhat close.Shulins
D
1

I don't think this is a good practice. Consider instead writing a Util-Method in a Util-Class taking two parameters: The array list and the object you would like to add. There you can check whatever you want and can reuse the logic all over your code.

Deferred answered 20/2, 2012 at 9:38 Comment(0)
M
0

Only issue would be if you go to reuse this code and you don't remember you've overriden the ArrayList class, make sure to comment thoroughly.

Mneme answered 20/2, 2012 at 9:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.