28 Mar, 2010, Skol wrote in the 1st comment:
Votes: 0
I'm unclear on switch statements being a unwanted approach Sash?
Is it really better to write a 300 count if/else if/else check you think? I just find it more concise when dealing with those kinds of variables to use a switch and simply add cases. I use switch often in PHP and C.

Anyway, I'd never heard of that and was curious.
29 Mar, 2010, David Haley wrote in the 2nd comment:
Votes: 0
Shas would handle all switches using OOP, or at least so he has said in the past. He believes them to be unclear and harder to understand than polymorphism etc.

flumpy said:
I don't think we disagree as much as it seems sometimes DH

We just need to grab a meal and have a beer or two if/when we're in the same geographical vicinity. :biggrin:
29 Mar, 2010, Runter wrote in the 3rd comment:
Votes: 0
David Haley said:
Shas would handle all switches using OOP, or at least so he has said in the past. He believes them to be unclear and harder to understand than polymorphism etc.

flumpy said:
I don't think we disagree as much as it seems sometimes DH

We just need to grab a meal and have a beer or two if/when we're in the same geographical vicinity. :biggrin:


It's a traaaaap.
29 Mar, 2010, flumpy wrote in the 4th comment:
Votes: 0
Runter said:
David Haley said:
Shas would handle all switches using OOP, or at least so he has said in the past. He believes them to be unclear and harder to understand than polymorphism etc.

flumpy said:
I don't think we disagree as much as it seems sometimes DH

We just need to grab a meal and have a beer or two if/when we're in the same geographical vicinity. :biggrin:


It's a traaaaap.


It's ok, I have my detect trap scroll ready and waiting… and then I'm going to cast "befuddle" :tongue:
29 Mar, 2010, flumpy wrote in the 5th comment:
Votes: 0
David Haley said:
Shas would handle all switches using OOP, or at least so he has said in the past. He believes them to be unclear and harder to understand than polymorphism etc.


I tend to agree, large switch statements are fugly. Don't get me wrong, they have their place, but any more than about 3 or 4 cases is too cyclomatically complex for my liking.
29 Mar, 2010, flumpy wrote in the 6th comment:
Votes: 0
Skol said:
I'm unclear on switch statements being a unwanted approach Sash?
Is it really better to write a 300 count if/else if/else check you think? I just find it more concise when dealing with those kinds of variables to use a switch and simply add cases. I use switch often in PHP and C.

Anyway, I'd never heard of that and was curious.


