1

I have a use case with 7-8 if else. Sample use case:

 String type;
 List < Entity > entityList;


 if (type.equals("A")) {
    ClassA a = new ClassA();
    a.performTask();

    for (Entity e: entitylist) {
        // do some task
    }
 }

  else if (type.equals("B")) {
    ClassB b = new ClassB();
    b.performTask();

    for (Entity e: entitylist) {
        // do some different task
    }
 }

Which java design pattern fits best in this case as I want to eliminate this if else ladder?

Dev
  • 13,492
  • 19
  • 81
  • 174
  • 3
    [Strategy pattern](https://en.wikipedia.org/wiki/Strategy_pattern)? – Andy Turner Oct 08 '15 at 08:45
  • IMHO _pattern_ is a concept too big for your problem. Just design the method in a way that you can `return` at the end of each `if`, thus eliminating the need for `else`s. Ref. my answer to [Setting a default value at the beginning or set it explicitly if no if case matches](https://stackoverflow.com/questions/31113280/setting-a-default-value-at-the-beginning-or-set-it-explicitly-if-no-if-case-ma/31113698#31113698). – f_puras Oct 08 '15 at 08:50
  • If you are happy to "move" the if/else ladder to another class, `Factory` pattern could work here. Your business logic will be cleaner, and your code polymorphic. – vikingsteve Oct 08 '15 at 08:53
  • 1
    I don't know suitable design pattern for this, but just to eliminate if-else you can use switch statement. – hata Oct 08 '15 at 08:53
  • I think you can use interface and dynamic instantiate an object (http://stackoverflow.com/questions/13868986/dynamically-create-an-object-in-java-from-a-class-name-and-set-class-fields-by-u) – Anh Nguyen Ngoc Oct 08 '15 at 09:00

4 Answers4

1

If you really want to use a design pattern in this case I would suggest the Visitor Pattern. This is the one (as far as I know) which is best suited for this kind of "type-checking". You can find an good example here. But as alreday stated in the comments, I agree that a pattern would be to much overhead in this case.

Westranger
  • 1,308
  • 19
  • 29
1

Introduce an interface for all tasks and use a factory pattern. The factory can use a map internally. E.g.

public class TaskFactory {

    private Map<String, Class<? extends Task>> taskTypeMap = new HashMap<String, Class<? extends Task>>();

    public TaskFactory() {
        taskTypeMap.put("A", ATask.class);
        taskTypeMap.put("B", BTask.class);
    }

    public Task createTask(String type) {
        Class<? extends Task> taskType = taskTypeMap.get(type);

        if (taskType == null) {
            throw new IllegalArgumentException("Task type " + type
                    + " is not supported");
        }

        try {
            return taskType.newInstance();
        } catch (Exception e) {
            throw new IllegalStateException(
                    "Unable to instantiate Task of type " + taskType, e);
        }
    }

}

Your client code will then change to

String type = ...;
List<Entity> entityList = ...;

TaskFactory taskFactory = new TaskFactory();
Task task = taskFactory.createTask(type);

task.performTask();

for (Entity e: entitylist) {
    // do some task
}
René Link
  • 48,224
  • 13
  • 108
  • 140
0

A factory implementation could look like this:

public class WidgetFactory {
    public static void main(String[] args) {
        String type = "A";
        List<Entity> entityList = new ArrayList<>();

        Widget widget = WidgetFactory.createWidget(type);
        widget.performTask();
        for (Entity e : entityList) {
            widget.performTaskOnEntity(e);
        }
    }

    private static Widget createWidget(String type) {
        switch (type) {
            case "A":
                return new ClassA();
            case "B":
                return new ClassB();
            default:
                throw new IllegalArgumentException("Unknown type: " + type);
        }
    }

    private interface Widget {
        void performTask();
        void performTaskOnEntity(Entity entity);
    }

    private static class ClassA implements Widget {
        public void performTask() { }
        public void performTaskOnEntity(Entity entity) { }
    }

    private static class ClassB implements Widget {
        public void performTask() { }
        public void performTaskOnEntity(Entity entity) { }
    }

    private static class Entity {
    }
}
vikingsteve
  • 38,481
  • 23
  • 112
  • 156
0

You need the following patterns to make this design generic -

  1. Factory Pattern - Make a BaseClass. Class A, B(and C...) should extend this BaseClass. BaseClass should have a single abstract method performTask() which should be implemented by Class A & Class B (and C...) to make them concrete implementations
  2. Template Pattern - Lets define a base class for the template - BaseTemplateClass. Now, the reason why I am using a template pattern here is that you have 3 distinct steps in the flow here -

    Step 1- Create a new instance of the BaseClass(we will use Factory defined in step 1 to do this). CreateInstance() will be the first concrete method in the TemplateBaseClass which will take in the string param identifier and call the factory. Since, this a fixed step we will keep CreateInstance() as a concrete method.

    Step 2 - BaseClass's performTask() will be called. This will be abstract.

    Step 3 - processEntityList() method will contain the for loop. This will also be a concrete method containing the call to the for loop - for (Entity e: entitylist){..}

    Lastly, we need a method execute() in BaseTemplateClass which calls the 3 methods defined in Steps 1, 2 & 3.

An implementation of the BaseTemplateClass will have only the implementation of the abstract method performTask() as per its needs - in this case just invoking the A, B (or C...)'s performtask(). But this will be helpful if more needs to be done with A, B(or C...).

The client(in classical terms) just needs to call execute() method of an instance of suitable implementation of BaseTemplateClass and rest will happen as per the design above.

Dhruv Rai Puri
  • 1,335
  • 11
  • 20