views:

1108

answers:

16
+5  Q: 

anti-if campaign

I recently ran against a very interesting site that expresses a very interesting idea - the anti-if campaign. You can see this here at www.antiifcampaign.com. I have to agree that complex nested IF statements are an absolute pain in the rear. I am currently on a project that up until very recently had some crazy nested IFs that scrolled to the right for quite a ways. We cured our issues in two ways - we used Windows Workflow Foundation to address routing (or workflow) concerns. And we are in the process of implementing all of our business rules utilizing ILOG Rules for .NET (recently purchased by IBM!!). This for the most part has cured our nested IF pains...but I find myself wondering how many people cure their pains in the manner that the good folks at the AntiIfCampaign suggest (see an example here) by creating numerous amounts of abstract classes to represent a given scenario that was originally covered by the nested IF. I wonder if another way to address the removal of this complexity might also be in using an IoC container such as StructureMap to move in and out of different bits of functionality. Either way...

Question: Given a scenario where I have a nested complex IF or SWITCH statement that is used to evaluate a given type of thing (say evaluating an Enum) to determine how I want to handle the processing of that thing by enum type - what are some ways to do the same form of processing without using the IF or SWITCH hierarchical structure?

public enum WidgetTypes
{
    Type1,
    Type2,
    Type3,
    Type4
}

...

WidgetTypes _myType = WidgetTypes.Type1;

...

switch(_myType)
{
    case WidgetTypes.Type1:
        //do something
        break;

    case WidgetTypes.Type2:
        //do something
        break;

    //etc...
}
+4  A: 

...What?

I can't imagine how to do something like that without some kind of comparison. At all.

An anti-goto campaign I can imagine, but if seems like a pretty damn required control flow structure to me.

Kawa
Hence my post! I was wondering how people address the simple case above as in my thinking I can't see a clear way to do this. I am hoping to be enlightened!...as I can't think of a single project I have ever done that didn't have something similar or more complex in it which I would love to get rid of.
Andrew Siemer
I am now reading about an anti-for approach!
Andrew Siemer
Okay, so there are replacements. Frankly, I find these replacements more unreadable than the original code.
Kawa
That depends on amount of nested ifs I guess. And if what they do can be separated and isolated into table of functions or polymorfic objects. This is "do one thing" again.I find that a lot of nested ifs can be eliminated just by returning early.
Eugene
Dijkstra beat you to the anti-goto campaign. See Also: GoTo Statement Considered Harmful.
R. Bemrose
R. Bemrose: I know, right? That's what I was referring to, actually.
Kawa
+30  A: 

The problem is not the 'if' statement, it is the programmers who write bad code.

EDIT: Also, as others have pointed out, you should be using polymorphism (if available) when you are using if statements to check the type of an object, but if statements in and of themselves are very useful and fundamental constructs.

Ed Swangren
So incredibly true.
Kawa
Isn't that always the problem?
Greg D
Yup, it sure is :-).
Ed Swangren
Can any of you suggest good code to the programmers that write bad code?
Andrew Siemer
Sometime's it's better to make use of polymorphism or lookup tables, although in that case the problem is in placing the code in the wrong part of the program, not the conditionals.
IRBMe
@IRBMe: Yes, polymorphism should be used when you are checking the *type* of an object, but you can't use it in all circumstances. If I want to know whether or not an integer is less than 0, I'm using an 'if' dammit! :-)
Ed Swangren
A: 

Case statements work great to organize nested ifs.

Lance Roberts
Yes, however, no switch statements. :/ But I 100% agree with you.
AlbertoPL
+10  A: 

Switch statements can often be replaced by polymorphism:

abstract class WidgetBase {
  public abstract void DoSomething();
}

class Widget1 : WidgetBase {
  public override void DoSomething() {
    // Do something ...
  }
}

class Widget2 : WidgetBase {
  public override void DoSomething() {
    // Do something ...
  }
}

...

