mardi 5 juillet 2016

An extendable factory class with generics and reflection

There's a class MyFactory which creates a MyObject. Instantiation is done using reflection, next setters are called. By our convention the MyObject class needs to has a public default constructor (i.e. without parameters).

Our MyFactory looks like this:

public class MyFactory extends AbstractFactory<MyObject>
{
  public MyFactory()
  {
    super(MyObject.class);    
  }

  @Override
  public MyObject createObject()
  {
    MyObject objt = super.createObject();
    objt.setStuff(stuff);
    return objt;
  }
}

It uses a simple abstract factory:

public abstract class AbstractFactory <T>
{
  private Class<? extends T> clazz;

  public AbstractFactory(Class<? extends T> clazz)
  {
    this.clazz = clazz;
  }

  public T createObject()
  {
    return clazz.newInstance();
  }
}

The problems began when people wanted to override the factory to create objects that are extended of MyObject (e.g. MyExtendedObject). We added a constructor to facilitate this.

protected MyFactory(Class<? extends MyObject> clazz)
{
  super(clazz);
}

And in first instance that was enough to create extended factories, such as:

public class MyExtendedFactory extends MyFactory
{
  public MyExtendedFactory()
  {
    super(MyExtendedObject.class);
  }

  @Override
  public MyObject createObject()
  {  
    // and this is a bit uggly
    MyExtendedObject objt = (MyExtendedObject) super.createObject();
    objt.setExtendedStuffToo(extendedStuff);
    return objt;
  }
}

So, everything works, but it's obvious that people wanted to use generics instead. So, we tried to provide generics. And then things started to get complicated.

public class MyFactory <O extends MyObject> extends AbstractFactory<O>
{
  public MyFactory()
  {
    // but now this is uggly and not really correct.
    super(Class<O> extends MyObject.class);    
  }

  protected MyFactory(Class<O> clazz)
  {
    super(clazz);
  }

  @Override
  public O createObject()
  {
    O objt = super.createObject();
    objt.setStuff(stuff);
    return objt;
  }
}

The problem is, that people can actually override this class, but there's a hidden trap. They are not supposed to call the default constructor. In fact the whole constructor design looks broken here.

Any idea how to improve this ? I have the feeling that there must be a best-practice or pattern to fix this.





Aucun commentaire:

Enregistrer un commentaire