how to avoid “cascading if\case”-statements in a factory class

半世苍凉 提交于 2020-02-05 03:48:07

问题


I have to create an object based on a specific situation. I read, that the solution could be the Factory Pattern, but in my case it has a lot of disadvantages.

For instance: I have an application that manages animals. At some point a client gives to me a list of animals that must be created. A solution by using the factory pattern should be:

//PRODUCTS
public interface  Animal {
    String getCall();
}

public class Dog implements Animal {
    public String getCall() {
        return "Bau";
    }
}

public class Cat implements Animal {
    public String getCall() {
        return "Miao";
    }
}
public class Cow {...}
public class Rooster{...}

public enum AnimalEnum {
    Cat, Dog, Cow, Rooster
}



//FACTORY
public class AnimalFactory {
    public Animal getAnimal (AnimalEnum type){
        Animal retval = null;
        switch (type){
            case Cat:
                retval = new Cat();
                break;
            case Dog:
                retval = new Dog();
                break;
            case Cow:[...]
            case Rooster[...]

        }
        return retval;
    }
}

In my opinion this is a code smell. The problem is the case-statement that I have to write in order to check what type of animal the client wants. Furthermore, if in the future I want to create a new object "Tiger", I have to change all the factory classes.

My question is: is there a way to avoid this situation? Is there a pattern that allows me to create an object based on another parameter without having "cascading if\case-of" like that?

I was thinking, to use the command pattern, but at the end I still have this situation.


回答1:


The notion of "code smell" and its opposite, "clean code", goes back to Martin Fowler and Robert Martin, see this post for the nuances of olfactory, hygienic and moral simile in the area of software development.

As to the question, your notion that this sort of switch over an enum stinks is consistent with Martin Fowler as of the original edition of "Refactoring: Improving the Design of Existing Code", but he retracted it in the 2015 edition. There's no consensus on that, also not among several 100K reputation contributors, e.g. @tim-biegeleisen proclaims that there is no stink, @mark-seemann insists that there is, also after 2015, and necessarily so as long as the universe exists, according to his inclination.

As of the grievance you feel about that particular piece of code, you could move the instance creation to the Enum itself, thus avoid the switch statement and also avoid to forget to add additional switch branches when you augment the enum.

public enum AnimalEnum {
    Cat(Cat::new), 
    Dog(Dog::new), 
    Cow(Cow::new), 
    Rooster(Rooster::new);

    private final Supplier<Animal> createAnimal;

    public Animal createInstance() {
        return createAnimal.get();
    }

    AnimalEnum(Supplier<Animal> factory) {
        this.createAnimal = factory;
    }
}

Anticipating that this proposal will stir controversy based on the dispositions of individual olfactory organs and centered around the smelly/clean dichotomy I'd like to refer to this post dedicated solely to the question, whether functions as members of enums may enrage our noses in justifiable ways or not.



来源:https://stackoverflow.com/questions/58570377/how-to-avoid-cascading-if-case-statements-in-a-factory-class

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