John Baum
John Baum

Reputation: 3331

Configuring the order of method execution

I have an api client that is able to make calls to 4 different endpoints. I am running into an issue where i am trying to make it as configurable as possible since each api calling method is independent of the other. So, I would like to leave the order in which they can be called flexible. Here is what I mean:

public class APICaller () {
    public void endpointA () {...
    }

    public void endpointB () {...
    }

    public void endpointC () {...
    }

    public void endpointD () {...
    }
}

I have tried to do this by passing in an array of an Endpoint enum(A,B,C,D) and then iterating over the contents of the array and calling the related method using a switch statement:

for (Endpoint end : passedInArrayOfOrdering) {
    switch(end) {
        case A:
            endpointA();
        ...
    }
}

My above attempt seems a little dirty to me. Is there an existing pattern that lets me configure the order in which to make these endpoint calls?

Upvotes: 1

Views: 80

Answers (4)

Nazar Merza
Nazar Merza

Reputation: 3454

public class EndPoint {

    public void call() {
        // process endpoint
    }
}

Now let the content/data of each endpoint distinguish one end point from the other. For example if they are geometric points, it is their coordinates what matter. No need to create methods like endpointA, endpointB etc. What if you have case where there are hundreds of points, will you create hundreds of methods? No.

...

for (Endpoint end : passedInArrayOfOrdering) {
   end.call();
}

Upvotes: 0

Marco13
Marco13

Reputation: 54639

There isn't really any particular pattern involved here - at best, one could consider it as an instance of the Command Pattern. Fortunately, Java 8 already contains everything that you need to handle this generically: You can just store the methods to be called, using Method References, and then iterate through the list:

import java.util.Arrays;
import java.util.List;

public class ApiCallerExample {
    public static void main(String[] args) {
        ApiCaller a = new ApiCaller();

        List<Runnable> commands = Arrays.asList(
            a::endpointB, 
            a::endpointA, 
            a::endpointD,
            a::endpointC, 
            a::endpointA, 
            a::endpointB);
        commands.forEach(Runnable::run); // Prints "BADCAB"
    }
}

class ApiCaller { 
    public void endpointA () { System.out.print("A"); }
    public void endpointB () { System.out.print("B"); }
    public void endpointC () { System.out.print("C"); }
    public void endpointD () { System.out.print("D"); }
}

Note that the type of the methods here is Runnable, because they are no-argument methods that don't return anything. If your methods required arguments (or returned something), then you could either use the appropriate functional interface, or "pin" your method arguments to the particular invovation at the call site:

Runnable runnable0 = () -> apiCaller.endpointA(argument0, argument1);
commands.add(runnable0);

Upvotes: 1

Ren&#233; Link
Ren&#233; Link

Reputation: 51353

You are using an enum type to switch the logic that is executed, it means that the logic depends on a type - so define it on a type. Make the Endpoint an interface and create specific endpoints. E.g.

public interface Endpoint {
      public void call();
}

And the implementations, e.g.

public class EndpointA implements Endpoint {

    public void call(){
      // endpoint a specific code
    }

}

You can avoid the switch then

for (Endpoint end : passedInArrayOfOrdering) {
   end.call()
}

PS: Everytime you use some kind of type-switch (e.g. with enum) you resolve the code to be execute manually based on a type. The JVM can do this better. It's named dynamic dispatch. From an OO point of view it is simply polymorphism.

Some time ago I wrote a blog ablout type-switches and discussed the pros/cons. See https://www.link-intersystems.com/blog/2015/12/03/enums-as-type-discriminator-anti-pattern/

Upvotes: 1

mcdowella
mcdowella

Reputation: 19601

You could define an interface for making calls

public interface Caller

{ void callSomething(APICaller ac); }

And then define implementations

public class CallA implements Caller
{
  public void callSomething(APICaller ac)
  {
     ac.endpointA()
   }
}

Then pass around objects such as instances of CallA instead of your Endpoint enumeration, so you don't need to use a switch() statement.

Upvotes: 1

Related Questions