Guut Boy
Guut Boy

Reputation: 155

Is there a simple way to combine Factories in Java?

Suppose I have interfaces of factory classes FactoryA and FactoryB with methods for producing objects of type A and B respectively, and suppose I have implementations of these interfaces. Now suppose I need a factory class FactoryAB that implements both interfaces, i.e., produces both A and B. Is there a simple way to combine the implementations of interfaces FactoryA and FactoryB to get a FactoryAB?

Edit: This is motivated by a situation where a class needs a large number of factories to produces a large number of different objects. In this case it seems to me more convenient to just have a single large factory that produces all the needed components instead of having to keep track of many small factories.

The main goal is to not have to write too much code to achieve this. Perhaps, some other pattern is more appropriate for this scenario?

Edit2: Essentially I would like to go from having bloated code like:

public class Consumer {
 FactoryA factoryAImpl;
 FactoryB factoryBImpl;
 // ...
 FactoryY factoryYImpl;
 FactoryZ factoryZImpl;

 public Consumer(FactoryA factoryA, FactoryB factoryB, ..., FactoryZ factoryZ) {
   // ...
 }
 //...
}

To having compact/simple code like:

public class Consumer {
 FactoryAtoZ factoryAtoZImpl;

 public Consumer(FactoryAtoZ factoryAtoZ) {
   // ...
 }
 //...
}

Upvotes: 0

Views: 608

Answers (3)

tddmonkey
tddmonkey

Reputation: 21184

You could do this by dynamically generating a Proxy and delegating the method calls to the concrete objects if and only if you have a reliable way of determining which delegate to hand this off to.

First of all you're going to need an interface that combines your existing factories

public interface FactoryAB extends FactoryA, FactoryB {
}

(note that lack of methods)

Then you need to create the Proxy

    DelegatingHandler handler = new DelegatingHandler(new ConcreteFactoryA(), new ConcreteFactoryB());
    FactoryAB factoryAb = (FactoryAB)Proxy.newProxyInstance(handler.getClass().getClassLoader(), new Class[]{FactoryAB.class}, handler);

And finally the InvocationHandler to do the actual method interception for you

public class DelegatingHandler implements InvocationHandler {
    private final List<Object> factoryDelegates;

    public DelegatingHandler(Object ... factories) {
        factoryDelegates = Arrays.asList(factories);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        for (Object factory : factoryDelegates) {
            Method[] declaredMethods = factory.getClass().getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                if (declaredMethod.getReturnType().equals(method.getReturnType())) {
                    return method.invoke(factory, args);
                }
            }
        };
        return null;
    }
}

This works by providing a list of concrete factories to delegate to, and on every method call looks at these factories to find one that has a method that matches the return type. When it finds one it will invoke the method you called on the delegate factory and return whatever it returns.

I suspect you might need more involved checking to find the right delegate, but only you know that.

Using this you can add new factories to your delegate by merely adding to the FactoryAB interface and injecting the required delegate into the DelegatingHandler, no further work required.

You should note that using this method, all of your calls are now being routed by reflection instead of direct method calls

Upvotes: 1

baymax
baymax

Reputation: 159

You can use composite concept and factory method pattern to combine both FactoryA and FactoryB interfaces. Assuming you have a common interface AB and public A getA() the factory method for FactoryA and public B getB() the factory method for FactoryB:

public class ABFactory implements FactoryA, FactoryB {
    public AB GetAB(InterfaceType type) {
        AB implementation = null;
        switch (type)
        {
            case InterfaceType.AType:
                implementation = getA();
                break;
            case InterfaceType.BType:
                implementation = getB();
                break;
            default:
                break;
        }
        return implementation;
    }
   public A getA() {
      return new FactoryAImpl().getA();
   }

   public B getB() {
      return new FactoryBImpl().getB();
   }

}

assuming

public enum InterfaceType {
    AType,
    BType
}

Upvotes: 0

Julia Leder
Julia Leder

Reputation: 816

You could do it with Composition:

public class FactoryAB implements FactoryA, FactoryB
{
   FactoryA factoryAImpl = new FactoryAImplementor();
   FactoryB factoryBImpl = new FactoryBImplementor();

   public ObjectA getObjectA()  {
        return factoryAImpl.getObjectA();
   }

   public ObjectB getObjectB()  {
        return factoryBImpl.getObjectB();
   }
}

Upvotes: 4

Related Questions