views:

874

answers:

12

Hello!

I have a code for updating my application resources to current application version. This code is called after application update.

int version = 1002;   // current app version

switch(version)
{
   case 1001:
      updateTo1002();
      goto case 1002;

   case 1002:
      updateTo1003();
      goto case 1003;

   case 1003:
      updateTo1004();
      goto case 1004;
      break;

   case 1004:
      updateTo1005();
      break;
}

Here we have a cascade method calling by jumping to specified case block. I wonder - is that good practice to use go to (often considered as such bad practise!) in this case? I do not want to call method one by other - like this:

updateTo1002()
{
   // do the job
   updateTo1003();
}
updateTo1003()
{
   // do the job
   updateTo1004();
}

It there any design pattern describes such an issue?

+6  A: 

goto is always considered as bad practice. If you use goto it is usually harder to read code and you always can write your code differently.

For example you could used linked list to create a chain of methods and some processor class that processes the chain. (See pst's answer for good example.). It is much more object oriented and maintainable. Or what if you have to add one more method call beetween 1003 and case 1004?

And of course see this question.

alt text

Andrew Bezzub
Uninformed answer is uninformed.
Nathan Taylor
ok, but why always?
UGEEN
Have to agree. It's dogmatic and not true. I'll admit that I haven't use a goto since I first learned Pascal 27 years ago, but it's still not a good answer.
Adam Crossland
If your justification for never using goto is a Velociraptor attack, I think your argument needs work.
Jon B
Whether it is or not is besides the point, provide a better sample than OP without using goto or else don't universally state that goto is always bad.
*Always* is an extremely strong word. It's also an extremely overused word. It makes me skeptical, by default, whenever I see someone use it. I can't think of a good reason to use GOTO, and I'm not sure this question's issue is one, but I wouldn't say it's 'always' a bad practice.
Andrew Barber
@Nathan It's not uninformed - can't you **see** the **Velociraptor**??
djacobson
Added example how goto code could be refactored.
Andrew Bezzub
It is patently untrue that using goto is always less readable than an alternative. I have seen countless examples of code that went through hideous gyrations to avoid an obvious situation where `goto` would have provided a clear and obvious solution.
Adam Crossland
Sorry, but the linked list and processor method sounds 'complicated.' And unless it is unprecedentedly well-documented, it is not going to be easier to read and understand than a simple `goto`. Regardless, @Andrew, no matter how many really, reaaly good examples of not using `goto` you provide, I'm still going to think that it's not enough evidence to demonstrate that `goto` is ALWAYS bad.
Adam Crossland
@Adam It is always a bad practice in c# since c# is purely object oriented while goto is not. I think it was left in c# only for code-generating purposes.
Andrew Bezzub
Seems like the number of un/under-informed people is quite high. Current score on this answer is +6/-5. :\
Nathan Taylor
How about we expand this problem to have 100 versions? How does this code look now? We need to stop looking at the very small sample data set for this example and look at the big picture and provide a design pattern to solve the problem more completely, not this very particular instance of the problem.
Dave White
I'm pretty sure that at some point someone proved that a series of alglorithms existed (that didn't calculate anything useful) that became exponentially more complicated to specify without GOTO's, but with GOTO's only became linearly more complicated.
DonaldRay
+1 for the altnerative to goto. @pst has nice sample code for this.
0xA3
@Andrew: C# `goto` isn't nearly as dangerous as `goto` in other languages in that it does not allow you to jump from outside a block into a block. While it is rare for `goto` to be appropriate in C#, `goto` is not universally evil; there are some problems where using `goto` results in much cleaner code than not using it. Mind you, I haven't noticed any instances of this situation in my own code.
Brian
If goto is always considered bad practice, they wouldn't put it into the language.
Michael Stum
The `goto` statement in C# was *intended specifically* for this scenario.
Gabe
@Andrew, did you realize that _you_ are the dinosaur in the cartoon?
Henk Holterman
@Henk Holterman: Is there an echo in here? ;-)
0xA3
I tried to use goto in C# on Mono for the Mac, inside of a lambda expression and it crashed the compiler. I just rewrote it with if statements (and an extra control flow bool, [shudder]).
Jared Updike
+1  A: 

I would say that this is a very eligible reason to use the GOTO feature.

http://weblogs.asp.net/stevewellens/archive/2009/06/01/why-goto-still-exists-in-c.aspx

In fact, the switch() statement in C# is effectively a pretty face for a collection of labels and a hidden goto operation. case 'Foo': is just another way of defining a type of label inside the scope of a switch().

