15

I am trying to understand factory pattern.If there are many implementation then my factory pattern will have lot of if else or switch cases. And also every time I introduce a new implementation i should change my factory code

Like in below examples if lets assume dog duck are implementing Pet interface like tomorrow if many animals implement pet interface my factory frows long with lot of if else else if code or switch case. Is there any way to solve this with bringing more dynamic approach?

package com.javapapers.sample.designpattern.factorymethod;

//Factory method pattern implementation that instantiates objects based on logic
public class PetFactory {

    public Pet getPet(String petType) {
        Pet pet = null;

        // based on logic factory instantiates an object
        if ("bark".equals(petType))
            pet = new Dog();
        else if ("quack".equals(petType))
            pet = new Duck();
        return pet;
    }

If the animals grows

if ("bark".equals(petType))
    pet = new Dog();
else if ("quack".equals(petType))
    pet = new Duck();
else if ("mno".equals(petType))
    pet = new MNO();
else if ("jkl".equals(petType))
    pet = new JKL();
else if ("ghi".equals(petType))
    pet = new GHI();
else if ("def".equals(petType))
    pet = new DEF();
......
else if ("abc".equals(petType))
    pet = new ABC();
return pet
ericbn
  • 10,163
  • 3
  • 47
  • 55
constantlearner
  • 5,157
  • 7
  • 42
  • 64
  • Well, there is nothing to do. Its build factory – Maxim Shoustin Aug 14 '13 at 15:29
  • [Differences between Abstract Factory Pattern and Factory Method](https://stackoverflow.com/questions/5739611/differences-between-abstract-factory-pattern-and-factory-method/50786084#50786084) – jaco0646 Oct 12 '18 at 16:19

5 Answers5

24

I think there is a dynamic approach:

  1. In your factory you need a Map<String, Class<? extends Pet>>
  2. In static constructor of every class, which extends Pet, register it with such map.
  3. Than creating a class will be just map.get(pet).newInstance ( you'd have to check for nulls, of course)
Eugene Ryzhikov
  • 17,131
  • 3
  • 38
  • 60
  • 10
    One caveat here, though, is the class must be loaded for its static constructor to be executed. This does not happen automatically if the class is never referenced. If you've ever run into an API where you had to do an arbitrary `Class.forName("")` something first, that's a demonstration of this. This isn't prohibitive, it's just something to note; something somewhere will have to load the classes. – Jason C Aug 14 '13 at 15:56
  • with this approach, when you add new implementation class, you have to add that class to your `Map`. That's right? So you still must modify your Factory code. – Bui Minh Duc May 01 '18 at 09:58
11

The idea behind the factory pattern is to let you dynamically instantiate objects whose types you don't necessarily know about at design time.

Having a big if block defeats that purpose.

The effective way to implement this pattern is to also have a factory for each type, which implements a base factory interface and has the ability to instantiate a new object of that type (by the way, in Java, the built-in Class is an example of such a factory).

Then you register a map of names/ids/etc. to instances of these individual factories at runtime. When it's time to instantiate one of the types, you look up the factory in the map by name and use that to instantiate a new object of that type.

How you register individual factories in the map is totally up in the air. You could register some explicitly, you could scan a configuration file, etc.

Essentially you want to replace your if block with a map that is dynamically created at runtime.

You don't even need to solely use a preregistered "map" - sometimes it may be appropriate to figure out how to create an object with a given name on the fly, or a combination of the two (e.g. Class.forName() searches the class path if it can't find the class already loaded). The point is the translation of the name to the class type can take place without the base factory actually knowing what the class type is.

It's worth noting that Java reflection provides a very workable factory implementation already via Class.forName() and/or Class.newInstance(), so consider using that instead of reinventing the wheel if it makes sense.

Jason C
  • 38,729
  • 14
  • 126
  • 182
4

use reflection

public Pet getPet(String petType)
{
     Pet _pet = (Pet)Class.forName(petType).newInstance();
     return _pet;
}

you need to change your arguments from 'bark','quack' to 'Dog' and 'Duck' etc

wonde
  • 1,181
  • 10
  • 20
1

I've been banging my head a little on this since I had a similar problem, and finally I came with a solution based on Reflections Library (pay attention to the final S in Reflections!)

It can be applied to your problem IF all your pet subclasses have an attribute that can be used to discriminate them, for example

   public String petType;

The method of your factory could be the following:

        public static Pet getPet(String _petType) {
    String packageName = "your.package.with.pet.classes";

    Reflections reflections = new Reflections(packageName);

    Set<Class<? extends Pet>> allPets = reflections
            .getSubTypesOf(Pet.class);

    Iterator<Class<? extends Pet>> it = allPets.iterator();

    while (it.hasNext()) {
        try {
            Pet pet = it.next().newInstance();
            if (pet.petType.equals(_petType))
                return pet;
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    System.out.println("Pet " + _petType
            + " not yet implemented in package " + packageName);
    return null;
}

This method would remain unaffected if new Pets were defined.

Pros:

  • It doesn't need further modification to Pet subclasses, neither any sort of initialization/registration on a Map structure maintained by the Factory

  • It's more general than the solution based on Java Reflection API because you can discriminate Pet subclasses upon some attributes instead of the class name

Cons:

  • It creates local instances of all Pet sub-classes to find the appropriate one. On this side, I'm confident that this approach can be improved.
1

In Java8, there's a Supplier interface that supports this quite well. Reflections and manual invocations can be avoided and a cleaner approach can be used.

Something like this for Factory:

public class DynamicSupplierTierFactory {

    public static final Map<String, Supplier<? extends Tier>> registeredSuppliers = new HashMap<>();

    static {
        registeredSuppliers.put("blue", new BlueSupplier());
        registeredSuppliers.put("silver", new SilverSupplier());
        registeredSuppliers.put("golden", new GoldenSupplier());
    }

    public static void registerSupplier(String type, Supplier<? extends Tier> supplier){
        registeredSuppliers.put(type, supplier);
    }

    public static Tier getTier(String type){
        Supplier<? extends Tier> supplier = registeredSuppliers.get(type);
        return supplier != null ? supplier.get():null;
    }
}

Suppliers will be like:

public class BlueSupplier implements Supplier<Tier> {
    @Override
    public Tier get() {
        return new Blue();
    }
}

This can be seen here in more detail: https://medium.com/@mhd.durrah/factory-pattern-the-dynamic-way-with-java-8-3ca5ab48a9cf

Ankit Bhatnagar
  • 745
  • 5
  • 16