Creating a factory method in Java that doesn't rely on if-else

前端 未结 10 1504
走了就别回头了
走了就别回头了 2020-11-30 23:37

Currently I have a method that acts as a factory based on a given String. For example:

public Animal createAnimal(String action)
{
    if (action.equals(\"M         


        
10条回答
  •  时光说笑
    2020-12-01 00:11

    Use Scannotations!

    Step 1. Create an annotation like below:

    package animal;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AniMake {
        String action();
    }
    

    Note that the RetentionPolicy is runtime, we'll be accessing this via reflection.

    Step 2. (Optional) Create a common super class:

    package animal;
    
    public abstract class Animal {
    
        public abstract String greet();
    
    }
    

    Step 3. create the subclasses with your new annotation:

    package animal;
    
    @AniMake(action="Meow")
    public class Cat extends Animal {
    
        @Override
        public String greet() {
            return "=^meow^=";
        }
    
    }
    ////////////////////////////////////////////
    package animal;
    
    @AniMake(action="Woof")
    public class Dog extends Animal {
    
        @Override
        public String greet() {
            return "*WOOF!*";
        }
    
    }
    

    Step 4. Create the factory:

    package animal;
    
    import java.util.Set;
    
    import org.reflections.Reflections;
    
    public class AnimalFactory {
    
        public Animal createAnimal(String action) throws InstantiationException, IllegalAccessException {
            Animal animal = null;
            Reflections reflections = new Reflections("animal");
            Set> annotated = reflections.getTypesAnnotatedWith(AniMake.class);
    
            for (Class clazz : annotated) {
                AniMake annoMake = clazz.getAnnotation(AniMake.class);
                if (action.equals(annoMake.action())) {
                    animal = (Animal) clazz.newInstance();
                }
            }
    
            return animal;
        }
    
        /**
         * @param args
         * @throws IllegalAccessException 
         * @throws InstantiationException 
         */
        public static void main(String[] args) throws InstantiationException, IllegalAccessException {
            AnimalFactory factory = new AnimalFactory();
            Animal dog = factory.createAnimal("Woof");
            System.out.println(dog.greet());
            Animal cat = factory.createAnimal("Meow");
            System.out.println(cat.greet());
        }
    
    }
    

    This factory, can be cleaned up a bit e.g. deal with the nasty checked exceptions etc.
    In this factory, I've used the Reflections library.
    I did this the hard way, i.e. I didn't make a maven project and I had to add the dependencies manually.
    The dependencies are:

    • reflections-0.9.5-RC2.jar
    • google-collections-1.0.jar
    • slf4j-api-1.5.6.jar
    • nlog4j-1.2.25.jar
    • javassist-3.8.0.GA.jar
    • dom4j-1.6.jar

    If you skipped Step 2, then you'll need to change the factory method to return Object.
    From this point on you can keep adding subclasses, and as long as you annotating them with AniMake (or whatever better name you come up with), and place them in the package defined in the Reflections constructor (in this case "animal"), and leave the default no-args constructor visible, then the factory will instantiate your classes for you without having to be changed itself.

    Here's the output:

    log4j:WARN No appenders could be found for logger (org.reflections.Reflections).
    log4j:WARN Please initialize the log4j system properly.
    *WOOF!*
    =^meow^=
    

提交回复
热议问题