Search code examples
javacode-duplicationdeduplication

Deduplicate this java code duplication


I have about 10+ classes, and each one has a LUMP_INDEX and SIZE static constant. I want an array of each of these classes, where the size of the array is calculated using those two constants. At the moment i have a function for each class to create the array, something along the lines of:

private Plane[] readPlanes()
{
    int count = header.lumps[Plane.LUMP_INDEX].filelen / Plane.SIZE;
    Plane[] planes = new Plane[count];
    for(int i = 0; i < count; i++)
        planes[i] = new Plane();

    return planes;
}

private Node[] readNodes()
{
    int count = header.lumps[Node.LUMP_INDEX].filelen / Node.SIZE;
    Node[] nodes = new Node[count];
    for(int i = 0; i < count; i++)
        nodes[i] = new Node();

    return nodes;
}

private Leaf[] readLeaves()
{
    int count = header.lumps[Leaf.LUMP_INDEX].filelen / Leaf.SIZE;
    Leaf[] leaves = new Leaf[count];
    for(int i = 0; i < count; i++)
        leaves[i] = new Leaf();

    return leaves;
}

etc. There are 10 of these functions, and the only differences is the class type, so as you can see, there's a ton of duplication.

Does any one have any ideas on how to avoid this duplication? Thanks. (I asked a similar question before, but i guess the way i asked it was a bit off)


Solution

  • Okey doke ... I've tested this to make sure, and I believe it does what you're looking for.

    You need an interface:

    public interface MyInterface
    {
        public int getSize();
        public int getLumpIndex();
    }
    

    Your classes implement that interface:

    public class Plane implements MyInterface
    {
    
        ...
        public int getSize()
        {
            return SIZE;
        }
    
        public int getLumpIndex()
        {
            return LUMP_INDEX;
        }
    
    }
    

    In the class that header is an instance of, you have ...

    public <E extends MyInterface> E[] 
        getArray(Class<E> c, MyInterface foo)
    {
        int count = lumps[foo.getLumpIndex()].filelen / foo.getSize();
        E[] myArray = (E[]) Array.newInstance(c, count);
        for(int i = 0; i < count; i++)
             myArray[i] = c.newInstance();
        return myArray;
    }
    

    You could call it from say, your Plane class as:

    Plane[] p = header.getArray(Plane.class, this);
    

    I think? :) Can someone look at this and see if I'm off?

    (EDIT: Becasue I've tested it now - That works)

    On an additional note, you could eliminate the getters in each class by making getArray() take the size and index as arguments:

    public <E extends MyInterface> E[] 
        getArray(Class<E> c, int size, int index)
    {
        int count = lumps[index].filelen / size;
        E[] myArray = (E[]) Array.newInstance(c, count);
        for(int i = 0; i < count; i++)
             myArray[i] = c.newInstance();
        return myArray;
    }
    

    And call it as:

    Plane p[] = header.getArray(Plane.class, SIZE, LUMP_INDEX);
    

    from inside your classes. The interface just becomes empty to provide the generic type and you don't have to define the getter methods.

    OR (last edit I promise, but this does give you choices and explains a bit about generics)

    Ditch the interface. What this removes is some sanity checking because the method doesn't care what type of object you give it:

    public <E> E[] 
        getArray(Class<E> c, int size, int index)
    {
        ...
    

    Now you don't have to define the interface or implement it, you just call:

    Plane p[] = header.getArray(Plane.class, SIZE, LUMP_INDEX);