Search code examples
javaarraylistabstractionreusability

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 ?


Solution

  • 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));