Nathan Taylor
One problem I have with that article is the appeal to authority he has at the end: "Before you disagree with the inclusion of the goto in the C# language, remember you are disagreeing with the people who created the language. You are also disagreeing with Steve McConnel the author of "Code Complete"." - not just a logical fallacy, but I've seen proof that the framework and language designers didn't always like their own decisions (e.g. their decision to add data to every single object, just to support lock, not always following naming conventions/not always supporting tryparse, etc)
Merlyn Morgan-Graham
@Merlyn - One more problem with that article `if (Step1() == true)`
Ishtar
@Merlyn and @Ishtar shoot the author, not the copy-paster! :p
Nathan Taylor
@Nathan: No worries, I didn't down-vote
Merlyn Morgan-Graham
+31  A: 

In the example the version is increasing and always calling the earlier ones in sequence. I think that a set of if statements is probably more appropriate here

if (version == 1001 ) { 
  updateTo1002();
}

if (version <= 1002) {
  updateTo1003();
}

if (version <= 1003) {
  updateTo1004(); 
}

if (version <= 1004) {
  updateTo1005();
}

Some have commented that this approach is unmaintainable as the number of versions gets higher (think 50 or so). In that case here is an easier to maintain version

private List<Tuple<int, Action>> m_upgradeList;

public Program()
{
    m_upgradeList = new List<Tuple<int, Action>> {
        Tuple.Create(1001, new Action(updateTo1002)),
        Tuple.Create(1002, new Action(updateTo1003)),
        Tuple.Create(1003, new Action(updateTo1004)),
        Tuple.Create(1004, new Action(updateTo1005)),
    };
}

public void Upgrade(int version)
{
    foreach (var tuple in m_upgradeList)
    {
        if (version <= tuple.Item1)
        {
            tuple.Item2();
        }
    }
}
JaredPar
This is a very good point indeed.
Nathan Taylor
in my case update methods must be called incrementally - previous method do not cover functionality of current - so I would have to set if(version == number) for each cases separately anyway
UGEEN
@UGEEN, this solution does call them incrementally. If version started out as 1002 it would call `updateTo1003`, `updateTo1004` and `updateTo1005` in sequence.
JaredPar
@JaredPar - yes you've right, it will work, but as I see, I can use '==' operator insteed of '<='
UGEEN
@UGEEN, that won't work unless you put in a big while loop or update version each time
The value of version could be updated with each call to updateToX(). It might make it clearer about what the current version is.
Daniel Ballinger
I still don't like this for the same reason I commented on another answer (which has been deleted). As we get more and more version numbers that we need to support, this is going to turn into an really hard to maintain mess. The Chain of Responsibility pattern is, imho, a more appropriate design pattern to apply to this problem.
Dave White
@Dave, OP question suggested a smaller number of versions. Added an alternative for larger version numbers.
JaredPar
@Jared - Thanks Jared! That is much better than the if/switch/goto mess previous, but it is significantly different than your original suggested answer. Which one, in hind sight, would you want as "the" answer you would most support? :)
Dave White
@Dave, probably the most recent version. I have a personal style affinity for nice type loops, delegates and tuples so it appeals to me. It also has the least going forward overhead (upgrade function + 1 entry in a table).
JaredPar
@JaredPar - tuple example looks clear and easy to manage - interesting - thank you.
UGEEN
+5  A: 

I hate blank statements that don't provide supporting information, but goto is fairly universally panned (for good reason) and there are better ways to achieve the same results. You could try the Chain of Responsibility pattern that will achieve the same results without the "spaghetti-ish" goo that a goto implementation can turn into.

Chain of Responsibility pattern.

Dave White
+1 (though leave it to OO to take 2-line flow control, and turn it into 5-10 line classes, multiplied by the number of patches you need to do :). Even still, that "updateTo2002()" has to go somewhere...)
Merlyn Morgan-Graham
A: 

I think it's alright, although I doubt this is your real code. Are you sure you don't need to encapsulate an Update method inside an AppUpdate class or something? The fact that you have methods named XXX001, XXX002 etc. is not a good sign, IMO.

Anyway. here's an alternative with delegates (not really suggesting you use it, just an idea):

var updates = new Action[] 
                 {updateTo1002, updateTo1003, updateTo1004, updateTo1005};

if(version < 1001 || version > 1004)
   throw new InvalidOperationException("...");    

foreach(var update in updates.Skip(version - 1001))
   update();

It would be difficult to recommend the most appropriate pattern without more detail.

