-1

Refer below code structure which I want to refactor:

      @Builder
      @Value
      class ReasonAndAction {
        String reason;
        String action;
        }
        
        
        ReasonAndAction decideReasonAndAction(Input input) {
        
        if(condition1(input)) {
         return reactionAndAction1;
        }
        
        if(condition2(input)) {
         return reactionAndAction2;
        }
        
        if(condition3(input)) {
         return reactionAndAction3;
        }
        
        return ReasonAndAction.builder()
               .reason("default")
               .action("default")
               .build();
        
        }

All the if conditions are independent and may have some default behavior (else case). There may be need to add more if conditions with next versions which will make code bit ugly.

I want to remove if conditions and abstract out in separate classes and execute independently rather sequential.

Expected Behaviour :

  1. Dont Have all If conditions at same place
  2. If 2 or more if conditions are satisfied at the same time , then return new ReasonAndAction object in decideReasonAndAction method . Example - action : DO_SOMETHING , reason : CONDITION_1_AND_CONDITION_2_MET

Basically , execute all if conditions independently and get the result of all if conditions and then take action and reason accordingly.

What Java design pattern can be used here(if any) for meaningful abstraction such that default behavior(else condition at the end) is also maintained?

Can somebody please explain with example/pseudocode?

zargam08
  • 11
  • 2
  • It's difficult to give any suggestions here without some more context. Please provide a [mcve] that we can compile and run ourselves. I want to emphasize the words **minimal** and **complete**. The code example should be narrowly focused and not your entire code. Often it helps to create a new file or project from what you are actively working on and build out just enough to illustrate what you are asking. But at the same time it should be as complete as possible so that we can understand what you are trying to do. – Code-Apprentice Mar 06 '22 at 03:27
  • I want to remove if conditions and have separate classes for those as they are independent . Updated the query .I dont have any running piece of code . The code example I have used is some dummy code for reference . Hope It helps ! – zargam08 Mar 06 '22 at 06:09
  • Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking. – Community Mar 06 '22 at 08:57
  • Highlighted . I want to remove if conditions and abstract out in separate classes and execute independently rather sequential. – zargam08 Mar 06 '22 at 13:16
  • There isn't a refactoring that will do that. There isn't a design pattern that will do that. Why? Because the decision making in the example you showed us is inherently sequential. If you try to make this non-sequential you have to cope with the possibility that two or more of the conditions is true ... and then figure out what value to return. – Stephen C Mar 06 '22 at 14:02
  • Yes , there may exist a case where 2 conditions are true and that is one of the reason to not return from the if condition that is executed first with incomplete reason . Is there any abstraction possible to get rid of if else conditions @StephenC ? – zargam08 Mar 06 '22 at 14:09
  • OK. But you get my point. The so called "refactored" code you are asking for is not a proper refactoring. It has different semantics to the version above. Because in the above, the conditions are tried in a specific order, and the first of them that is true will determine the result. Please edit your Question to explain clearly how you want the code to behave. – Stephen C Mar 06 '22 at 14:11
  • My intention is to get rid of independent if conditions and not have them at the same place . Can you help me with possible ways to do this ? – zargam08 Mar 06 '22 at 14:13
  • **Fix your question first!** When you have fixed it so that it is clear what you are asking ... I will have a go at answering it. – Stephen C Mar 06 '22 at 14:14
  • Added. You can have a look now – zargam08 Mar 06 '22 at 14:19
  • Still doesn't make sense: *"If 2 or more if conditions are satisfied at the same time , then return corresponding action and reason"*. Which corresponding action and reason do you return? The method signature allows you to return only one `ActionAndReason`. Please state *clearly* what is supposed to happen. – Stephen C Mar 06 '22 at 14:23
  • Yes . both action and reason are strings in ActionAndReason object . We can set those values accordingly and return . Any of ActionAndReason from if conditions wont be returned as it should construct new object and return. I have added the example , not sure what more clearity I need to add in that . – zargam08 Mar 06 '22 at 14:27
  • I think there is a fundamental problem here. If 2 or more conditions are satisfied, there are two or more actions and two or more reasons. You can only return a single action and a single reason in a `ActionAndReason` and a call to `decideReasonAndAction` returns a single `ActionAndReason`. You can `new` as many as you like ... but you can only return one from `decideReasonAndAction`. Got it? – Stephen C Mar 06 '22 at 14:59
  • This is not an issue . We can have any value in string attributes of new object based on the if conditions executed . We will be knowing that 'x' if conditions are satisfied basis which we will construct the object . Alternatively , we can change the return type of this method -> lets say it can return List of ReasonAndAction , can you suggest any solution now ? – zargam08 Mar 06 '22 at 15:06
  • Not now. I need to go to bed. But you should at least fix the question so that the method signatures *allow* the method to return a list of objects. This is what people need so that they can understand your question. When the text says one thing and the code says something contradictory ... that makes your question unclear. (And also leads people to doubt that you actually know what you are asking ...) – Stephen C Mar 06 '22 at 16:07

