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 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 {
}
}
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
}
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.
You need the following patterns to make this design generic -
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.