Ani
I find that code to be more difficult to read than the original goto code.
Holstebroe
@Holstebroe - Look at the original code with 100 version choices in it. This may "look" harder if you are unfamiliar, but this is a step towards testability and maintainability.
Dave White
The code doesn't have 100 version choices, it has 5. The point being that the cost of generalization only starts to pay off at a certain complexity level. Of course one should have the discipline to refactor when that level is reached.
Holstebroe
+36  A: 

Well, if we want to be "object oriented", why not let the objects-do-the-talking?

var updates = availableUpdates.Where(u => u.version > ver).OrderBy(u => u.version);
foreach (var update in updates) {
  update.apply();
}
pst
Finally, an answer that is starting to make some sense! All this `if`, `switch` and `goto` non-sense is scaring me!
Dave White
This is what I meant, good example!
Andrew Bezzub
Nitpick: this is really more functional than object-oriented. Which is *exactly* the right thing to do here, of course.
Jörg W Mittag
cute and crystal clear - another reason to switch to linq
UGEEN
+1. I believe this is the best case I have ever seen for linq. Excellent.
Carl Manaster
+1  A: 

I think perhaps the logic is somewhat backwards here and causing the problem. What if your methods looked like this:

updateTo1002() 
{ 
   if (version != 1001) {
       updateTo1001();
   }
   // do the job     
} 
updateTo1003() 
{ 
   if (version != 1002) {
       updateTo1002();
   }
   // do the job     
} 

I don't know your exact use case, but it would seem to me like most often you would want to update to the most recent version, but install the incremental updates as needed along the way. I think doing it this way captures that logic better.

Edit: from @user470379's comment

In this case, mostly it's identifying the fact that you have a copy/paste pattern and editing it.

The coupling problem is, in this case, barely an issue but could be. I'll give you a few things that could come up in your scenario that would be hard to code if done this way:

  • Every update now needs an extra cleanup step, so after updateTo1001() call cleanup(), etc.
  • You need to be able to step back in order to test older versions
  • You need to insert an update between 1001 and 1002

Let's take a combination of these two done following your pattern. First let's add an "undoUpgradeXXXX()" to undo each upgrade and be able to step backwards. Now you need a second, parallel set of if statements to do the undos.

Now, let's add to that "insert 1002.5". All of a sudden you are re-writing two potentially long chains of if statements.

The key indication that you are going to have these kind of problems is that you are coding in a pattern. Look out for patterns like this--in fact, one of my first indications is usually when I'm looking over someone's shoulder at their code, if I can see a pattern without even being able to read anything written like this:

********
   ***
   *****

********
   ***
   *****
...

then I know I'm going to have problems with their code.

The easiest solution is generally to remove the differences from each "group" and put them into data (often an array, no necessarily an external file), collapse the groups into a loop and iterate over that array.

In your case, the easy solution is to make each of your upgrade objects with a single upgrade method. Create an array of these objects and when it's time to upgrade, iterate over them. You may also need some way to sequence them--You're currently using a number, that might work--or a date might be better--that way you can "Go to" a given date easily.

A few differences now:

  • Adding a new behavior to each iteration (cleanup()) would be a single line modification to your loop.
  • Reordering would be localized to modifying your objects--possibly even simpler.
  • Breaking your upgrade into multiple steps that must be called in order would be easy.

Let me give you an example of that last one. Suppose after all your upgrades have been run you need to go through an initialize step for each (different in each case). If you add an initialize method to each object then the modification to your initial loop is trivial (simply add a second iteration through the loop). In your original design you'd have to copy, paste & edit the entire if chain.

Combine JUST undo & initialize and you have 4 if chains. It's just better to identify problems before you start.

I can also say that eliminating code like this can be difficult (downright tough depending on your language). In Ruby it's actually pretty easy, in java it can take some practice and many can't seem to do it so they call Java inflexible and difficult.

Spending an hour here and there mulling over how to reduce code like this has done more for my programming abilities than any books I've read or training I've had.

Also it's a challenge, gives you something to do instead of editing huge if-chains looking for the copy/paste error where you forgot to change 8898 to 8899. Honestly it makes programming fun (which is why I spent so much time on this answer)

