Improve code with several booleans conditions java - java

I got a big problem to deal with, my code is too long and full characters. I removed a lot, using methods and using some proper design patterns... But it is still too "crowded".
I get a string from the user, a question like:
"How are you Josh?"
"Who is Josh's mother?"
I need to analyze that question so see it's content and to System.out.print() the answer.
so a long serie of "if/else if" starts e.g
if (question.startsWith("How") && question.endsWith("Josh?"))
{
//do a lot of things here.
System.out.print(actualHealth);
}
else if (question.startsWith("Who") && question.endsWith("mother?"))
{
//do a lot of things here.
System.out.print(getParents().getMother());
}
*
*
* //Lot of "else if" here to recognize the question meaning.
*
*
else
{
System.out.print("question not recognized");
}
I called this class AnswersFactory as referred to the Design Pattern "Factory Pattern" because the question, is "asked" in another class. But I suppose it's a wrong way to consider it a design pattern.
How to simplify all those conditions even if they seem impossible to simplify, or at least make the code seem more organized? Is there a good design pattern to follow?
My code works great but is not beautiful to see. I hope you understand that frustration!
Thank you.

Not sure why you want to check the question based on the keyword, it has some drawbacks like mentioned by HCBPshenanigans
But to change it to be more flexible, I would do something like this:
An interface for all question handlers
public interface IQuestionHandler
{
bool CanHandle(string question);
void Handle(string question);
}
Concrete class for each scenario. Each class will tell whether it can handle the question or not, and contain logic to handle the question:
public class HealthQuestionHandler : IQuestionHandler
{
public bool CanHandle(string question)
{
return question.StartsWith("How") && question.EndsWith("Josh?");
}
public void Handle(string question)
{
//Replace by actual processing
string healthStatus = "I'm fine";
Console.WriteLine(healthStatus);
}
}
public class MotherQuestionHandler : IQuestionHandler
{
public bool CanHandle(string question)
{
return question.StartsWith("Who") && question.EndsWith("mother?");
}
public void Handle(string question)
{
//Replace by actual processing
string mother = "...";
Console.WriteLine(mother);
}
}
And finally a question handler processor to manage all the handlers. It will register all available handlers in constructor. When called to process, it goes through all of available handlers, ask one by one which one can handle the question
public class QuestionHandlerProcessor
{
private List<IQuestionHandler> _handlers;
public QuestionHandlerProcessor()
{
//Register available handlers
_handlers = new List<IQuestionHandler>
{
new HealthQuestionHandler(),
new MotherQuestionHandler()
};
}
public void Process(string question)
{
foreach(var handler in _handlers)
{
if(handler.CanHandle(question))
{
handler.Handle(question);
return;
}
}
Console.WriteLine("Question not recognized");
}
}
Usage:
QuestionHandlerProcessor processor = new QuestionHandlerProcessor();
processor.Process("How are you Josh?");
processor.Process("Who is Josh's mother?");
Although my answer is in C#, but should not be difficult to convert to Java.

you can use Chain Of Responsibility pattern. you will need catching the exception
UnhandledQuestionException
.
public abstract class QuestionHandler {
protected QuestionHandler successor;
public void setSuccessor(QuestionHandler successor) {
this.successor = successor;
}
public abstract void handle(String question);
}
and implementors should be so
public class HealthQuestionHandler extends QuestionHandler {
private bool canHandle(String question) {
return question.startsWith("How") && question.endsWith("Josh");
}
public void handle(String question) {
if( canHandle(question) ) {
String healthStatus = "I am fine";
System.out.println(healthStatus);
} else {
super.successor.handle(question);
}
}
}
public class MotherQuestionHandler extends QuestionHandler {
private bool canHandle(String question) {
return question.startsWith("Who") && question.endsWith("Mother");
}
public void handle(String question) {
if( canHandle(question) ) {
String mother = "..."; //name
System.out.println(mother);
} else {
super.successor.handle(question);
}
}
}
the final handler, when question cannot be handled:
public class UnhandledQuestionHandler extends QuestionHandler {
public void handle(String question) {
throw new UnhandledQuestionException("question not recognized");
}
}
you should create
UnhandledQuestionException
first, that extends Exception class.
you should create QuestionHandlerFactory too.
public class QuestionHandlerFactory {
public static QuestionHandler create() {
//if you can have several variants of this handler combinations, this method shouldn't be static
QuestionHandler healthQuestionHandler = new HealthQuestionHandler();
QuestionHandler motherQuestionHandler = new MotherQuestionHandler();
QuestionHandler unhandledQuestionHandler = new UnhandledQuestionHandler()'
motherQuestionHandler.setSuccessor(unhandledQuestionHandler);
healthQuestionHandler.setSuccessor(motherQuestionHandler);
return healthQuestionHandler;
}
}
and in the user of this class will be:
QuestionHandler handler = QuestionHandlerFactory.create();
try {
handler.handle(question);
} catch( UnhandledQuestionException ex ) {
System.out.println(ex.getMessage());
}