1 Answers1

0

Your question is very interesting. And you have one condition:

If 2 or more conditions are satisfied, there are two or more actions and two or more reasons.

In my view, it can be solved by delegating of checking these conditions before the execution of class. However, the class should be very simple.

So let us start. This is a bad design as if we want to add new operation, then we need to add new else condition. So by doing this, we violate open closed principle of SOLID.

public class Calculator
{
    public int Exec(int a, int b, string operation) 
    {
        if (operation == "+")
            return a + b;
        else if (operation == "-")
            return a - b;
        else if (operation == "/")
            return a / b;

        return a * b;
    }
}

What can we do? We can create an interface and use dependency inversion principle of SOLID.

Our interface:

public interface IOperation
{
    int Exec(int a, int b);
}

and class:

public class CalculatorWithDependencyInversionPrinciple
{
    public int Exec(IOperation operation, int a, int b)
    {
        return operation.Exec(a, b);
    }
}

and extensions:

public class SumOperation : IOperation
{
    public int Exec(int a, int b)
    {
        return a + b;
    }
}

And if you want to add new functionality, then you need to add new class with implemented Operation interface. So our class CalculatorWithDependencyInversionPrinciple is closed for modification, but it is open for extension.

And this is a strategy pattern in action.

And you can call it like this:

int result = new CalculatorWithDependencyInversionPrinciple()
    .Exec(new SumOperation(), 1, 2); 

Output will be: 3

So far so good, but we want to have different operators ("Reasons" in your task) and our code should be simple and testable. In addition, we will try to make that our classes will have just one single responsibility. Read more about Single Responsibility Principle of SOLID.

So this is our operators:

public enum Operator // Reason
{
    Plus,
    Minus,
    Divide,
    Multiply
}

And this is mapping between Operator and its Operation:

public class OperationToOperator
{
    public Dictionary<Operator, IOperation> OperationByOperator = 
        new Dictionary<Operator, IOperation>
    {
        { Operator.Plus, new SumOperation() },
        { Operator.Minus, new MinusOperation() },
        { Operator.Divide, new DivideOperation() },
        { Operator.Multiply, new MultiplyOperation() },
    };
}

Our condition class:

public class Condition
{
    public Operator Operator { get; private set; }

    public int A { get; private set; }

    public int B { get; private set; }


    public Condition(Operator operato, int a, int b)
    {
        Operator = operato;
        A = a;
        B = b;
    }
}

and code would be executed like this:

List<Condition> conditions = new List<Condition>
{
    new Condition(Operator.Plus, 1, 2),
    new Condition(Operator.Minus, 4, 3),
    new Condition(Operator.Multiply, 5, 6),
    new Condition(Operator.Divide, 8, 2),
};

OperationToOperator operationToOperator = new OperationToOperator();
CalculatorWithDependencyInversionPrinciple calculatorWithDependencyInversionPrinciple
    = new CalculatorWithDependencyInversionPrinciple();

foreach (Condition condition in conditions)
     Console.WriteLine
     (
         calculatorWithDependencyInversionPrinciple.Exec
         (                        
             operationToOperator.OperationByOperator[condition.Operator], 
             condition.A, 
             condition.B
         )
     );

So we've created simple classes that are testable and we used Strategy pattern.

StepUp
  • 36,391
  • 15
  • 88
  • 148