A "better way" (lol) than to use 300 if statements or large fugly switch cases would be to write a chain-of-responsibility type pattern, in which you break the decisions up into lots of objects that perform one task each in order. Each object is responsible for checking the conditions normally handled in the if statement or switch (often using a single if or switch it's self), and forwarding on to the next object if the conditions pass or failing with an exception or error code returned.

This can simplify that kind of code immensely, and testing each object is far simpler. That said, I would never do this unless I got fairly knee deep in complex if statements or more than 3 or 4 switch cases. Often the simplest solution is the best, and this is not "simple" as in "simple to grok" on a small scale like that.
29 Mar, 2010, shasarak wrote in the 7th comment:
Votes: 0
Skol said:
I'm unclear on switch statements being a unwanted approach Sash?
Is it really better to write a 300 count if/else if/else check you think? I just find it more concise when dealing with those kinds of variables to use a switch and simply add cases. I use switch often in PHP and C.

Anyway, I'd never heard of that and was curious.

David Haley said:
Shas would handle all switches using OOP, or at least so he has said in the past. He believes them to be unclear and harder to understand than polymorphism etc.

Switch statements are certainly necessary in procedural languages, but they're often a sign that something is amiss in OO code. It's notjust a question of readability, however; far from it. I's an important aspect of correct class design too.

Consider the following example.

Animal pet = Shasarak.Pets[0];
string speech;

switch pet.Species
case "cat":
speech = "miaouw";
break;

case "dog":
speech = "woof";
break;

case "mouse":
speech = "squeak";
break;

default:
speech = "something unintelligible";
break;

WriteToOutput("Your pet says " + speech);
Now, there's a problem with this. This bit of code is testing the species of my pet, and then deciding what noise to display depending on the pet's species. That's bad OO. The information about what noise the species makes is something that the species class itself should control. Therefore, the code shouldn't say "if you're a cat, say 'miaouw'"; the code should say "make a noise", and then the animal itself responds differently, depending on which species it is.

So the outer code would now look like this:

Animal pet = Shasarak.Pets[0];
WriteToOutput("Your pet says " + pet.Noise());
You then define Noise() as a virtual function on the Animal superclass:

public abstract class Animal {

public virtual string Noise() {
return "something else";
}
}
Then, on each subclass of animal, you override that method, for example:

public class Cat : Animal {

public override string Noise() {
return "miaouw";
}
}
Or:

public class Dog : Animal {

public override string Noise() {
return "woof";
}
}
And so on.

So, yes, this is much easier to read. It's also much easier to understand what the code does. You look at the top-level code and you say "okay, so it's writing out the noise the pet makes". That may well be all you need to know at that point, in which case you don't need to delve into how the noise is determined. If you're experiencing a bug which causes cats to say "woof", then at that point you need to delve into the implementation of the Cat class to find out why, but at least you now know where to look, and the source of the problem is easy to locate.

An awful lot of good OO design is getting the right piece of code in the right place; and switch statements in OO are often an indicator that you're breaking encapsulation and you should be using polymorphism instead. It's notable that Smalltalk - one of the purest OO languages around - doesn't even have a switch statement. There are very good reasons for that!
29 Mar, 2010, shasarak wrote in the 8th comment:
Votes: 0
David Haley said:
Yes, well, to be honest it was meant to point out the silliness I found in what you'd initially said

And that statement, I suppose, is also not intended to be patronising?

David Haley said:
I don't view it as "scoring points" to point out that what you're saying is hyperbolic

It wasn't hyperbolic in the slightest. If a calculation is "complex" it just means you're carrying out more steps to complete it; it doesn't mean that the calculation somehow no longer has steps.

David Haley said:
Shasarak said:
If an API requires you to know its implementation details in order to use it properly, then that API has been written badly.

Not necessarily. There are many cases, typically related to performance, where implementation details can be very important. The API can, for instance, in some cases provide far better performance if operations are performed in this order rather than that order. You can still use it "properly", in the sense of "correctly", without following that order, but knowing the implementation details can dramatically improve performance.

In that case you need to know that certain invocations will create performance issues; you don't need to know why.
29 Mar, 2010, flumpy wrote in the 9th comment:
Votes: 0
shasarak said:
An awful lot of good OO design is getting the right piece of code in the right place; and switch statements in OO are often an indicator that you're breaking encapsulation and you should be using polymorphism instead. It's notable that Smalltalk - one of the purest OO languages around - doesn't even have a switch statement. There are very good reasons for that!


Ok taking your example for a base, what about if you had some kind of AnimalFactory object that created your required animal? How would you encapsulate the decision to create the right animal without having to specify the class of animal you require?

Using a switch in that scenario would read better than a nested if (or indeed any kind of further encapsulation):

class AnimalFactory{
static Animal createAnimal(String type){
Animal animal = null
if(type == "cat"){
animal = new Cat()
}
if(type == "dog"){
animal = new Dog()
}

if(animal == "cow"){
animal == new Cow()
}
return animal
}
}


as opposed to:
class AnimalFactory{
static Animal createAnimal(String type){
Animal animal = null
switch(type){
case "cow":
animal = new Cow()
break
case "dog"
animal = new Dog()
break

case "cow"
animal = new Cow()
break
}
return animal
}


.. in this (contrived) scenario the switch statement wins for readability, and is perfectly suited to the task. I mean its contrived nicely, because in a real scenario you would create a generic animal object and use a factory to configure it (with some meta data on how different animals behave, perhaps) to the animal you require rather than specialising with inheritance. Or at least, that's what I'd do.
29 Mar, 2010, flumpy wrote in the 10th comment:
Votes: 0
flumpy said:
class AnimalFactory{
static Animal createAnimal(String type){
Animal animal = null
switch(type){
case "cow":
animal = new Cow()
break
case "dog"
animal = new Dog()
break

case "cow"
animal = new Cow()
break
}
return animal
}


lol I have two cows in my example. One's ok, the udder is moooot. :biggrin:

(sorry)
29 Mar, 2010, David Haley wrote in the 11th comment:
Votes: 0
Shasarak said:
And that statement, I suppose, is also not intended to be patronising?

Unless you feel that disagreeing with you is patronizing, no, it was not meant to be patronizing.

Shasarak said:
If a calculation is "complex" it just means you're carrying out more steps to complete it; it doesn't mean that the calculation somehow no longer has steps.

I'm not sure what this has to do with comments.

By the way, some calculations are complex without having that many steps. I spoke to this already with math, scientific or numeric code (in my case, I'm thinking in particular of certain machine learning algorithms or financial math I've seen/written). I hate to say it but I fear that you might have missed the point there. (That's not meant to be patronizing either.) If you disagree or aren't sure what I mean, please just say so or ask me to elaborate, it would be much more expedient.

Shasarak said:
David Haley said:
Not necessarily. There are many cases, typically related to performance, where implementation details can be very important. The API can, for instance, in some cases provide far better performance if operations are performed in this order rather than that order. You can still use it "properly", in the sense of "correctly", without following that order, but knowing the implementation details can dramatically improve performance.


In that case you need to know that certain invocations will create performance issues; you don't need to know why.

Hmm, well, yes, perhaps, but we've established now that implementation can matter whereas before it wasn't supposed to matter in the slightest. Sometimes you cannot explain the cases where performance will matter without speaking more about what is actually happening. No, you don't have to explain every implementation detail. Database prefetching tuning is an example of this. There are others but hopefully the case is clear now.
29 Mar, 2010, KaVir wrote in the 12th comment:
Votes: 0
flumpy said:
lol I have two cows in my example. One's ok, the udder is moooot. :biggrin:

Another reason why I prefer the switch statement over if-else; the compiler spots duplicates.
29 Mar, 2010, Idealiad wrote in the 13th comment:
Votes: 0
This doesn't help with duplicates, but in flumpy's example I would far prefer a hash/map/dictionary.
29 Mar, 2010, David Haley wrote in the 14th comment:
Votes: 0
Idealiad said:
This doesn't help with duplicates, but in flumpy's example I would far prefer a hash/map/dictionary.

Actually, it would help with duplicates, at least in the sense that you couldn't have any. :wink:

But you could quite easily have a dictionary that raised an exception if you tried to enter a key that already existed, for example.
29 Mar, 2010, shasarak wrote in the 15th comment:
Votes: 0
David Haley said:
Hmm, well, yes, perhaps, but we've established now that implementation can matter whereas before it wasn't supposed to matter in the slightest.

We have established nothing of the sort.

That there is a performance issue is relevant; what the code is actually doing internally when the performance issue is observed is not. You need to document the API's public behaviour; you don't need to document its private behaviour.
29 Mar, 2010, David Haley wrote in the 16th comment:
Votes: 0
Performance issues stem from implementation. If performance matters, implementation matters. This is pretty much a logical truism so I'm not sure what is wrong with it; perhaps you could elaborate…

I didn't say that you had to expound on the implementation. I point you to what I said: "Sometimes you cannot explain the cases where performance will matter without speaking more about what is actually happening. No, you don't have to explain every implementation detail." There are various degrees of implementation detail, Shasarak, as you are surely aware. It is somewhat disingenuous to say that you don't need to discuss it in the slightest and to purport that it is absolutely irrelevant and unhelpful to document it in even the slightest manner.
30 Mar, 2010, Skol wrote in the 17th comment:
Votes: 0
Ok, the case that comes into mind (no puns intended ;p) is say one I did for a farm bureau website. There are say 35 counties in the state, so I run a switch statement for each case, and default takes the first one (so that people don't run an injection attack etc).

This yields the value as well as what it reads:
print '	<tr><td align="right">Farm Bureau:</td>
<td><input type="hidden" value="'.$county.'">';
switch ($county)
{
case '1_baker': echo 'Baker'; break;
case '2_benton': echo 'Benton'; break;
case '3_clackamas': echo 'Clackamas'; break;
case '4_clatsop': echo 'Clatsop'; break;
case '5_columbia': echo 'Columbia'; break;
case '6_coos_curry': echo 'Coos-Curry'; break;
case '7_crook_wheeler': echo 'Crook-Wheeler'; break;
case '9_deschutes': echo 'Deschutes'; break;
case '10_douglas': echo 'Douglas'; break;

case '11_gilliam': echo 'Gilliam'; break;
case '12_grant': echo 'Grant'; break;
case '13_harney': echo 'Harney'; break;
case '14_hood_river': echo 'Hood River'; break;
case '15_jackson': echo 'Jackson'; break;
case '16_jefferson': echo 'Jefferson'; break;
case '17_josephine': echo 'Josephine'; break;
case '18_klamath_lake': echo 'Klamath-Lake'; break;
case '20_lane': echo 'Lane'; break;

case '21_lincoln': echo 'Lincoln'; break;
case '22_linn': echo 'Linn'; break;
case '23_malheur': echo 'Malheur'; break;
case '24_marion': echo 'Marion'; break;
case '26_multnomah': echo 'Multnomah'; break;
case '27_polk': echo 'Polk'; break;
case '28_sherman': echo 'Sherman'; break;
case '29_tillamook': echo 'Tillamook'; break;
case '30_umatilla_morrow': echo 'Umatilla-Morrow'; break;

case '31_union': echo 'Union'; break;
case '32_wallowa': echo 'Wallowa'; break;
case '33_wasco': echo 'Wasco'; break;
case '34_washington': echo 'Washington'; break;
case '36_yamhill': echo 'Yamhill'; break;
default: echo 'Unknown'; break;
}
print ' County Farm Bureau';
print '
</td>';

Another approach I could see would be to have a table it checks against rather than a switch.

This to me is easily readable and makes sense.

So you'd say something more like:
County name = oregon.counties[$county];
WriteToOutput ("county.Name() + " County Farm Bureau");

Then …
public abstract class County {
public virtual string Name() {
return "unknown county";
}
}


And then make a class for each County as you did with cat etc.
Would it be more simple to make a table including all of the counties stats, name, number, value entry (ie 1_baker), prices for membership etc etc. And then simply pull say 'Name' from the corresponding line pulled from the table?

Thanks for bs'ing with me on this btw.
30 Mar, 2010, Skol wrote in the 18th comment:
Votes: 0
Ps. Maybe we should have this tanget split off into a new topic of like: if/else checks vs switch statements, vs chain-of-responsibilty in program design?
30 Mar, 2010, David Haley wrote in the 19th comment:
Votes: 0
You would want to encapsulate the process of translating your magic-county-IDs to the actual county names. Whether or not you would create objects for this depends on how many other things you're doing with them. If indeed the only thing you're doing is going from ID to name, then there is little point in creating objects. Personally, I would not use a switch statement but instead a map from id to name, but there is little difference – again, if that is all you're doing. If counties have more information to store, it becomes useful to treat it as a structure with several fields, so that you can refer to them all at once and easily "move around" the county.

But you would not make a class for each county. You would have counties be objects that contain an ID and a name, and then have a list of counties (or more efficiently a mapping from id to county object) that you search for a given ID. Creating a class for each county inheriting from some common abstract county would be a prime example of OOP design gone wonky, taken far too literally and pushed to a negative extreme.

It's worth noting that code that deals with user input is more likely to use switches appropriately than other sorts of code. For example, if you're taking user input and testing against Y/y/N/n, it would be silly to create some class called a YesNoUserAnswer, and from that inherit a YesUserAnswer and a NoUserAnswer, etc., and use polymorphism. (This is another example of OO design gone wonky taken far too literally where absolutely everything must be solved with objects and polymorphism, with conditionals being The One True Evil.)
30 Mar, 2010, flumpy wrote in the 20th comment:
Votes: 0
David Haley said:
It's worth noting that code that deals with user input is more likely to use switches appropriately than other sorts of code. For example, if you're taking user input and testing against Y/y/N/n, it would be silly to create some class called a YesNoUserAnswer, and from that inherit a YesUserAnswer and a NoUserAnswer, etc., and use polymorphism. (This is another example of OO design gone wonky taken far too literally where absolutely everything must be solved with objects and polymorphism, with conditionals being The One True Evil.)


Well, I kinda disagree, although not entirely. It is wonky to do what you suggest (because you are taking it to extremes), however, it is not wonky to create some kind of "CallBack" object which deals with different types of user input or performs different tasks based on user input.

Take for example the Sun JAAS CallBack objects, which I use when logging someone in:

// This is part of one of my login steps

ConfirmationCallback alreadyPlaying = new ConfirmationCallback("You are already playing. Throw the other copy out?",
ConfirmationCallback.INFORMATION, ConfirmationCallback.YES_NO_OPTION,
ConfirmationCallback.NO);
cb = createCallbackArray(alreadyPlaying); // creates an array of callbacks to execute
data.getCallbackHandler().handle(cb); // handle the callbacks by prompting the user for a response
if (alreadyPlaying.getSelectedIndex() == ConfirmationCallback.YES) {
dupe.getRegistry().dest(dupe, true);
} else {
throw new FailedLoginException("cannot log in, already logged in!");
}


ConfirmationCallbackis a generic object which prompts the user for a Yes/No answer. It is part of a larger framework, which makes it a little more complex to grok than one would first expect, but actually that complexity is worth it in the context of /not understanding the implementing application/ and therefore provides a generic solution.

You will also notice if you look at the actual ConfirmationCallBackclass, that the internal representation uses switches to perform it's task, but this task is done on a very small problem domain (a yes, no, cancel query) and can be reused.

You will often realise when you've taken things to extremes as you suggest, because (as Runter pointed out in a different thread) Things Get Too Complicated. However, that complexity (as I've said) can be useful to others, as long as most of it is hidden.
0.0/27