create an enum for your Constants such "WHO","HOW", .... and WHERE" then try to use switch after that
you can create enum in the same class

You can use Factory pattern and a strategy pattern. Since Qestion is being asked in a different class (let's call it QueryResolver ) it should look like this :
class QueryProcessor
{
private IQueryResolver _ resolver;
//We will be injecting our dependencies in the constructor (Dependency Inversion)
public QueryProcessor(IQueryResolver resolver )
{
_resolver = resolver;
}
public string ProcessQuery()
{
_resolver.ResolveQuery();
}
Now your QueryResolver Implements the IQueryResolver interface
public interface IQueryResover
{
string ResolveQuery();
}
And you will have multiple implementation of IQueryResolver each responsible for a particular kind of Query e.g.:
//This particular implementation know how to resolve question including the "Who" key word.
class WhoQueryResolver : IQueryResolver
{
private string _question;
public WhoQueryResolver(string question)
{
_question = question;
}
public string ResolveQuery()
{
//do a lot of things here.
System.out.print(getParents().getMother());
}
Similarly,
class HowQueryResolver : IQueryResolver
{
private string _question;
public HowQueryResolver(string question)
{
_question = question;
}
public string ResolveQuery()
{
//do a lot of things here.
System.out.print(GetActualHealth());
}
than finally a factory which return concrete implementation of IQueryResolver
public class QueryResolverFactory
{
public static IQueryResolver GetQueryResolver()
{
if (question.startsWith("How") && question.endsWith("Josh?"))
{
return new HowQueryResolver(question);
}
else if (question.startsWith("Who") && question.endsWith("mother?"))
{
return new WhoQueryResolver(question);
}
}
}

Related

When to use member variables vs design by composition?

I am trying to wrap my head around how to best design the system I am working on.
Let’s say it’s an application similar to a pawn store. I have abstructed the flow of purchasing and reselling into something called an ExecutionStrategy. There are four ExecutionStrategy implementations in this application: registering a customer, bidding and purchasing, pricing, and posting to store.
There a basic steps that each one of the strategies follow including the main execution workflow and recording what we’ve done in a data store.
in addition to these bidding and purchasing as well as pricing require a consultation of an expert before we can do anything in the execution workflow.
this is where I am a little bit confused on the decision that I would like to make in terms of design. it appears as if there are three options to go with and I’m not too sure which one is most correct. 1) Extending Execution strategy with something like ExecutionStrategy with ExecutionStrategyWithConsultation, which wraps the execution workflow of the strategy with a consultation phase. 2) Creating a Decorator pattern for ExecutionStrategy and extends that with something like ConsultationServiceDecorator. 3) create a member variable in the implementation of purchasing/bidding and pricing to call the consultation service at any time with an interface around the service.
I will outline the design below.
Some considerations:
ConsultationService is very, very slow. Caching is not really an option here as the data is very loosely formed and we do not want to create a document datastore just for this.
ConsultationService returns an object that matches what was given. So it ends up having 1 method that looks like T consultOn(T item)
There could be some desire to call ConsultationService at any time in the execution workflow. Currently the only use case is to call the service before the main flow, but this is not necessarily the only use case right now.
Pros/cons of each approach above:
Extending ExecutionStrategy directly:
PRO: We can have access to a protected ConsultationService variable in the code
PRO: We have an understanding from reading the code that a, say, PurchasingExecutionStrategy extends ExecutionStrategyWithConsultation, so we know a bit about what kind of workflow it is just from that.
CON: This seems to break the "composition over inheritance" pattern. We are using inheritance for storing member variables.
CON: The service returns a whole new object, so after the first line of code when we make the call to the service, we are dealing with an entirely different object than the one passed originally.
Creating a Decorator:
PRO: We are more closely conforming with the composition over inheritance principal.
PRO: We can enforce that the service is called first, and pass that new object to the main workflow, so it only executes its main workflow on the object passed.
CON: I have not figured out a way to design this in a way to allow for potentially multiple or any time service calls.
CON: When looking at the code, we lose the knowledge gained from PurchasingExecutionStrategy extends ExecutionStrategyWithConsultation, unless we look at where PurchasingExecutionStrategy is actually being instantiated as a constructor arg of ConsultationServiceDecorator
Create a member variable with interface:
PRO: Same pros as #1. Easy to understand fully what code is doing without digging.
CON: Same cons as #1. Cannot enforce order. Execution deals with inheritenly different object than the one passed.
CON: If we need to make multiple calls in the same workflow, this would be very slow due to service speed and no cache.
Examples of each:
//Number 1
public interface ExecutionStrategy<T> {
/**
* Perform the main execution workflow
*/
public T execute(T item);
}
public interface ConsultationService {
public StoreItem consultOn (StoreItem item);
}
public abstract class ExecutionStrategyWithConsultation implements ExecutionStrategy<StoreItem> {
protected ConsultationService consultationService;
}
public class ListingExecutionStrategy extends ExecutionStrategyWithConsultation {
public StoreItem execute(StoreItem item) {
if (item.hasDirectBuyer()) { //hasDirectBuyer is populated by ConsultationService
item.sellTo = item.directBuyer.getId();
} else {
//no direct buyer
SuggestedPriceRange priceRange = item.getConsultationPriceRange(); //consultationPriceRange is populated by ConsultationService
item.priceRange = priceRange;
item.listToWebsite = true;
}
return item;
}
}
//Number 2
public interface ExecutionStrategy<T> {
/**
* Perform the main execution workflow
*/
public T execute(T item);
}
public abstract class ExecutionStrategyDecorator<T> implements ExecutionStrategy<T>{
protected final ExecutionStrategy<T> executionStrategy;
public ExecutionStrategyDecorator(ExecutionStrategy<T> execStrategy) {
executionStrategy = execStrategy;
};
}
public class ExecutionStrategyWithConsultation extends ExecutionStrategyDecorator<StoreItem> {
protected ConsultationService consultationService;
public ExecutionStrategyWithConsultation(ExecutionStrategy<StoreItem> execStrat, ConsultationService service) {
super(execStrat);
consultationService = service;
}
public StoreItem execute(StoreItem item) {
StoreItem itemAfterConsultation = consultationService.consultOn(item);
return execStrategy.execute(itemAfterConsultation);
}
}
public class ListingExecutionStrategy implements ExecutionStrategy<StoreItem> {
public StoreItem execute(StoreItem item) {
if (item.hasDirectBuyer()) { //hasDirectBuyer is populated by ConsultationService
item.sellTo = buyer.getId();
} else {
//no direct buyer
SuggestedPriceRange priceRange = item.getConsultationPriceRange(); //consultationPriceRange is populated by ConsultationService
item.priceRange = priceRange;
item.listToWebsite = true;
}
return item;
}
}
public class ListingExecutionStrategyFactory {
public ExecutionStrategy instantiate() {
return new ExecutionStrategyWithConsultation(new ListingExecutionStrategy(), new ConsultationServiceImpl());
}
}
//Number 3
public interface ExecutionStrategy<T> {
/**
* Perform the main execution workflow
*/
public T execute(T item);
}
public interface ConsultationService {
public DirectBuyer getDirectBuyerIfExists(StoreItemType itemType);
public SuggestedPriceRange getSuggestedPriceRange(StoreItem item);
}
public class ListingExecutionStrategy implements ExecutionStrategy<StoreItem> {
ConsultationService service;
public PurchasingExecutionStrategy(ConsultationService consultService) {
service = ConsultationService;
}
public StoreItem execute(StoreItem item) {
DirectBuyer buyer = service.getDirectBuyerIfExists(item.getItemType())
if (Optional.ofNullable(buyer).isPresent()) {
item.sellTo = buyer.getId();
return item;
} else {
//no direct buyer
SuggestedPriceRange priceRange = service.getSuggestedPriceRange(item);
item.priceRange = priceRange;
item.listToWebsite = true;
return item;
}
}
}
Thanks for the input. Appreciate the help.
As an alternative to your ConsultationService, you might consider building a chain of ExecutionService instances to allow creation of complex processing scenarios:
public interface ExecutionStrategy<T> {
public T execute(T item);
}
public interface ExecutionStrategyChain<T> extends ExecutionStrategy<T> {
public static <T> ExecutionStrategyChain<T> newInstance(ExecutionStrategy<T> executionStrategy) {
return new ExecutionStrategyChainImpl<T>(executionStrategy);
}
public ExecutionStrategyChain<C> chainTo(ExecutionStrategy<C> executionStrategy);
}
public abstract class AbstractExecutionStrategyChain<T> implements ExecutionStrategyChain<T> {
protected AbstractExecutionStrategyChain() {
this(null);
}
public abstract T execute(T item);
public ExecutionStrategyChain<T> chainTo(ExecutionStrategy<T> executionStrategy) {
return new ExecutionStrategyChainImpl<T>(this, executionStrategy);
}
}
public final class ExecutionStrategyChainImpl<T> extends AbstractExecutionStrategyChain<T> {
private final ExecutionStrategy<T> firstExecutionStrategy;
private final Executionstrategy<T> secondExecutionStrategy;
public ExecutionStrategyChainImpl(ExecutionStrategy<T> first, ExecutionStrategy<T> second) {
if(first == null) throw new NullPointerException();
this.firstExecutionStrategy = first;
this.secondExecutionStrategy = second;
}
public ExecutionStrategyChainImpl(ExecutionStrategy<T> first) {
this(first, null);
}
#Override
public final T execute(T item) {
if(item == null) {
return null;
}
T result = firstExecutionStrategy.execute(item);
if(result != null && secondExecutionStrategy != null) {
result = secondExecutionStrategy.execute(result);
}
return result;
}
}
public class PreProcessor<T> implements ExecutionStrategy<T> {
public PreProcessor() {
}
#Override
public T execute(T item) {
//Do some pre-processing of item
return item;
}
}
public class PostProcessor<T> implements ExecutionStrategy<T> {
public PostProcessor() {
}
#Override
public T execute(T item) {
//Do some post-processing of item
return item;
}
}
public class MyNormalProcessor<T> extends AbstractExecutionStrategyChain<T> {
public MyNormalProcessor() {
}
#Override
public T execute(T item) {
//Do something normal with the item
return item;
}
}
public static final ExecutionStrategy<StoreItem> COMPLEX_EXECUTION_STRATEGY =
ExecutionStrategyChain<StoreItem>.newInstance(new PreProcessor<StoreItem>())
.chainTo(new MyNormalProcessor<StoreItem>())
.chainTo(new PostProcessor<StoreItem>());

Which pattern or method to use when calling a regex check method in multiple if statements?

I created a factory pattern in my class.
In this class I injected classes which implements Command interface based on incoming String parameter.
Factory class
#Component
#RequiredArgsConstructor
public class CommandFactory {
private final ACommand aCommand;
private final BCommand bCommand;
private final CCommand cCommand;
private final DCommand dCommand;
private final ECommand eCommand;
private final FCommand fCommand;
public Command createCommand(String content) {
if (aCommand.isMatching(content)) {
return aCommand;
} else if (bCommand.isMatching(content)) {
return bCommand;
} else if (cCommand.isMatching(content)) {
return cCommand;
} else if (dCommand.isMatching(content)) {
return dCommand;
} else if (eCommand.isMatching(content)) {
return eCommand;
} else if (fCommand.isMatching(content)) {
return fCommand;
} else {
return null;
}
}
In isMatching() method there are different regex'es and I try to figure out how this incoming String should be processed.
I am looking for a cleaner way to get rid of these sequential if statements. Because whenever I create a new class into this factory I add another if statement.
Maybe Stream can help?
Stream<Command> stream = Stream.of(aCommand, bCommand, cCommand ...);
return stream.filter(x -> x.isMatching(content)).findFirst().orElse(null);
Now every time you add a new class, you just add a new object to the first line.
If you want to get rid of the sequential if statements you can use streams (like user Sweeper suggested) or loops and I would also suggest to return and optional which makes null handling clearer for the client.
Here are two suggested options to get rid of if else repetitions one with loops another with streams:
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class CommandPatternExample {
private List<Command> candidates = Arrays.asList(new ACommand(), new BCommand(), new CCommand());
public Optional<Command> createCommand(String content) {
for(Command command : candidates) {
if(command.isMatching(content)) {
return Optional.of(command);
}
}
return Optional.empty();
}
public Optional<Command> createCommandStream(String content) {
return candidates.stream().filter(c -> c.isMatching(content)).findFirst();
}
}
interface Command<T> {
void execute(T obj);
boolean isMatching(String s);
}
class ACommand implements Command<String> {
#Override
public void execute(String obj) {
}
#Override
public boolean isMatching(String s) {
return "A".equals(s);
}
}
class BCommand implements Command<String> {
#Override
public void execute(String obj) {
}
#Override
public boolean isMatching(String s) {
return "B".equals(s);
}
}
class CCommand implements Command<String> {
#Override
public void execute(String obj) {
}
#Override
public boolean isMatching(String s) {
return "C".equals(s);
}
}
Map might be a good idea. Meaning if you place your command instances into a map as values where your key would be something that you could match against incoming String. Then instead of sequential search with Efficiency O(n) you can get much better performance O(1). This is a short answer.
Besides that There is an open source java library MgntUtils (wriiten by me) that contains some utility called "Self-instantiating factories" Basically it manages and the Factory for you. All you will need to do is to create a class that implements a certain interface and the utility will add it for you into a map based factory. It might be useful to you. Here is the link to an article that explains about the utilities in the library as well as where to get the library (Github and Maven central). In the article look for the paragraph "Lifecycle management (Self-instantiating factories)". Also library comes with a detailed written javadoc and code example for that feature.

Using a fluent builder pattern without inner static classes for workflow

This is a continuation from what I was working in Passing 1 to many parameters of same object type
I've gotten good feedback on that , I believe i have the improved the design . The whole code is at https://github.com/spakai/flow_input_builder
The requirement is simple : -
I need to build a set of input for different workflows using 1 or more outputs from previous workflows
I have a set of interfaces
public interface SwfInput {
}
public interface SwfOutput {
}
public interface Workflow<I extends SwfInput, O extends SwfOutput> {
public O execute(I input);
}
public interface Builder<I extends SwfInput> {
public I build();
}
Now , Say I have 3 flows which gets executed in sequence FlowA->FlowB->FlowC
FlowC needs mandatory output from FlowB but only optionally from FlowA
so I have a implementation for FlowCBuilder
public class FlowCInputBuilder implements Builder<FlowCInput> {
private final FlowBOutput mandatoryflowBOutput;
private FlowAOutput optionalflowAOutput;
public FlowAOutput getOptionalflowAOutput() {
return optionalflowAOutput;
}
public FlowCInputBuilder setOptionalflowAOutput(FlowAOutput optionalflowAOutput) {
this.optionalflowAOutput = optionalflowAOutput;
return this;
}
public FlowCInputBuilder(FlowBOutput mandatoryflowBOutput) {
this.mandatoryflowBOutput = mandatoryflowBOutput;
}
#Override
public FlowCInput build() {
FlowCInput input = new FlowCInput();
input.setMandatoryFromFlowB(mandatoryflowBOutput.getOutput1FromB());
if (optionalflowAOutput != null) {
input.setOptionalFromFlowA(optionalflowAOutput.getOutput2FromA());
}
return input;
}
}
one test i have written shows an example usage
FlowBOutput mandatoryflowBOutput = new FlowBOutput();
mandatoryflowBOutput.setOutput1FromB("iNeedThis");
FlowAOutput optionalflowAOutput = new FlowAOutput();
FlowCInput input = new FlowCInputBuilder(mandatoryflowBOutput)
.setOptionalflowAOutput(optionalflowAOutput)
.build();
I have not used static inner class for the Builder pattern.
Any suggestions are welcomed.
You should use static inner class. The key point of using this approach is that, the inner can directly access private properties of the object being constructed. This helps eliminating duplicated code since the builder does not need to maintain a long list of temporary state for the constructing. So, your code can be rewritten like this:
public class FlowCInput {
private int output1FromB; // suppose that it is int
private String output2FromA; // suppose that it is String
private FlowCInput() { }
//...
public static class FlowCInputBuilder implements Builder<FlowCInput> {
private final FlowCInput result;
public FlowCInputBuilder(FlowBOutput mandatoryflowBOutput) {
result = new FlowCInput();
// output1FromB is private but still accessed from here
result.output1FromB = mandatoryflowBOutput.getOutput1FromB();
}
public FlowCInputBuilder setOptionalflowAOutput(FlowAOutput optionalflowAOutput) {
// same for output2FromA
result.output2FromA = optionalflowAOutput.getOutput2FromA();
return this;
}
#Override
public FlowCInput build() {
return result;
}
}
}
As you see, the builder now holds only a FlowCInput object, it does not unnecessarily hold mandatoryflowBOutput and optionalflowAOutput as before.

enum singleton, referenced by interface, instance by string name

Thanks for viewing my question, which I have not successfully found an answer for in my searches/books. I'm learning java by writing a roguelike, but I think this question is more java-related than game-related. Feel free to educate me if I'm wrong.
I have similar classes that I want to each have specific abilities. The abilities are enum singletons with a set of standard method names that I would pass the Actor to - I wanted to avoid implementing methods from an interface in every Actor class, and just really liked the envisioned use of this approach as I go forward. I come from a shell/perl background and can't tell if I'm just not thinking OOP, or if I'm on to something useful and don't have the skills yet to pull it off.
addAbility(String) in StdActor is where it finally broke in this experiment.
Question is - am I doing something wrongheaded here? If not, how could I implement this?
interface for manipulating abilities:
public interface ActorAbility {
// doesn't work, but need something to enable
// instance retrieval for addAbility...
public ActorAbility getInstance();
public void act(Actor actor);
public boolean isTickable();
}
sanitized implementation of interface:
public enum ActorMove implements ActorAbility {
INSTANCE;
private ActorMove() {
}
public ActorAbility getInstance() {
return INSTANCE;
}
public void act(Actor actor) {
log.debug("Move");
}
public boolean isTickable() {
return true;
}
}
sanitized use of the ability. trial and error run amock. addAbility(String) broken, copy/paste from SO and elsewhere. it probably needs to be nuked from orbit.
public class StdActor implements Actor {
private HashSet<ActorAbility> abilities = new HashSet<>();
// this whole method is wrecked
public void addAbility(String ability) {
// Class<? extends ActorAbility> action; // in a maze of twisty passages...
ActorAbility actionInstance = null;
try {
// action = Class.forName("game3.Actors.Abilities." + ability);
actionInstance = ActorAbility.valueOf("game3.Actors.Abilities."
+ ability);
} catch (Exception e) {
e.printStackTrace();
}
this.abilities.add(actionInstance);
}
}
use case:
public class StdCharClass extends StdActor {
public StdCharClass() {
// I like this because it's clean and easily
// changeable
addAbility("ActorMove");
}
}
future planned use:
HashSet<ActorAbility> abilities = actor.getAbilities();
for (ActorAbility ability : abilities) {
if (ability.isTickable()) {
ability.act(actor);
}
}
Thanks!
EDIT:
Thanks for such a quick comment, JB. I tried what you suggested and it appears to do what I was hoping. It appears I was just off in the weeds and needed to be pulled back.
new class:
public enum Ability {
MOVE(ActorMove.INSTANCE), FIGHT(ActorFight.INSTANCE);
private ActorAbility ability;
private Ability(ActorAbility abilityClass) {
this.ability = abilityClass;
}
public ActorAbility getAbility() {
return this.ability;
}
}
StdActor:
public class StdActor implements Actor {
private HashSet<Ability> abilities = new HashSet<>();
public void addAbility(Ability ability) {
this.abilities.add(ability);
}
subclass:
public class StdCharClass extends StdActor {
public StdCharClass() {
addAbility(Ability.MOVE);
}
}
and finally, usage:
HashSet<Ability> abilities = bob.getAbilities();
for (Ability ability : abilities) {
ActorAbility abilityClass = ability.getAbility();
if (abilityClass.isTickable()) {
abilityClass.act(bob);
}
}
output!
12:44:15.835 [main] DEBUG ActorMove - Move

Replacing if else statement with pattern

I have a if else statement which might grow in the near future.
public void decide(String someCondition){
if(someCondition.equals("conditionOne")){
//
someMethod("someParameter");
}else if(someCondition.equals("conditionTwo")){
//
someMethod("anotherParameter");
}
.
.
else{
someMethod("elseParameter");
}
}
Since, this is already looking messy, I think it would be better if I can apply any design patterns here. I looked into Strategy pattern but I am not sure if that will reduce if else condition here. Any suggestions?
This is a classic Replace Condition dispatcher with Command in the Refactoring to Patterns book.
Basically you make a Command object for each of the blocks of code in your old if/else group and then make a Map of those commands where the keys are your condition Strings
interface Handler{
void handle( myObject o);
}
Map<String, Handler> commandMap = new HashMap<>();
//feel free to factor these out to their own class or
//if using Java 8 use the new Lambda syntax
commandMap.put("conditionOne", new Handler(){
void handle(MyObject o){
//get desired parameters from MyObject and do stuff
}
});
...
Then instead of your if/else code it is instead:
commandMap.get(someCondition).handle(this);
Now if you need to later add new commands, you just add to the hash.
If you want to handle a default case, you can use the Null Object pattern to handle the case where a condition isn't in the Map.
Handler defaultHandler = ...
if(commandMap.containsKey(someCondition)){
commandMap.get(someCondition).handle(this);
}else{
defaultHandler.handle(this);
}
Let's assume that we have such code (which is the same as yours):
public void decide(String someCondition) {
if(someCondition.equals("conditionOne")) {
someMethod("someParameter");
}
else if(someCondition.equals("conditionTwo")) {
someMethod("anotherParameter");
}
else {
someMethod("elseParameter");
}
}
Assuming that you don't want to refactor other parts of the application and you don't want to change method signature there are possible ways in which it could be refactored:
Warning - You should use generic versions of mentioned patterns.
I showed non generic ones because it is easier to read them.
Strategy + Factory Method
We can use Strategy and Factory Method patterns. We also take advantage of polymorphism.
private final StrategyConditionFactory strategyConditionFactory = new StrategyConditionFactory();
public void decide(String someCondition) {
Strategy strategy = strategyConditionFactory.getStrategy(someCondition)
.orElseThrow(() -> new IllegalArgumentException("Wrong condition"));
strategy.apply();
}
It would be better to design it in a way that else condition is included in the factory, and developer calls it on purpose. In such case we throw exception when condition is not meet. Alternatively we could write it exactly as it was in question. If you want so instead of .orElseThrow(() -> new IllegalArgumentException("Wrong condition")); put .orElse(new ElseStrategy());
StrategyConditionFactory (factory method):
public class StrategyConditionFactory {
private Map<String, Strategy> conditions = new HashMap<>();
public StrategyConditionFactory() {
conditions.put("conditionOne", new ConditionOneStrategy());
conditions.put("conditionTwo", new ConditionTwoStrategy());
//It is better to call else condition on purpose than to have it in the conditional method
conditions.put("conditionElse", new ElseStrategy());
//...
}
public Optional<Strategy> getStrategy(String condition) {
return Optional.ofNullable(conditions.get(condition));
}
}
Strategy interface:
public interface Strategy {
void apply();
}
Implementations:
public class ConditionOneStrategy implements Strategy {
#Override
public void apply() {
//someMethod("someParameter");
}
}
public class ConditionTwoStrategy implements Strategy {
#Override
public void apply() {
//someMethod("anotherParameter")
}
}
public class ElseStrategy implements Strategy {
#Override
public void apply() {
//someMethod("elseParameter")
}
}
Usage (simplified):
public void strategyFactoryApp() {
//...
decide("conditionOne");
decide("conditionTwo");
decide("conditionElse");
//...
}
Strategy + Factory Method - this particular case (where only parameter changes)
We can use the fact that in this case we always call the same method, only parameter changes
We change our base strategy interface to abstract class with getParameter() method and we make new implementations of this abstract class. Other code remains the same.
public abstract class Strategy {
public abstract String getParameter();
public void apply() {
someMethod(getParameter());
}
private void someMethod(String parameter) {
//someAction
}
}
Implementations:
public class CondtionOneStrategy extends Strategy {
#Override
public String getParameter() {
return "someParameter";
}
}
public class CondtionTwoStrategy extends Strategy {
#Override
public String getParameter() {
return "anotherParameter";
}
}
public class ElseStrategy extends Strategy {
#Override
public String getParameter() {
return "elseParameter";
}
}
Enum + enum kinda "factory"
We might use Enum to implement strategy and instead of factory method we can use valueOf() from enum.
public void decide(String someCondition) {
ConditionEnum conditionEnum = ConditionEnum.valueOf(someCondition);
conditionEnum.apply();
}
Condition enum:
public enum ConditionEnum {
CONDITION_ONE {
#Override
public void apply() {
//someMethod("someParameter");
}
},
CONDITION_TWO {
#Override
public void apply() {
//someMethod("anotherParameter");
}
},
CONDITION_ELSE {
#Override
public void apply() {
//someMethod("elseParameter");
}
};
//...more conditions
public abstract void apply();
}
Usage (simplified):
public void enumFactoryApp() {
//...
decide("CONDITION_ONE");
decide("CONDITION_TWO");
decide("CONDITION_ELSE");
//...
}
Notice that you will get IllegalArgumentException when enum type has no constant with the specified name.
Command + Factory
The difference between strategy and command is that command holds also state, so if you have for example compute(int a, int b, String someCondition) and you want to refactor it with strategy including it's signature change you can reduce it to compute(int a, int b, ComputeStrategy computeStrategy) with command you can reduce it to one argument compute(ComputeCommand computeCommand). In this case we also take advantage of polymorphism similarly to strategy pattern case.
CommandConditionFactory commandConditionFactory = new CommandConditionFactory();
public void decide(String someCondition) {
Command command = commandConditionFactory.getCommand(someCondition)
.orElseThrow(() -> new IllegalArgumentException("Wrong condition"));
command.apply();
}
It would be better to design it in a way that else condition is included in the factory, and developer calls it on purpose. In such case we throw exception when condition is not meet. Alternatively we could write it exactly as it was in question. If you want so instead of .orElseThrow(() -> new IllegalArgumentException("Wrong condition")); put .orElse(new ElseCommand());
CommandConditionFactory (factory method):
public class CommandConditionFactory {
private Map<String, Command> conditions = new HashMap<>();
public CommandConditionFactory() {
conditions.put("conditionOne", new ConditionOneCommand("someParameter"));
conditions.put("conditionTwo", new ConditionTwoCommand("anotherParameter"));
//It is better to call else condition on purpose than to have it in the conditional method
conditions.put("conditionElse", new ElseCommand("elseParameter"));
//...
}
public Optional<Command> getCommand(String condition) {
return Optional.ofNullable(conditions.get(condition));
}
}
Command interface:
public interface Command {
void apply();
}
Implementations (there is some redundancy, but It is there to show how command should look in more general case where instead of someMethod() we have three different methods):
public class ConditionOneCommand implements Command {
private final String parameter;
public ConditionOneCommand(String parameter) {
this.parameter = parameter;
}
#Override
public void apply() {
//someMethod(parameter);
}
}
public class ConditionTwoCommand implements Command {
private final String parameter;
public ConditionTwoCommand(String parameter) {
this.parameter = parameter;
}
#Override
public void apply() {
//someMethod(parameter);
}
}
public class ElseCommand implements Command {
private final String parameter;
public ElseCommand(String parameter) {
this.parameter = parameter;
}
#Override
public void apply() {
//someMethod(parameter);
}
}
Usage (simplified):
public void commandFactoryApp() {
//...
decide("conditionOne");
decide("conditionTwo");
decide("conditionElse");
//...
}
Command + Factory - This particular case.
This in fact isn't a real command pattern just a derivative. It takes advantage of the fact that in this case we are always calling the same method someMethod(parameter) and only the parameter changes.
Abstract class:
public abstract class Command {
abstract void apply();
protected void someMethod(String parameter) {
//someAction
}
}
Implementation (the same for all 3 conditional cases):
public class CommandImpl extends Command {
private final String parameter;
public CommandImpl (String parameter) {
this.parameter = parameter;
}
#Override
public void apply(){
someMethod(parameter);
}
}
Factory, please notice that there is only one command implementation, only parameter changes:
public class CommandConditionFactory {
Map<String, Command> conditions = new HashMap<>();
public CommandConditionFactory() {
conditions.put("conditionOne", new CommandImpl("someParameter"));
conditions.put("conditionTwo", new CommandImpl("anotherParameter"));
//It is better to call else condition on purpose than to have it in the conditional method
conditions.put("conditionElse", new CommandImpl("elseParameter"));
//...
}
public Optional<Command> getCommand(String condition) {
return Optional.ofNullable(conditions.get(condition));
}
}
Nested if's
Note that even if you have nested ifs sometimes it is possible to refactor them and use one of the mentioned techniques.
Lets say that we have following code:
public void decide2(String someCondition, String nestedCondition) {
if(someCondition.equals("conditionOne")) {
if(nestedCondition.equals("nestedConditionOne")){
someLogic1();
}
else if(nestedCondition.equals("nestedConditionTwo")){
someLogic2();
}
}
else if(someCondition.equals("conditionTwo")) {
if(nestedCondition.equals("nestedConditionThree")){
someLogic3();
}
else if(nestedCondition.equals("nestedConditionFour")){
someLogic4();
}
}
}
You could refactor it using mathematical logic rules:
public void decide2(String someCondition, String nestedCondition) {
if(someCondition.equals("conditionOne")
&& nestedCondition.equals("nestedConditionOne")) {
someLogic1();
}
else if(someCondition.equals("conditionOne")
&& nestedCondition.equals("nestedConditionTwo")) {
someLogic2();
}
else if(someCondition.equals("conditionTwo")
&& nestedCondition.equals("nestedConditionThree")) {
someLogic3();
}
else if(someCondition.equals("conditionTwo")
&& nestedCondition.equals("nestedConditionFour")) {
someLogic4();
}
}
and then you can use strategy, enum or command. You just have a pair of Strings <String, String> instead of single String.
Decision Tables
When you have nested ifs that couldn't be refactored as mentioned you can implement your own decision tables or use some ready to go decision tables solution. I won't give the implementation there.
Rules Engine
When you have nested ifs that couldn't be refactored as mentioned you can also implement your own simple rules engine. You should use it only if you have many nested ifs, otherwise it is triumph of form over content.
For very complicated Business Logic there are professional Rule Engines like Drools.
I won't give the implementation there.
One more thing
In the example that you gave there is a high possibility that someone introduced these ifs, but they are totally redundant. And we can check it by trying to refactor decide method signature to make it take some other argument and to refactor surrounding code that is calling our method. By doing so we are getting rid of our Factory Method. There are examples that present how the code might look when it occurs that these ifs were redundant.
Strategy
Decide method:
public void decide(Strategy strategy) {
strategy.apply();
}
Usage (simplified):
public void strategyApp() {
//...
decide(new ConditionOneStrategy());
decide(new ConditionTwoStrategy());
decide(new ElseStrategy());
//...
}
Enum
Decide method:
public void decide(ConditionEnum conditionEnum) {
conditionEnum.apply();
}
Usage (simplified):
public void enumApp() {
//...
decide(ConditionEnum.CONDITION_ONE);
decide(ConditionEnum.CONDITION_TWO);
decide(ConditionEnum.CONDITION_ELSE);
//...
}
Command
Decide method:
public void decide(Command command) {
command.apply();
}
Usage (simplified):
public void commandApp() {
//...
decide(new ConditionOneCommand("someParameter"));
decide(new ConditionTwoCommand("anotherParameter"));
decide(new ElseCommand("elseParameter"));
//...
}
In fact it is quite specific case, there are cases in which for example we have to use simple type like String, because it comes from the external system or condition is based on integer from input so we can't refactor the code so easily.
The general recommendation by Martin Fowler is to
Replace Conditional with Polymorphism.
In terms of design patterns this would often be the Strategy Pattern
Replace Conditional Logic with Strategy.
If you have a small, finite set of conditions, I recommend to use an enum to implement the Strategy Pattern (provide an abstract method in the enum and override it for each constant).
public enum SomeCondition{
CONDITION_ONE{
public void someMethod(MyClass myClass){
//...
}
},
CONDITION_TWO{
public void someMethod(MyClass myClass){
}
}
public abstract void someMethod(MyClass myClass);
}
public class MyClass{
//...
public void decide(SomeCondition someCondition){
someCondition.someMethod(this);
}
}
If it's really just a parameter you want to pick, then you could define the enum like this instead:
public enum SomeCondition{
CONDITION_ONE("parameterOne"),
CONDITION_TWO("parameterTwo");
private final String parameter;
private SomeCondition(String parameter){
this.parameter = parameter;
}
public String getParameter(){
return parameter;
}
}
public class MyClass{
//...
public void decide(SomeCondition someCondition){
someMethod(someCondition.getParameter());
}
}
Another way to solve the current problem is to use Factory Pattern. This provides functionality to extract a factory method that returns an object of a given type and performs the operation based on the concrete object behavior.
public interface Operation {
String process(String a, String b);
}
The method takes two string as input and returns the result.
public class Concatenation implements Operation {
#Override
public String process(String a, String b) {
return a.concat(b);
}
}
public class Join implements Operation {
#Override
public String process(String a, String b) {
return String.join(", ", a, b);
}
}
And then we should define a factory class which returns instances of Operation based on the given operator:
public class OperatorFactory {
static Map<String, Operation> operationMap = new HashMap<>();
static {
operationMap.put("concatenation", new Concatenation());
operationMap.put("join", new Join());
// more operators
}
public static Optional<Operation> getOperation(String operator) {
return Optional.ofNullable(operationMap.get(operator));
}
}
And now we can use it:
public class SomeServiceClass {
public String processUsingFactory(String a, String b, String operationName) {
Operation operation = OperatorFactory
.getOperation(operationName)
.orElseThrow(() -> new IllegalArgumentException("Invalid Operation"));
return operation.process(a, b);
}
}
I guess you must have already considered it, but if you are using JDK 7 or above, you can switch on strings. That way your code can look cleaner than a bunch of if-else statements.

Categories