Unmaintainable.
Dave White
@DaveWhite Explain. If you're concerned about the verbosity it could be shortened to `if (version != 1001) updateTo1001();`
Unmaintainable was harsh, but imagine your example with 20 version numbers. 50 version numbers. 100 version numbers. Do you want to maintain that code? Replace Unmaintainable with "Very hard to maintain".
Dave White
I don't think any of the current answers are overly verbose. Terse, not many of them, but nothing that would startle me.
Dave White
@Dave I would tend to agree, but compared to most of the other answers here I would say it's about even: A `goto` for each update vs. an if block around each update vs. this is about the same, while giving the flexibility to allow multiple update paths if needed.
I've commented similarly on the other answers too, as appropriate.
Dave White
Bill K
@user470379 See the answer, I added a bunch to it.
Bill K
+2  A: 

I would suggest a variation of the command pattern, with each command being self-validating:

interface IUpgradeCommand<TApp>()
{
    bool UpgradeApplies(TApp app);
    void ApplyUpgrade(TApp app);
}

class UpgradeTo1002 : IUpgradeCommand<App>
{
    bool UpgradeApplies(App app) { return app.Version < 1002; }

    void ApplyUpgrade(App app) {
        // ...
        app.Version = 1002;
    }
}

class App
{
    public int Version { get; set; }

    IUpgradeCommand<App>[] upgrades = new[] {
        new UpgradeTo1001(),
        new UpgradeTo1002(),
        new UpgradeTo1003(),
    }

    void Upgrade()
    {
        foreach(var u in upgrades)
            if(u.UpgradeApplies(this))
                u.ApplyUpgrade(this);
    }
}
dahlbyk
I think this solution is interesting, but in practical usage I think it's a little bit triumph of form over content
UGEEN
Depends on your needs and other implementation details. The individual upgrade implementations are easily testable, have a single responsibility, and you could configure an IoC container to automatically inject all implementers of IUpgradeCommand<> so you don't have to manage the list by hand. You did ask for an OO solution... ;)
dahlbyk
A: 

I've had to deal with some such a problem (get file into this format, so it can be gotten into this other format, etc.) and I don't like the switch statement. The version with 'if' tests might be good, or it might be good to recursively have something like:

/* Upgrade to at least version 106, if possible.  If the code
   can't do the upgrade, leave things alone and let outside code
   observe unchanged version number */

void upgrade_to_106(void)
{
  if (version < 105)
    upgrade_to_105();
  if (version == 105)
  {
    ...
    version = 105;
  }
}

Unless you have thousands of versions, stack depth shouldn't be a problem. I think the if-test version, testing specifically for versions that each upgrade routine can handle, reads better; if the version number at the end isn't one the main code can handle, signal an error. Alternatively, lose the "if" statements in the main code and include them in the routines. I don't like the 'case' statement because it doesn't consider the possibility that a version-update routine might not work, or might update more than one level at a time.

supercat
A: 

I threw into a comment that using goto is never worth the crap you'll take for using it (even if it's an awesome, perfect use)--too many programmers learn something and can never get it unstuck from their brain.

I wasn't going to post an answer, but I don't think it's been made clear enough that the entire solution you are implying is just wrong. I had assumed it was just to make your point, but it should be made clear: Be very careful of patterns in code--this is just as bad as copy/paste code (in fact, it IS copy/paste code).

You should just have a collection of objects, each with the upgrade code and a version number.

You simply iterate over that collection while the version number is < your target version, and call the upgrade code on that object for each one. That way to create a new upgrade level you are just making a single "Upgrade" object and sticking it into the collection.

Your chain of upgrade objects can even be traversed backwards as well as forwards with the addition of an undo and a very trivial code addition to the controller--something that would become a nightmare to maintain using the example code.

Bill K
A: 

You may look at State Machine workflow pattern. The simple and usefull for you may be: Stateless project

BrainMaxx
+3  A: 

why not:

int version = 1001;

upgrade(int from_version){
  switch (from_version){
    case 1000:
      upgrade_1000();
      break;
    case 1001:
      upgrade_1001();
      break;
    .
    .
    .
    .
    case 4232:
      upgrade_4232();
      break;
  }
  version++;
  upgrade(version);
 }

Sure, all this recursion creates overhead, but not all that much (with a call to the carbage collector only a context and an int), and it's all packaged up to go.

Note, I don't mind the goto's much here, and the tuple (int:action) variations have their merits too.

EDIT:

For those who don't like recursion:

int version = 1001;
int LAST_VERSION = 4233;

While (version < LAST_VERSION){
  upgrade(version);
  version++;
}

upgrade(int from_version){
  switch (from_version){
    case 1000:
      upgrade_1000();
      break;
    case 1001:
      upgrade_1001();
      break;
    .
    .
    .
    .
    case 4232:
      upgrade_4232();
      break;
  }

}
Martijn