views:

90

answers:

1

First, I'm sorry for the question title but I can't think of a better one to describe my problem. Feel free to change it :)

Let's say I have this abstract class Box which implements a couple of constructors, methods and whatever on some private variables. Then I have a couple of sub classes like BoxA and BoxB. Both of these implement extra things.

Now I have another abstract class Shape and a few sub classes like Square and Circle.

For both BoxA and BoxB I need to have a list of Shape objects but I need to make sure that only Square objects go into BoxA's list and only Circle objects go into BoxB's list.

For that list (on each box), I need to have a get() and set() method and also a addShape() and removeShape() methods.

Another important thing to know is that for each box created, either BoxA or BoxB, each respectively Shape list is exactly the same. Let's say I create a list of Square's named ls and two BoxA objects named boxA1 and boxA2. No matter what, both boxA1 and boxA2 must have the same ls list.

This is my idea:

public abstract class Box {
    // private instance variables

    public Box() {
        // constructor stuff
    }

    // public instance methods
}

public class BoxA extends Box {
    // private instance variables

    private static List<Shape> list;

    public BoxA() {
        // constructor stuff
    }

    // public instance methods

    public static List<Square> getList() {
        List<Square> aux = new ArrayList<Square>();

        for(Square s : list.values()) {
            aux.add(s.clone()); // I know what I'm doing with this clone, don't worry about it
        }

        return aux;
    }

    public static void setList(List<Square> newList) {
        list = new ArrayList<Square>(newList);
    }

    public static void addShape(Square s) {
        list.add(s);
    }

    public static void removeShape(Square s) {
        list.remove(list.indexOf(s));
    }
}

As the list needs to be the same for that type of object, I declared it as static and all methods that work with that list are also static. Now, for BoxB the class would be almost the same regarding the list stuff. I would only replace Square by Triangle and the problem was solved. So, for each BoxA object created, the list would be only one and the same. The same would happen for each BoxB object created, but with a different type of list of course.

So, what's my problem you ask? Well, I don't like the code... The getList(), setList(), addShape() and removeShape() methods are basically repeated for BoxA and BoxB, only the type of the objects that the list will hold is different. I wanted to avoid, somehow, all those methods "duplication".

I can't think of way to do it in the super class Box instead. Trying to do it statically too, using Shape instead of Square or Triangle, wouldn't work because the list would be only one for all BoxA and BoxB objects, and I need it to be only one but for each sub class of Box.

How could I do this differently and better?

P.S: I could not describe my real example because I don't know the correct words in English for the stuff I'm doing, so I just used a box and shapes example, but it's basically the same.

A: 

You could try something like:

abstract class Box<E extends Shape> {

    abstract protected List<E> getList();

    abstract protected void setList(List<E> list);

    protected static <T extends Shape> List<T> getCommon(Box<T> box) {
        List<T> aux = new ArrayList<T>();
        for (T s : box.getList()) {
            aux.add((T) s.clone());
        }
        return aux;
    }

    protected static <T extends Shape> void setCommon(Box<T> box, List<T> newList) {
        // do something on newList here if needed as common functionality
        box.setList(new ArrayList<T>(newList));
    }
}
class BoxA extends Box<Square> {
    private static List<Square> list;

    @Override
    protected List<Square> getList() {
        return list;
    }

    @Override
    protected void setList(List<Square> list) {
        this.list = list;
    }

    public static List<Square> get() {
        return getCommon(new BoxA());
    }

    public static void set(List<Square> newList) {
        setCommon(new BoxA(), newList);
    }
}

It's a bit hackish but it allows you to use the parent class to hold some common functionality. You'll still have to have the final methods in the child classes, but all you do in them is call the parent methods giving them a new instance of your current class, so that the parent method can infer the generic type from that. Notice that getList() and setList() are not static to allow them to have the type parameter of Box, and they're protected so they're not visible outside. They act as getter setter for a private static variable (that each extending class should have), to allow access to the parent to do the common work on a variable that belongs to the children.

The get() and set() method use new BoxA() (or new BoxB()) to allow access to the static list, while also passing the type parameter to the parent. Since the list is static it doesn't matter what instance returns it. The common methods have they're own type parameter which is not the one the class have; you could in fact call getCommon(new BoxB()) from inside BoxA, so it will be your job to ensure that you call the right one.

Andrei Fierbinteanu