Widget widget = new Widget1(); // Use a factory pattern instead.
widget.DoSomething();
Martin Liversage
My question here though is how do I know which widget do I want to use without evaluating the type of widget that I need to process? I can see using interfaces and and a WidgetFactory to return the appropriate widget...but inside that WidgetFactory (or somewhere) wouldn't I still have some evaluation type code to help me decide which widget to return/use?
Andrew Siemer
This doesn't really answer the question. It just means that instead of deciding which code to invoke, you have to decide which class to instantiate, which contains the code you want to invoke. You would have to do something like place instances of each class in some sort of mapping data structure (such as a Dictionary), which maps the widget type to the correct object. I gave an example of that in my answer. Also, the base class should probably be an interface.
IRBMe
+2  A: 

Nice looking site.

Bad idea for a campaign.

Janie
Yeah, it was kinda nice. Except for the link-like parts that weren't.
Kawa
+8  A: 

You can use the Command Pattern to perform commands based on some value or input.

In answer to your question, you can use a simple lookup table or polymorphism to achieve this, although it places the logic in a different part of the program, which may indeed be the correct thing to do. Here are a couple of examples in C#:

var commands = new Dictionary<WidgetTypes, Action>()
 {
  { WidgetTypes.Type1, () => Console.WriteLine("Type 1") },
  { WidgetTypes.Type2, () => Console.WriteLine("Type 2") },
  { WidgetTypes.Type3, () => Console.WriteLine("Type 3") },
  { WidgetTypes.Type4, () => Console.WriteLine("Type 4") }
 };

commands[WidgetTypes.Type1]();


public interface ICommandHandler
{
 void HandleCommand();
}

public class Command1Handler : ICommandHandler
{
 public void HandleCommand()
 {
  Console.WriteLine("Type 1");
 }
}

// ...

var commands = new Dictionary<WidgetTypes, ICommandHandler>()
 {
  { WidgetTypes.Type1, new Command1Handler() },
  { WidgetTypes.Type2, new Command2Handler() },
  { WidgetTypes.Type3, new Command3Handler() },
  { WidgetTypes.Type4, new Command4Handler() }
 };

commands[WidgetTypes.Type1].HandleCommand();

And a bonus reflection method to invoke the method on the current object with the same name:

public void Type1()
{
 Console.WriteLine("Type 1");
}

//...

var type = WidgetTypes.Type2;
typeof(MyClass).GetMethod(type.ToString()).Invoke(this, new object[] { });

Note: don't actually do that

IRBMe
Very nice. I will look at this pattern more (http://www.dofactory.com/Patterns/PatternCommand.aspx).
Andrew Siemer
A: 

Wow... is this for real?

You could try catch absolutely every exception possible when you go down the wrong execution path until you hit a path of execution that DOESN'T break, but that is FAR worse than if's.

You can use a Command pattern or a Map for when object-orientation and polymorphism is natural, but what if you're dealing with outcomes from user input that are complex and NOT in and of themselves objects?

AlbertoPL
Yes...I wondered what people would think of this! <GRIN>
Andrew Siemer
+7  A: 

Replace Conditional with Polymorphism

William
ding ding ding, we have a winner
Janie
That only applies when your condition is based on the TYPE of the argument...which may be exactly what you intend to convey. :)
Bill the Lizard
After reading some of the material in the campaign web page linked in the question, I think this is correct.
Bill the Lizard
+3  A: 

It depends on how you handle the situation. If you called a different function using the same signature, you could have a dictionary where the key would be the type and the value a delegate pointing to the real handler. Thus you can get rid of the switch and just do

_myDictionary[ _myType ]( param1, ... );
Trap
A: 

Implementing that idea in a C-like language is very lame... but in a proper functional language with guarded pattern matching is much more idiomatic and a richer way to express conditionals, I dare to add.

fortran
+1  A: 

I think I understand what they mean. We probably won't survive without if's and switches, but it is true that deeply nested conditional blocks are a pain to read.

An example from my own job: a function to calculate the simulated strain of a shunted wheatstone bridge. This function takes like 5 configuration parameters (which shunt branch, are there any shunt sense lines, supply sense lines, ...), and I wasn't quite looking forward to writing the dispatching routine.

