**Yes, it's do-able, and may be a good idea**
Certainly this is doable, and it can often yield clearer logic, depending on what parameters you are using. Hopefully the parameters are related in some way that ties into a well-understood business concept.
**Identify the hidden business concept**
In cases where the two parameters map to a single (hidden) business concept, you need to make that concept explicit, using a mapping function that takes `p` and `q` and outputs the case.
This example corresponds to your first example:
z = map(p, q)
switch (z)
case z1:
a();
break;
case z2:
b();
break;
case z3:
c();
break;
case z4:
d();
break;
}
Or for your second example:
z = map(p, q)
switch (z)
case z1:
a();
break;
case z2:
a();
b();
break;
case z3:
c();
break;
case z4:
//No operation
break;
}
**What is the mapping function?**
The mapping function can be as simple as combining `p` and `q` into a bitmask (if they are Booleans) or combining strings (if they are, for example, product and subproduct codes). This could be a stretch in some circumstances, but is a plain and obvious solution in other circumstances. For example, consider these questions that may appear on an application for health insurance:
1. Are you married? Yes or no
2. Do you have children? Yes or no
In this case, your company might offer four products (the hidden business concept): single, married, single with children, and married with children. In this sort of scenario it is perfectly justified to combine the user's answers into a virtual product (given by the mapping function) and choosing a logic path based on the product instead of the individual flags. In fact it is much better code, IMO.
**Getting rid of the switch case**
Switch/case is ugly. Another approach would continue to use the hidden business concept but in a cleaner code pattern:
IProduct z = ProductFactory.GetProduct(q, p);
z.Execute();
...where the factory could return one of four products, all of which implement `IProduct` which contains a product-specific `Execute` method that will execute `a()`, `b()`, `c()`, and/or `d()` as needed.
**Won't the factory have the same (original) issue?**
The factory could contain nested `if` statements (bringing back the original problem) or it could use a lookup table for instantiation, like this:
class ProductFactory
{
private static readonly Dictionary<bool, Dictionary<bool, Func<IProduct>>> _products
= new Dictionary<bool, Dictionary<bool, Func<IProduct>>>();
static ProductFactory()
{
_products.Add(false, new Dictionary<bool, Func<IProduct>> {
{ true, () => new ProductSingleWithChildren() },
{ false, () => new ProductSingle() }
});
_products.Add(true, new Dictionary<bool, Func<IProduct>> {
{ true, () => new ProductMarriedWithChildren() },
{ false, () => new ProductMarried() }
});
}
public IProduct GetProduct(bool isMarried, bool hasChildren)
{
return _products[isMarried][hasChildren]();
}
}
...where `_products` is a list of lists, each item containing a `Func<IProduct>` that will instantiate the appropriate type.
Dictionaries with a key of `bool` seem a little weird, so in an actual piece of code you might lean toward using an `enum` instead, or possible a `string` containing a code. I would suggest using an identifier that is aligned with the business/domain; make it easy for other developers to understand what is going on.
For completeness, here is an idea what the classes would look like:
interface IProduct
{
void Execute();
}
class ProductSingle : IProduct
{
public void Execute()
{
a();
}
}
class ProductSingleWithChildren : IProduct
{
public void Execute()
{
a();
b();
}
}
class ProductMarried: IProduct
{
public void Execute()
{
c();
}
}
class ProductMarriedWithChildren : IProduct
{
public void Execute()
{
//No operation
}
}
You'll notice in the end we not only got rid of the duplicate `if` condition, there are in fact ZERO `if` statements in the whole solution!!! :) Pretty neat, and will get you a low cyclomatic complexity score and a nice clean CPU pipeline. Plus much more readable code.
Then again, we just wrote a LOT more lines of code than the original example. So, depending on context, maybe the nested `if` statements would be better, in certain cases, e.g. the combination of the flags is arbitrary or doesn't map to a logical concept within the business problem, or the extra readability isn't necessary for a certain small problem. It's up to you, of course.