Based on various threads on SO (ex. Replacing if else statement with pattern ) I understand that I can replace multiple if\\else statements with the Command
pattern.
My situation is a bit different.
I have a series of Commands
and I need to execute each Command
only if the previous command was unsuccessful.
For example, suppose I need to retrieve text from a hypothetical web page - I could scrape the text directly from the page using screen scraping or I could fetch the text from the API. I would only want to fetch the text from API if screen scraping was not successful. In other words I would only execute the "fetch" command if the "scrape" command didn't work.
In this case, I would test if the scraped String is empty or equal to null and then execute the second Command. If the second command is also unsuccessful I would execute the third Command and so on.
The important point is that we only execute subsequent commands if a certain condition is true/false. The condition is always the same for each Command but the number of commands might grow in the future.
I cannot implement this via the typically suggested route (using a Map
and Command
interface) bec this would not execute the next Command
if the first one failed (it would also be unable to check if a Command
was successful or not)
What design pattern could be used to solve this problem?
As others have said, use the Chain of Responsibility Pattern which can keep trying different implementations in serial until one succeeds.
Let's say your Command has this interface where a handled Request returns a Result
object if it succeeds or null
if it failed ( could also use a boolean or Optional
or whatever you want)
public interface Command{
Result handle( Request request);
}
Then your chained Command could wrap several other Commands and try each one until something works.
public class ChainedCommand implements Command {
//could also be set in a constructor instead
List<Command> commandChain = Arrays.asList( new ScrapperCommand(),
new ApiCommand());
public Result handle( Request request){
Result result = null;
Iterator<Command> iter = commandChain.iterator();
while( result ==null && iter.hasNext()){
result = iter.next().handle(request);
}
return result;
}
}
People have suggested using the chain of responsibility
pattern however as I was thinking of the problem it occurred to me I could use a variation of the Command
pattern to solve this problem. It works like this.
Replace each if
statement with a corresponding class which implements the Command
interface. In our example, I would implement the classes ScreenScrapeTextCommand
and FetchTextFromAPICommand
. Since each class will implement the Command
interface we will add the method execute()
to each class (this method will actually execute the functionality we need such as screen scraping or fetching from API).
We'd then create a new class which contains all related commands. Let's call this class GetTextCommandCenter
. The class would contain a list
of Commands
in the form of public List<Command> commandList;
We'd then hard code commands by adding the first two classes to the list in the following manner:
commandList.add(new ScreenScrapeTextCommand();
commandList.add(new FetchTextFromAPICommand();
We could then replace the if\\else
chain with the following:
public String replaceIfElseWithCommand()..
String text = null;
int commandIndex = 0;
while (text == null && text == someCondition)
{
text = new GetTextCommandCenter().commandList(commandIndex).execute();
commandIndex++;
}
return text;
}
As soon as a command is successful the the condition in the while loop will fail and method will return accordingly.
This way, we can easily add more Commands by adding more classes to CommandCenter
class or change the precedence of individual commands quickly.
I think this is a simple and straightforward solution to my problem (but please place a comment if you think the chain of responsibility
pattern is more suitable and why you think so)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.