I came up with a templated code structure that allowed me to 'declare' the proper functions (about 30 or so) instead of having to crunch them into one dispatching routine.

This enabled us to very quickly add new configuration parameter cases, and kinda 'copy' the equations from the mathematical overview. Quite a win, really.

xtofl
+1  A: 

Don't worry about the simple if statement, or the straight forward switch statement.

Worry about this:

  1. Horribly nested if statements (the Arrowhead anti-pattern). These things are impossible to read and debug.
  2. The if (or case) with a bagilion lines of code in them. "OK, we do this and this.... in this case, and that and that... in that case. Also very hard to read. Almost impossible to test or debug.
  3. Super complicated conditions: if (bowl AND KitchenSink OR BathroomTub AND ...). What are you testing for again? Wrap that in a method -- in this case: CanHoldWater().
Chris Brandsma
In general, an "if () statement else if () statement else if () statement" construct, sort of a generalized switch, is easy to read and understand (assuming that the individual statements aren't too long). You've nailed the problem cases.
David Thornley
A: 

Polymorphism is the right way to go when the logic must be baked into the classes.

But since many applications these days already interact with their own database, another solution is to use table-based logic. Advantages of using tables:

  1. Adjust business logic without recompile.
  2. Database instances can share the same code but different logic.

I'm not talking about directly embedding source code in a database varchar() field and compiling it on the fly.

But let's say you have a list of input fields, and for each one, you need to perform (a) some RegEx to scrub the value, and (b) one or more tests to validate the result.

In a code-only world, you would have a subclass for each type of field, each implementing, say, it's own methods called ScrubValue() and Validate().

In a table-based pattern, a single Field class would reach into the database (cached of course) based on a FieldType property and retrieve a set of RegEx search/replace strings, and a list of validation test parameters (minimum and maximum length, regex patterns to match, etc.).

This works well for any situation where the number of subtypes is increasing over time and the logic can be expressed as a set of configurable strings, numbers, etc. rather than having to be directly implemented as code.

richardtallent
A: 

Being anti-if is silly.

Sometimes replacing a conditional via polymorphism is the right thing to do, but in those cases it wasn't the if statement that was the true problem. The real problem was working with abstract types in non-abstract ways, i.e., failing to think at level of abstraction of the base class.

Other times it is possible to replace a conditional via polymorphism, but doing so would be a bad idea. The logic that leads you to treat one type different from another may belong in the algorithm itself, not the individual classes. Moving that logic to the classes may cause the classes' code to be overly aware of the context in which they are used.

But most often, an if statement has nothing to do with polymorphism at all. Getting rid of if statements is the wrong goal here.

Being anti-if is silly.

Darryl
A: 

Once your if statements start becoming big and nested, one of the possiblities may be that the number of conditions you have to test has grown. In that case, as you add more conditions to the situation it starts becoming exponentially easier to let some combination of conditions fall through the cracks, and then bam, you've got yourself a bug.

I always thought in cases like these, some kind of truth table would be good. About the closest approximation I can come up though ends up being kind of baffling to the average programmer. It involves assembling all your conditions into a single integer, and then trying to cover every number in the range that creates with either a switch statement or a lookup table.

I'm not sure polymorphism is the right answer for this one though. Anyone got any better ideas?

Breton
+6  A: 

In Java it's easy to use enums as polymorphic anti-if agents.

public class AntiIf {
  public enum WidgetTypes {
    Type1 {
      public void doSomething() {
        //...
      }},
    Type2 {
      public void doSomething() {
        //...
      }},
    Type3 {
      public void doSomething() {
        //...
      }},
    Type4 {
      public void doSomething() {
        //...
      }};

    public abstract void doSomething();
  }

  WidgetTypes _myType; // set by someone to one of the types.

  public void someFunction() {
    //...
    _myType.doSomething();
    //...
  }
}
Robert C. Martin
+1 How YOU of all people didn't get any up votes amazes me! "heads down programmers" must not know you too well! :P
Andrew Siemer