According to anti-if campaign it is a best practice not to use ifs in our code. Can anyone tell me if it possible to get rid of the if in this piece of code ? (switch is also
A little late to the party, but combining the C# dictionary answers from MRFerocius and cletus gives the following implementation of bmargulies's answer:
private Dictionary<string,Action> data = new Dictionary<string, Action> {
{"foo", () => Console.WriteLine("Some logic here")},
{"bar", () => Console.WriteLine("something else here")},
{"raboof", () => Console.WriteLine("of course I need more than just WriteLine")},
}
public static void main(String[] args) {
data["foo"]();
}
Multiple actions can be composed:
There can be multiple calls to different methods, using multiline lambda syntax:
{"foobar", () => {
data["foo"]();
data["bar"]();
}
As Action
is a delegate type, multiple methods can be attached to a single delegate instance and that delegate instance set as the value; they will be called sequentially when the delegate is invoked:
public static void main(String[] args) {
data["foobar"] = data["foo"] + data["bar"];
//This will invoke first data["foo"] then data["bar"]
data["foobar"]();
}
For methods not referenced via the dictionary, this can also be done in the collection initializer:
{"foobar", (Action)method1 + method2}
Here's one way... :)
delegate void DoStuff();
...
IDictionary<string, DoStuff> dict = new Dictionary<string, DoStuff>();
dict["foo"] = delegate { Console.WriteLine("some logic here"); };
dict["bar"] = delegate { Console.WriteLine("something else here"); };
dict["raboof"] = delegate { Console.WriteLine("of course I need more than just Writeln"); };
dict["foo"]();
Java
Use an enum which implements a certain method.
enum MyEnum{
foo{
public void mymethod(String param1, String param2){
//dostuff...
}
},
bar{
public void mymethod(String param1, String param2){
//dostuff...
}
};
public abstract void mymethod(String param1, String param2);
}
Then in your class :
MyEnum.valueOf(mystring).mymethod(param1, param2);
Abuse the ternary operator, at least in C#:
Action result =
s == "bar" ? (Action)(() => { Console.WriteLine("bar"); }):
s == "foo" ? (Action)(() => { Console.WriteLine("foo"); }) :
(Action)(() => { Console.WriteLine(); });
Actually, I take that back... never EVER do this. Use a switch.
You can conceivably do something similar to the "strategy" pattern above using a map of Method calls instead:
public class FooOrBar {
private Map<String, Method> methodMap = new HashMap<String, Method>();
public FooOrBar() {
try {
methodMap.put("foo", this.getClass().getMethod("doFoo", new Class[0]));
methodMap.put("bar", this.getClass().getMethod("doBar", new Class[0]));
} catch (NoSuchMethodException n) {
throw new RuntimeException(n);
}
}
public void doSomething(String str) {
Method m = methodMap.get(str);
try {
m.invoke(this, null);
} catch (Exception n) {
throw new RuntimeException(n);
}
}
public void doFoo() {
System.out.println("foo");
}
public void doBar() {
System.out.println("bar");
}
public static void main(String[] args) {
FooOrBar fb = new FooOrBar();
fb.doSomething("foo");
}
}
My general perspective on this kind of problem is not that if statements are bad, it's that it's easier to debug data than it is to debug code.
Here's a non-trivial example from production code. This may look a little complicated at first blush, but at its core it's really simple: depending on the disposition code on a charge row, we need to perform an update to some of its related sentence rows. But we pick different sentence rows, and perform different kinds of updates on them, for different disposition codes.
This is a relatively simple example - there are only five disposition codes, two tests, and two types of updates. Even so, this is vastly simpler than what it replaced. Also, it's a lot easier to tell just from looking at the code that it does what the requirements say it should do, since the mappings in the code correspond to tables in the requirements document. (Before I wrote this code, I had to rewrite the requirements document so that this stuff was all defined in a table. The original code was a mess because the requirements were a mess too. Rewriting the requirements to make them clearer exposed bugs in the requirements, too.)
It's worth emphasizing that it's pretty easy to write a unit test that covers 100% of this code. It's also worth emphasizing that the complexity of this code scales linearly with the number of disposition codes, predicates, and updates that it supports; if case or if statements were used, it would scale exponentially.
/// <summary>
/// Update a sentence's status to Completed [401110]
/// </summary>
/// <param name="senRow"></param>
/// <param name="eventDate"></param>
private static void CompleteSentence(DataRow senRow, DateTime eventDate)
{
senRow.SetField("SenStatus", "401110");
senRow.SetField("SenStatusDate", eventDate);
}
/// <summary>
/// Update a sentence's status to Terminated [401120]
/// </summary>
/// <param name="senRow"></param>
/// <param name="eventDate"></param>
private static void TerminateSentence(DataRow senRow, DateTime eventDate)
{
senRow.SetField("SenStatus", "401120");
senRow.SetField("SenStatusDate", eventDate);
}
/// <summary>
/// Returns true if a sentence is a DEJ sentence.
/// </summary>
/// <param name="senRow"></param>
/// <returns></returns>
private static bool DEJSentence(DataRow senRow)
{
return Api.ParseCode(senRow.Field<string>("SenType")) == "431320";
}
/// <summary>
/// Returns true if a sentence is a Diversion sentence.
/// </summary>
/// <param name="senRow"></param>
/// <returns></returns>
private static bool DiversionSentence(DataRow senRow)
{
return Api.ParseCode(senRow.Field<string>("SenType")).StartsWith("43");
}
/// <summary>
/// These are predicates that test a sentence row to see if it should be updated
/// if it lives under a charge disposed with the specified disposition type.
///
/// For instance, if the PDDispositionCode is 413320, any DEJ sentence under the
/// charge should be updated.
/// </summary>
private static readonly Dictionary<string, Func<DataRow, bool>> PDSentenceTests =
new Dictionary<string, Func<DataRow, bool>>
{
{"411610", DiversionSentence}, // diversion successful
{"413320", DEJSentence}, // DEJ successful
{"442110", DiversionSentence}, // diversion unsuccessful
{"442111", DiversionSentence}, // diversion unsuccessful
{"442112", DiversionSentence}, // diversion unsuccessful
{"442120", DEJSentence} // DEJ unsuccessful
};
/// <summary>
/// These are the update actions that are applied to the sentence rows which pass the
/// sentence test for the specified disposition type.
///
/// For instance, if the PDDispositionCode is 442110, sentences that pass the sentence
/// test should be terminated.
/// </summary>
private static readonly Dictionary<string, Action<DataRow, DateTime>> PDSentenceUpdates =
new Dictionary<string, Action<DataRow, DateTime>>
{
{"411610", CompleteSentence}, // diversion successful (completed)
{"413320", CompleteSentence}, // DEJ successful (completed)
{"442110", TerminateSentence}, // diversion unsuccessful (terminated)
{"442111", TerminateSentence}, // diversion unsuccessful (terminated)
{"442112", TerminateSentence}, // diversion unsuccessful (terminated)
{"442120", TerminateSentence} // DEJ unsuccessful (terminated)
};
private void PDUpdateSentencesFromNewDisposition()
{
foreach (DataRow chargeRow in PDChargeRows
.Where(x => PDSentenceTests.ContainsKey(x.Field<string>("PDDispositionCode"))))
{
string disp = chargeRow.Field<string>("PDDispositionCode");
foreach (DataRow s in CHGRows[chargeRow]
.ChildRows("CAS-SUBCRM-CHG-SEN")
.Where(x => PDSentenceTests[disp](x)))
{
PDSentenceUpdates[disp](s, EventDate);
}
}
}