The best way to implement Factory without if, switch

[亡魂溺海] 提交于 2019-12-02 00:13:49
Misha

If you are using java 8, you can set up an enum like this:

enum AnimalFarm {
    Meow(Cat::new),
    Woof(Dog::new);

    public final Supplier<Animal> factory;
    private AnimalFarm(Supplier<Animal> factory) {
        this.factory = requireNonNull(factory);
    }
}

......

Animal dog = AnimalFarm.valueOf("Woof").factory.get();

You could even have the enum implement Supplier<Animal> and then do AnimalFarm.valueOf("Meow").get();.

Java 8 introduces some nice lambdas and functional interfaces that makes this really easy. It also avoids a lot of the ugly boilerplate code you would have had to write in previous Java versions. One thing that wasn't immediately clear was how to handle creating objects with constructors that take multiple arguments. Most of Java's functional interfaces allow either one argument or no arguments at all. In this case the solution is to implement your own functional interface.

Imagine your Animal classes all had constructors that looked like this:

public Dog(String name, int age) {
    // constructor stuff
}

You can create a function interface called something like Factory in your factory class that has 3 parameters. The first two parameters would be the types of objects you're going to pass to the constructor and the third will be what you're going to return.

public class AnimalFactory {

    private static final Map<String, Factory<String, Animal>> factoryMap;

    static {
        Map<String, Factory<String, Animal>> realMap = new HashMap<>();
        factoryMap = Collections.unmodifiableMap(realMap);

        realMap.put("MEOW", Cat::new);
        realMap.put("WOOF", Dog::new);
    }

    public static Animal createAnimal(String action) {
        return factoryMap.get(action).create(node);
    }

    @FunctionalInterface
    private interface Factory<T, R, S> {
        S create(T obj1, R obj2);
    } 
}

If all your classes use no-arg constructors then you can drop the custom interface and use something like Supplier instead. But everything else should more or less stay the same.

Try something like this:

class MyFactory {
    private static final Map<String,Animal<?>> factoryMap =
        Collections.unmodifiableMap(new HashMap<String,Class>() {
            put("Meow", Cat.BASE_INSTANCE);
            put("Woof", Dog.BASE_INSTANCE);
    });

    public Animal createAnimal(String action) {
        return factoryMap.get(action).instance();
    }
}

interface Animal<SELF> {
    public SELF instance();
}

class Cat implements Animal<Cat> {
    public static final Cat BASE_INSTANCE = new Cat();
    public Cat() {}
    public Cat instance(){
        return new Cat();
    }
}
// And a similar Dog class

This does not use reflection, if, or switch, at all.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!