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.