1
votes

I am currently doing some research on the factory design patterns and have listed a brief description of my understanding of each. Please correct me if any of them are not correct.

1) Factory - simple and not actually an official design pattern, usually one class which has one or more methods (sometimes static) accepting a parameter to determine which subclass of the abstract type to return.

2) Factory Method - officially a pattern and uses a abstract factory class. For each product of the intended return type, create an associated factory class and either implement or override the required methods. In the client code, although the variable is declared as the abstracted factory, it is instantiated with the concrete implementation.

3) Abstract Factory - a pattern to return more than one type of object through various methods that are either related or dependent upon each other.

My question is, i have been using the factory (not officially a pattern) with shared methods. However, i am looking at picking up the factory method and using this, but what i cannot get my head round is how i would determine which subclass of the abstracted factory class to use to create my product. From what i understand so far, you still use the new keyword to assign a concrete class to the variable declared as the abstract factory class.

For example:

Dim factory1 As IFactory = New ConcreteFactory1

Dim factory2 As IFactory = New ConcreteFactory2

If i want to dynamically determine which IFactory i would like to return based on a database record for instance, how would i do that? Am i better off leaving the code using the Factory pattern and not bother with the Factory Method pattern? I want to avoid doing a select case or if statement in the client code. Could i wrap the Factory Method pattern in a Factory pattern?

Hope i have made sense

1

1 Answers

1
votes

Well, this quite depends on the language you'll be using in order to support the idea of the design. I'll portray the idea in Java, let's start.

You have the following:

AbstractFacotry instance1 = new ConcreteFacotry1();
AbstractFacotry instance2 = new ConcreteFacotry2();

You want to avoid having to put ConcretFactory#. That's why you'll use a Factory class that is in charge of giving you the correct ConcretFactory# instance based on a parameter (in your case, that database record you mentioned). I'll try to be as brief yet explaining as possible, hope I don't lose anybody in the process.

I'll start with the creation of the abstract class AbstractFactory and concrete classes that extend from it for the purpose of the example.

public abstract class AbstractFactory {}

Of course yours will be much more detailed, I just need it instantiated to run a couple of tests. Now let's great the classes that extend from it:

public class ConcreteFactory1 extends AbstractFactory {}

I won't paste the code for the other ones, they are all the same. Here are the classes I created: ConcreteFactory1, ConcreteFactory2, ConcreteFactory3, ConcreteFactory4.

Next is the class that is in charge of giving you the concrete instance of AbstractFactory based on your database parameter. This implementation assumes that there wont' be more than 15 or so database parameters. And also that said database parameter can be translated into a String.

public class FactoryInstantiator {
public AbstractFactory optionOne(){
    return new ConcreteFactory1();
}

public AbstractFactory optionTwo(){
    return new ConcreteFactory2();
}

public AbstractFactory optionThree(){
    return new ConcreteFactory3();
}

public AbstractFactory optionFour(){
    return new ConcreteFactory4();
}
}

Obviously the name option# is the String translation of your database parameter.

The last thing, is to create the class that is in charge of calling the correct method based on the database parameter. Here is where the juice lies, and where you avoid your if-elses and your switches. This is the class you'll be calling from the client.

public class FactoryHandler {

public AbstractFactory getInstance(String databaseParameter) {
    try {
        return  getConcreteInstance(FactoryInstantiator.class, databaseParameter);
    } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | 
             InvocationTargetException | NoSuchMethodException ex) {
        Logger.getLogger(FactoryHandler.class.getName()).log(Level.SEVERE, null, ex);
    }
    throw new RuntimeException("Could not determine concrete instance based on data base parameter");
}


private AbstractFactory getConcreteInstance(Class<FactoryInstantiator> factoryInstantiator, String databaseParameter) 
                    throws InstantiationException, IllegalAccessException, IllegalArgumentException, 
                    InvocationTargetException, NoSuchMethodException {    
    Class[] methodParameterTypes=null;
    Object instance = factoryInstantiator.newInstance();
    Method method = factoryInstantiator.getDeclaredMethod(databaseParameter, methodParameterTypes);
    return  (AbstractFactory) method.invoke(instance);
}

}

Specifically, you'll be calling the getInstance(...) method to get your concrete instance. This method is in charge of capturing any exceptions that may arise from calling the getConcreteInstance(..). This separation is for readability and clearness.

The real deal is in getConcreteInstance(..) in charge of calling the appropriate method in FactoryInstantiator based on the dbParameter.

Let's get to using this in the main class as follows:

public class Main {

public static void main(String[] args) {
    ArrayList<AbstractFactory> factories = new ArrayList<>();
    ArrayList<String> dbParameters = getDBparameters();
    FactoryHandler factoryHandler = new FactoryHandler();

    for(String dbParameter:dbParameters)
        factories.add( factoryHandler.getInstance(dbParameter) );

    for(AbstractFactory factory : factories)
        System.out.println(factory.getClass());

}

private static ArrayList<String> getDBparameters(){
    ArrayList<String> dbparameters = new ArrayList<>();
    dbparameters.add("optionOne");
    dbparameters.add("optionTwo");
    dbparameters.add("optionThree");
    dbparameters.add("optionFour");
    return dbparameters;
}

}

Here is the printout:

class ConcreteFactory1
class ConcreteFactory2
class ConcreteFactory3
class ConcreteFactory4

And there you have it. Hope it helps.