Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best Practice to avoid using 'ifs' for calling methods [closed]

Tags:

java

I have legacy code, similar to this

private Resource getResource(String type){
    Resource resource = null;       

    if("A".equals(type)){
        resource = Utils.getResourceA(); //static func      
    }else if("B".equals(type)){
        resource = Utils.getResourceB(); //static func      
    }else if("C".equals(type)){
        resource = Utils.getResourceC(); //static func      
    }else if // etc..

}

As you can see it's something that will be hard to maintain for new types...

What is the best practice to solve that?

I was thinking of creating Class for each resource function that will implement same interface.

Then create a Map<String,IResource> key is the type and IResource will be the instance of class.

But there are problems with this solution.

  • Where to create this map? inside getResource() method? inside class that holds this method?
  • I will be holding instances of classes in memory in cases where I won't be using many of them
  • Instead of simple static method, I will now have a full object (for each method) !

A note: I am not using any frameworks, pure java.

Thanks

like image 834
veg Avatar asked Dec 06 '25 02:12

veg


2 Answers

The usual solution is indeed the one you describe. Example using Java 8 (but that could be written, in a more verbose way, in Java7):

private Map<String, Supplier<Resource>> resourceSuppliers;

public TheClass() {
    resourceSuppliers = new HashMap<>();
    resourceSuppliers.put("A", Utils::getResourceA);
    resourceSuppliers.put("B", Utils::getResourceB);
    resourceSuppliers.put("C", Utils::getResourceC);
    ...
}

private Resource getResource(String type){
    return resourceSuppliers.get(type).get();
}    

The map could also be static final and shared by all instance of TheClass, if the type/resource mapping is the same for all instances:

private static final Map<String, Supplier<Resource>> RESOURCE_SUPPLIERS = createResourceSuppliers();

private static Map<String, Supplier<Resource>> createResourceSuppliers() {
    // same code as in constructor above
}

In older Java versions, you would have to define the Supplier interface, and to define the suppliers by creating them in a more explicit way, for example, using an anonymous inner class:

resourceSuppliers.put("A", new Supplier<Resource>() {
    @Override
    public Resource get() {
        return Utils.getResourceA();
    }
});
like image 83
JB Nizet Avatar answered Dec 08 '25 15:12

JB Nizet


The best way it to turn type into an enum. This will prevent arbitrary types and requires implementing the getResource() method for any new type.

public enum ResourceType {
    A {
        @Override
        public Resource getResource() {
            return Utils.getResourceA();
        }
    },
    B {
        @Override
        public Resource getResource() {
            return Utils.getResourceB();
        }
    },
    C {
        @Override
        public Resource getResource() {
            return Utils.getResourceC();
        }
    };
    public abstract Resource getResource();
}

Update

Converting String to enum is easy (built-in):

ResourceType type = ResourceType.valueOf("A");
like image 30
Andreas Avatar answered Dec 08 '25 15:12

Andreas



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!