Sounds like he doesn't have a problem with switch statements, but rather with misusing switch statements. You could literally make that argument about anything. Wanna see why polymorphism is bad? Just look up any of the many "why OOP is bad" videos and you'll see someone using a misuse of polymorphism to explain why polymorphism is bad.
You also want to avoid if then else. The better way is to either put a drawShape in the class and override it in each subclass. So you would just call shape.drawShape() and it would print Circle or a Square, depending on the instance type (polymorphism). If you add another shape you just change one method instead of going through all switch (or if/else) statements. If you don't want to have drawShape inside the class, you can make universal visitor method and then override that method to call your visitor.draw(Circle class) or visitor.draw(Square class) and you overload each method in another class. Check visitor pattern.
I find that most discussions on "this is why this feature is bad" are mostly based on bad uses of that feature, rather than a problem with that feature itself. Bob's problem with switch statements has next to nothing to do with switch statements and everything to do with misusing switch statements. If you have a switch statement that works on just one input in a single function, the problem he describes will never arise. It's kinda ironic that he suggests the solution is polymorphism, because the internet is filled with "why OOP is bad and you shouldn't use it" videos, all built on criticising misuses of OOP.
What I find funny about this is that it effectively moves the switch statement from visible code to the function pointer table which figures out which of the many implementations of rotate you need to call for a given instance.
This is called The Expression Problem. If you use switches over types, it’s easier to add new functions. If you use polymorphism, it’s easier to add new type variants. Beyond this, though, it’s useful to ask yourself why you need polymorphism or type variants in the first place. You may be just as well off creating specialized functions as needed or something else that’s simpler.
I think the point is if you repeat the condition of a switch, use polymorphism instead. At least that's what i heard. One or two switches ain't gonna kill ya.
Well he uses an example where the switch statement is worse and polymorphism is better, but does ask what one has to do if one want to add a new method. You have to go to all the shape classes and add it there. Whereas you would only add one function with one switch statement otherwise.
Or lets say you have bunch of shapes and one operation is slow. Your CPU supports rotating multiple shapes at once. In the switch case it is a bulk operation, easy to do. In the polymorphism based solution it is hard because it only deals with one shape.
@@theevilcottonball it depends in how you implement polymorphism though. In java I use to declare an abstract class between the interface and the concrete implementations to at least be able to easily insert a default implementation for new methods (even if it's just to throw some exception).
@@theevilcottonball That is true, but after adding a new method to an interface the compiler will immediately tell you which classes you have to modify to satisfy the interface. With a switch statement you'll get no compiler assistance.
@lewke1059 wow.. thank you so, so much. I'm so glad you told me this, because I was obviously under the impression that they were exactly the same and not just related constructs.
@@dschledermann sorry, was getting tired of reading all the bad takes in these comments so I incorrectly assumed yours was intended to be a jab at match alongside switch
@@lewke1059 I can read there are different takes on it. I think the issue is that the advice given here is ambiguous, and perhaps not actually the best for all cases. I'd maintain that subclassing is often a bad idea, but you could definitely define an interface as some generalized type. I've recently ported some PHP code to Rust. Some of the polymorph types in this code have a small set of known variants. In PHP, the only reasonable choice you have is to define an interface, have some classes implement this interface, almost as he described. In Rust, you can choose either to define a trait and have some structs implement that trait, or you can have a single enum containing all the variants. When implementing a method on the single enum, you are now required to do a match for getting the specific type. Some may argue that this is "ugly" (it is, after all, similar to the switch he was complaining about in the video), but having a single enum with a single method simply beats the one interface to multiple classes in this use case. Way fewer lines of code, quicker and easier to read, and the compiler will refuse to build if I'm not covering all the cases. These are just the two languages I'm most familiar with. I'm sure that there will be similar cases in other languages with various functional or OOP features where you could do this in a number of ways. I hope it makes sense.
I think it's hardly the case that any sufficiently good programming language and software architecture will have this problem of "many switchs spread around the code", it literally never happened to me in 10 years of coding. But at the same time I found many codebases that had this problem, for me the question was not that they "should not had used switches" but rather that their uses of switch was exhaustively amateur. In the same way you can teach to make people write good code with polymorphism in the OOP way, you can teach people to write good code to use parametric polymorphism, function composition and many other constructs that simply remove the need for "spreading switches in the code" (of course, you also develop a different mindset when programming with languages that allows you to do this, which also avoids writting code that would float into this direction).
Never write a switch statement without adding a default branch and putting an assertion failure there (assuming you're not using the default branch as a catch all for some reason). Then at least you'll catch the missing switch case at run time!
It's better to catch this at compile time over runtime. Don't use default at all and the compiler can error on it. This is one of the strengths of rust. C is a warning that can be enabled.
This isn't the late 1900s, exhaustive switches are better than subclassing in most scenarios. Add a new 'shape' and you'll almost certainly have to update some factories, etc at the very least. And that's assuming a nice new homogenous shape that fits in your gloriously perfect abstraction (IRL we spend hours arguing over squares vs rectangles). If that new shape has some unique aspect? Now every shape has to accommodate that, not just the 1 that actually needs it. Then you'll find you're pulling on a thread that will unravel the whole sweater.
Said by someone who, clearly, does NOT understand polymorphism! I've been saying FOR YEARS that most engineers do not understand polymorphism, and can't help but tightly-couple everything they write. This guy, also, does not understand factories, either. A fundamental inability to understand THIS particular abstraction, but comfortable with bazillions of "patterns" I imagine. I recently encountered this exact issue when I wrote a small subsystem that was extremely loosely-coupled. It was secure, and all checks were made to ensure that the user had permissions to do the CRUD in question. The team lead, required adding a "trait" to a bunch of subsystems AND requiring SEPARATE routes for each of the classes the trait was given! It went from 4 total routes ( one for each CRUD ) to 12. Adding same pattern elsewhere, it ballooned to 72 ( so far ). The team lead produces prodigious amounts of code, but clearly cannot understand polymorphism - or interfaces, for that matter. *sigh*
@@diadetediotedio6918 I'm sorry, but I think he clearly stated why he doesn't understand it. Switch vs subclassing. Hmm. Seems obvious to me that - at the very very least - he's uncomfortable with poly-m. I cannot FATHOM needing to litter a bunch of switch statements all over my code to handle ( in this example, shapes ). I've seen some recent examples where a giant subsystem in Angular has if statements littered throughout based-upon several "special case" objects in various ways. The code was quite convoluted and when I had to dip into it, I managed to add a couple of features to some new objects WITHOUT REQUIRING specical-case if/then/else statements ( which is just a short switch statement ). Those features were now available to ALL the existing objects if they want it.
@@Jollyprez I absolutely understand polymorphism. Parametric, ad-hoc, subtype, subclass, etc. OOP with its subclassing is the most maligned one. IME, it deserves it. I'm not sure where the rest of your screed is coming from
@@adambickford8720 I reiterate - you don't like polymorphism, and I submit you don't understand its power. You just said it in your last sentence. It's ok - you're employable as most modern software engineers don't understand it, either - including my immediate tech lead, nor the tech lead on another project. I don't think the architect understands it, either, if what I see in the codebase is any indication. The upshot is that the codebase tends to be very tightly coupled, and increasingly difficult to maintain, with a profusion of abstract "patterns" replacing concrete classes and sub-classes.
Still holds true. It's all about solving a problem with the right tool. Don't solve a polymorphism-problem with switch statements. Don't solve a composition-problem with inheritance. The hard part is ofcourse to recognise what type the current problem is.
@@nonamesavalible and wouldnt want to solve a generics problem with polymorphism either. duck-typing can be a much more elegant and efficient solution over runtime polymorphism
@@nonamesavalible Depending on how literally you take it, "composition over inheritance" means you should _never_ use inheritance and _always_ use composition. There are even modern languages that don't support inheritance for that reason. And that's the first thing I thought when he said those scaling methods should all be part of the shape class. So everything that differentiates the type of shape should be in the shape class? That's gonna be a long class. And what if your code should be extensible? What if you want to support extensions that provide their own operations on shapes? You're not going to solve this with sub-classing.
@@Raspredval1337 duck typing IS runtime by definition. I'll assume you mean structural vs nominal typing: en.wikipedia.org/wiki/Duck_typing#:~:text=Duck%20typing%20is%20similar%20to,that%20is%20accessed%20during%20runtime.
it depends on the type system. Even the article says the check is SOMETIMES done at runtime IN SOME STATICALLY-TYPED languages. In practice, the check is done at compile time in most statically-typed languages
I used a switch statement today for http Request Method. Instead of creating a base class and many derived classes, each one in it's own file, that also needs an autoloader (that I don't have on a micro-project that is more of a working prototype than masterclass code). And they have to be instantiated, maybe in a switch statement! And what?!? Complexity is not an answer, wether it's putting switch cases everywhere or multiplying classes for almost nothing! More ideological, as I witnessed with people reading his books, than pragmatism. We all should be pragmatics. And accept our limits. Not ideologicals!
@@yapdogThey don't understand a thing to polymorphism. The point of Uncle Bob is that replacing polymorphism with multiple enum-based switch statements is simply bad design. But many comments believe it's just an attack against switch statements...
“Are you going to find them all?” Of course you are! The IDE is going to find them for you and it’s not in any way fragile and it does not break ever. This is programming for the 1980’s.
I thought that too, but I like the open-closed principle too. I think it really depends on the situation. I don't like rushing off to define base classes all the time.
At least in Java & Kotlin with sealed classes this point is not valid anymore. Switch statements used correctly actually provide a compile time error when you add a new shape (to stay in the xample).
If you need to store the type of shape to persistence (a database), you will have to map the type of shape to an enum of sorts. Either a string or an integer. This necessitates a switch in the code that persists and loads the shapes, probably a factory. It's a separate thing entirely, but this is where I've found that you cannot avoid switches. Unless... you add a method getShapeNumber() or the like, solely for the purpose of avoiding switching on the type of shape... Which I wouldn't do. You also can't depend on the class name and store it in the database, because that breaks if you retractor the name.
Yes! This is a problem that I never see talked about. Polymorphism is great, but then the type is dynamically selected by a user and must be stored and reloaded, there has to be a value to indicate the type. And when it's reloaded, there must be something that can take the value and create the expected object instance. That usually involves a switch statement.
@@CounterFragger Because the titles are regularly worded to imply that {fill in the blank} is a bad practice. Every piece of code has a problem use-case.
@@JohnHall Oh you want a use-case ? Don't use enum-based switches instead of polymorphism. Learn OOP. Learn patterns like Strategy. Some bad practices like this one are well-known, this is all these videos are about : the basics. When developers don't know these common sense good practices, you end up with the software I'm working on, bloated with a horrifying ten year technical debt.
Yes and no. It can be an evolutionary path. You might start out with a simple enumeration and no if's or switches used on it. Later you might add a feature and now you need a switch. Add a test first to prove the switch handles all cases. Still fine. But if this keeps going and you add more switches now might be the time to consider a class hierarchy. But don't assume that now all your problems are solved. If you did well, the important problems are now solved and you didn't introduce any major new problems.
"You might miss one" is a really bad argument against switch statements. All compilers and even most linters can tell you if you exhaustively switched on all possible values. Switch is amazing in imperative languages as it is super optimized for normal sized number of enum values. It is also used as one of the central concepts in functional programming. You are just wrong on this one Bob.
I think it depends, for enums I agree switch is good since you adding a new class will not solve anything unless all functionality around that enum is moved into the new classes which will then result in a completely different program structure and could very well be less efficient. BUT it all depends on what that enum represents, for internal use within a module I think switch is perfectly fine for performance. But I also agree with Bob on the concept of having the same switch statement spread over many different places in the code. But also, inheritance is hard to do right and you can make much more hard to solve problems than you can with switch statements as long as you make sure either to use a compiler that complains of missing ones, or make sure EVERY switch statement have an default or else case to identify the problems. Yes it will result in runtime errors but given where and what state was missing its not to hard to identify how to fix the problem. If you use inheritance and build a bad structure that suddenly does not work for some types you need to add, then you might have to rebuild it all to solve the problem. As mentioned above, composition is often a better solution but it still needs to be done right. Switch statements for all their faults are more flexible :)
Uncle Bob is only wrong for bad and lazy programmers who don't want to understand polymorphism. You don't base your code design on what your IDE is able to do. Learn strategy pattern.
@@CounterFragger "Learn strategy pattern" - As if that is some kind of authority to refer to. Yes, I have read GoF's book. Yes, I used to think it was something to strive towards but with experience I have also learned that a lot of it is not good advice.
@@CounterFragger Bob himself would also disagree with you here, he used the fact that Jetbrains IDEs can easily inline functions as an argument when he was talking to Primeagen.
@@adambickford8720 Thank you very much for your hate. Anybody that is not able to learn, like you, is a bad engineer. And you just want to justify your own behavior. *Plonk*
This seemed like such a bad take. I'm glad he arrived at the Polymorphism approach, but the first thing I thought was, "Why the hell are you checking class type in a condition like that instead of using inheritance?" Everything he said would basically be true for if-statements as well. I'm not even a big fan of switch-statements and 99% of the time prefer if-statements instead.
@@adambickford8720 Yeah exhaustive switch works fine if your language supports it. Switch and inheritance can both have the issue of missed handling of a given function. After all if you have rotate, resize, draw and so forth for a shape, it's possible in inheriting you may just forget to define a given case for your new shape and default to the base class function. And yes, in a good design paradigm, your code can be set up to force you to write certain functions for OOP, which is really the same as a language that supports exhaustive switch. The main drawback of switch is that it's a closed system. If you're writing a library and you want there to be the possibility of user defined types, you definitely want inheritance because they allow for the user to add their own.
@@taragnor Completely agree with you on having extensibility vs switches. It's a tradeoff. IME w/the typical business app, exhaustive switches and immutable structs with pure functions are generally simpler.
@@divinecomedian2 Yep. You can set that up to force OOP implementation if your language supports it, much in the same way you can use exhaustive switches (if your language supports it) to force implementation using that methodology.
Uncle Bob - the 2000s are calling and you can have languages that check exhaustiveness (Rust, F#, etc) or tooling that can handle this if you’re stuck with something like C++ or Java. Not saying you should use them everywhere, but the argument seems like a real straw man based on assumptions from the 90s.
Oh boo-hoo, the case for rotating a circle is empty in the switch statement. Soo... How does polymorphism help there? You have a variable of type Shape, instantite it with a Circle, and call "shape.rotate"... What happens? A: Nothing happens, because nothing can happen. Rotating a circle doesn't make a differene, doesn't do anything at all, any more under polymorphism than it does in a switch statement. As an example of "switch statement bad", that sucked.
And this is why most people really shouldn't listen to Bob. They're not at the point where they can grep what he's REALLY talking about and go of on wild crusades. Same as when Dijkstra made his case about GOTO's. And, what if you don't even have OOP? Not everyone has that "privilege". Oh, you write switches...
So don't even watch this video, he's not saying switch statements are bad, he's just saying enum-based switch statements are bad in OOP ! And goto is bad in every language, yeah !
@@CounterFragger Funny, i didn't see him specifically prefacing it with those premises. And yes, SOME people will infer that, but MOST will take it literally. Just like they did with "GOTO is bad", which wasn't what Dijkstra wrote, but what people presumed he meant. Oh, and on that, does every language include asm? Because GOTO's are all you have down there. You did know that right? All control structures are based around conditional jumps. Waiting for the inevitable "well, i meant all higher level languages". Which falls straight into Bob's category, ie, "What i meant wasn't what i literally said".
Why use IDE and easily update switch statements, just create new class (if you java new file for each class also) for each type in enum. So if you codebase has 100 enums with ~8 types in them - then just create 800 classes, that would be easily maintainable!1
That's not a problem with switch statements themselves, that's just bad architecture.
Agreed
Yeah, switch shouldn't substitute for polymorphism, but the title sounds like switch is bad in general
Switch statements are one of the 23 OOP Code Smells
Sounds like he doesn't have a problem with switch statements, but rather with misusing switch statements. You could literally make that argument about anything. Wanna see why polymorphism is bad? Just look up any of the many "why OOP is bad" videos and you'll see someone using a misuse of polymorphism to explain why polymorphism is bad.
@ Just saying, the video's title could be better
No, the title doesn't say that.
Who TF would design a system like that??? 🤨 In over 3 decades of professional programming I have never, EVER seen switches used so idiotically
Uncle bob would
In about a decade I've seen it in multiple codebases
Don't ever use spoons because when you try to dig an oil well with a spoon, it ends badly.
But why say "we don't like switch statements" when the same grievances you have identified apply equally to "if..then...else..." statements?
You did not get the point. If you just fixate on the headline and try to fix the headline of the lesson, you will not learn the lesson.
The difference is 'exhaustiveness'.
You also want to avoid if then else. The better way is to either put a drawShape in the class and override it in each subclass. So you would just call shape.drawShape() and it would print Circle or a Square, depending on the instance type (polymorphism).
If you add another shape you just change one method instead of going through all switch (or if/else) statements.
If you don't want to have drawShape inside the class, you can make universal visitor method and then override that method to call your visitor.draw(Circle class) or visitor.draw(Square class) and you overload each method in another class. Check visitor pattern.
“Switch Statements” are on of the 23 OOP code smells. If… else if… else… statements also count as a “Switch Statement code smell”
I find that most discussions on "this is why this feature is bad" are mostly based on bad uses of that feature, rather than a problem with that feature itself. Bob's problem with switch statements has next to nothing to do with switch statements and everything to do with misusing switch statements. If you have a switch statement that works on just one input in a single function, the problem he describes will never arise.
It's kinda ironic that he suggests the solution is polymorphism, because the internet is filled with "why OOP is bad and you shouldn't use it" videos, all built on criticising misuses of OOP.
What I find funny about this is that it effectively moves the switch statement from visible code to the function pointer table which figures out which of the many implementations of rotate you need to call for a given instance.
In this case this is not a problem, the less code you need to write and maintain the less opportunity for problems you create.
and tomorrow there will be a new video "the problem with polymorphism"
This is called The Expression Problem. If you use switches over types, it’s easier to add new functions. If you use polymorphism, it’s easier to add new type variants.
Beyond this, though, it’s useful to ask yourself why you need polymorphism or type variants in the first place. You may be just as well off creating specialized functions as needed or something else that’s simpler.
When you use the wrong tool for the problem, you don’t blame the tool. You should look closer at the problem…
So, switches are not a good replacement for inheritance and polymorphism, got it. But this is not the only case where switches can be applied.
I think the point is if you repeat the condition of a switch, use polymorphism instead. At least that's what i heard.
One or two switches ain't gonna kill ya.
Well he uses an example where the switch statement is worse and polymorphism is better, but does ask what one has to do if one want to add a new method. You have to go to all the shape classes and add it there. Whereas you would only add one function with one switch statement otherwise.
Or lets say you have bunch of shapes and one operation is slow. Your CPU supports rotating multiple shapes at once. In the switch case it is a bulk operation, easy to do. In the polymorphism based solution it is hard because it only deals with one shape.
@@theevilcottonball it depends in how you implement polymorphism though. In java I use to declare an abstract class between the interface and the concrete implementations to at least be able to easily insert a default implementation for new methods (even if it's just to throw some exception).
@@theevilcottonball That is true, but after adding a new method to an interface the compiler will immediately tell you which classes you have to modify to satisfy the interface. With a switch statement you'll get no compiler assistance.
So basically switch statements shouldn't substitute for redefining your methods in derived classes. Thanks for the pro tip there Charles Babbage.
Sure hope you never have to add new functionality to those shapes. Now every place that calls any shape function has to be examined.
Functional languages would like a word. You have match statement and if you don't cover all the cases, then the thing won't compile.
match is not switch, you're welcome
@lewke1059 wow.. thank you so, so much. I'm so glad you told me this, because I was obviously under the impression that they were exactly the same and not just related constructs.
@@dschledermann sorry, was getting tired of reading all the bad takes in these comments so I incorrectly assumed yours was intended to be a jab at match alongside switch
@@lewke1059 That's not what he said. Maybe try not being a jerk?
@@lewke1059 I can read there are different takes on it. I think the issue is that the advice given here is ambiguous, and perhaps not actually the best for all cases. I'd maintain that subclassing is often a bad idea, but you could definitely define an interface as some generalized type.
I've recently ported some PHP code to Rust. Some of the polymorph types in this code have a small set of known variants. In PHP, the only reasonable choice you have is to define an interface, have some classes implement this interface, almost as he described. In Rust, you can choose either to define a trait and have some structs implement that trait, or you can have a single enum containing all the variants. When implementing a method on the single enum, you are now required to do a match for getting the specific type. Some may argue that this is "ugly" (it is, after all, similar to the switch he was complaining about in the video), but having a single enum with a single method simply beats the one interface to multiple classes in this use case. Way fewer lines of code, quicker and easier to read, and the compiler will refuse to build if I'm not covering all the cases.
These are just the two languages I'm most familiar with. I'm sure that there will be similar cases in other languages with various functional or OOP features where you could do this in a number of ways.
I hope it makes sense.
I think it's hardly the case that any sufficiently good programming language and software architecture will have this problem of "many switchs spread around the code", it literally never happened to me in 10 years of coding. But at the same time I found many codebases that had this problem, for me the question was not that they "should not had used switches" but rather that their uses of switch was exhaustively amateur. In the same way you can teach to make people write good code with polymorphism in the OOP way, you can teach people to write good code to use parametric polymorphism, function composition and many other constructs that simply remove the need for "spreading switches in the code" (of course, you also develop a different mindset when programming with languages that allows you to do this, which also avoids writting code that would float into this direction).
Never write a switch statement without adding a default branch and putting an assertion failure there (assuming you're not using the default branch as a catch all for some reason). Then at least you'll catch the missing switch case at run time!
Just trying to fix the problems, instead fixing the cause. Bad strategy.
It's better to catch this at compile time over runtime. Don't use default at all and the compiler can error on it. This is one of the strengths of rust. C is a warning that can be enabled.
-Wswitch is the flag
This isn't the late 1900s, exhaustive switches are better than subclassing in most scenarios.
Add a new 'shape' and you'll almost certainly have to update some factories, etc at the very least. And that's assuming a nice new homogenous shape that fits in your gloriously perfect abstraction (IRL we spend hours arguing over squares vs rectangles). If that new shape has some unique aspect? Now every shape has to accommodate that, not just the 1 that actually needs it. Then you'll find you're pulling on a thread that will unravel the whole sweater.
Said by someone who, clearly, does NOT understand polymorphism!
I've been saying FOR YEARS that most engineers do not understand polymorphism, and can't help but tightly-couple everything they write. This guy, also, does not understand factories, either. A fundamental inability to understand THIS particular abstraction, but comfortable with bazillions of "patterns" I imagine.
I recently encountered this exact issue when I wrote a small subsystem that was extremely loosely-coupled. It was secure, and all checks were made to ensure that the user had permissions to do the CRUD in question. The team lead, required adding a "trait" to a bunch of subsystems AND requiring SEPARATE routes for each of the classes the trait was given!
It went from 4 total routes ( one for each CRUD ) to 12. Adding same pattern elsewhere, it ballooned to 72 ( so far ). The team lead produces prodigious amounts of code, but clearly cannot understand polymorphism - or interfaces, for that matter. *sigh*
@@Jollyprez
Please, give me a word of what is wrong with his understanding of polymorphism.
@@diadetediotedio6918 I'm sorry, but I think he clearly stated why he doesn't understand it. Switch vs subclassing. Hmm. Seems obvious to me that - at the very very least - he's uncomfortable with poly-m. I cannot FATHOM needing to litter a bunch of switch statements all over my code to handle ( in this example, shapes ).
I've seen some recent examples where a giant subsystem in Angular has if statements littered throughout based-upon several "special case" objects in various ways. The code was quite convoluted and when I had to dip into it, I managed to add a couple of features to some new objects WITHOUT REQUIRING specical-case if/then/else statements ( which is just a short switch statement ). Those features were now available to ALL the existing objects if they want it.
@@Jollyprez I absolutely understand polymorphism. Parametric, ad-hoc, subtype, subclass, etc. OOP with its subclassing is the most maligned one. IME, it deserves it.
I'm not sure where the rest of your screed is coming from
@@adambickford8720 I reiterate - you don't like polymorphism, and I submit you don't understand its power. You just said it in your last sentence. It's ok - you're employable as most modern software engineers don't understand it, either - including my immediate tech lead, nor the tech lead on another project. I don't think the architect understands it, either, if what I see in the codebase is any indication. The upshot is that the codebase tends to be very tightly coupled, and increasingly difficult to maintain, with a profusion of abstract "patterns" replacing concrete classes and sub-classes.
He is talking about a problem with OOP not switch statements.
So what about "composition over inheritance"...?
Still holds true. It's all about solving a problem with the right tool. Don't solve a polymorphism-problem with switch statements. Don't solve a composition-problem with inheritance. The hard part is ofcourse to recognise what type the current problem is.
@@nonamesavalible and wouldnt want to solve a generics problem with polymorphism either. duck-typing can be a much more elegant and efficient solution over runtime polymorphism
@@nonamesavalible Depending on how literally you take it, "composition over inheritance" means you should _never_ use inheritance and _always_ use composition. There are even modern languages that don't support inheritance for that reason.
And that's the first thing I thought when he said those scaling methods should all be part of the shape class. So everything that differentiates the type of shape should be in the shape class? That's gonna be a long class.
And what if your code should be extensible? What if you want to support extensions that provide their own operations on shapes? You're not going to solve this with sub-classing.
@@Raspredval1337 duck typing IS runtime by definition. I'll assume you mean structural vs nominal typing: en.wikipedia.org/wiki/Duck_typing#:~:text=Duck%20typing%20is%20similar%20to,that%20is%20accessed%20during%20runtime.
it depends on the type system. Even the article says the check is SOMETIMES done at runtime IN SOME STATICALLY-TYPED languages. In practice, the check is done at compile time in most statically-typed languages
I used a switch statement today for http Request Method.
Instead of creating a base class and many derived classes, each one in it's own file, that also needs an autoloader (that I don't have on a micro-project that is more of a working prototype than masterclass code). And they have to be instantiated, maybe in a switch statement!
And what?!? Complexity is not an answer, wether it's putting switch cases everywhere or multiplying classes for almost nothing!
More ideological, as I witnessed with people reading his books, than pragmatism. We all should be pragmatics. And accept our limits. Not ideologicals!
a lot of people in these comments missed the point
Enlighten us
@@yapdog read the comments that agree with the video, they already go through it
@@yapdogThey don't understand a thing to polymorphism. The point of Uncle Bob is that replacing polymorphism with multiple enum-based switch statements is simply bad design. But many comments believe it's just an attack against switch statements...
@@lewke1059 What I read was BS
“Are you going to find them all?” Of course you are! The IDE is going to find them for you and it’s not in any way fragile and it does not break ever. This is programming for the 1980’s.
I thought that too, but I like the open-closed principle too. I think it really depends on the situation. I don't like rushing off to define base classes all the time.
I don't use an IDE, so my method of finding all the switch statements is to search for one member of the enum. This works really well.
@@rossnwilliams You don’t use an IDE? Still using punchcards? 😂 no seriously, what do you use then?
Not everything is switched on an enum. And in that case you get no compiler or IDE assistance.
Yes, using switch statements is 1980's programming.
At least in Java & Kotlin with sealed classes this point is not valid anymore. Switch statements used correctly actually provide a compile time error when you add a new shape (to stay in the xample).
You make it seem that the problem is the switch statement but the issue here is the implementation.
If you need to store the type of shape to persistence (a database), you will have to map the type of shape to an enum of sorts. Either a string or an integer. This necessitates a switch in the code that persists and loads the shapes, probably a factory. It's a separate thing entirely, but this is where I've found that you cannot avoid switches.
Unless... you add a method getShapeNumber() or the like, solely for the purpose of avoiding switching on the type of shape... Which I wouldn't do. You also can't depend on the class name and store it in the database, because that breaks if you retractor the name.
Yes! This is a problem that I never see talked about. Polymorphism is great, but then the type is dynamically selected by a user and must be stored and reloaded, there has to be a value to indicate the type. And when it's reloaded, there must be something that can take the value and create the expected object instance. That usually involves a switch statement.
Clickbait as usual.
Says a developer who surely doesn't understand polymorphism and the issues when replacing it by switch statements...
@@CounterFragger Says a Sr. Architect who is tired of these just being pulls for clicks.
@@JohnHall So why are videos about coding best practices clickbait ?
@@CounterFragger Because the titles are regularly worded to imply that {fill in the blank} is a bad practice. Every piece of code has a problem use-case.
@@JohnHall Oh you want a use-case ? Don't use enum-based switches instead of polymorphism. Learn OOP. Learn patterns like Strategy.
Some bad practices like this one are well-known, this is all these videos are about : the basics. When developers don't know these common sense good practices, you end up with the software I'm working on, bloated with a horrifying ten year technical debt.
I see, the problem is code has to be ready for dumb people to work on, you know, the nephews and kids.
A good example of how not to teach software. Makes a good soundbite though
Yes and no. It can be an evolutionary path. You might start out with a simple enumeration and no if's or switches used on it. Later you might add a feature and now you need a switch. Add a test first to prove the switch handles all cases. Still fine. But if this keeps going and you add more switches now might be the time to consider a class hierarchy. But don't assume that now all your problems are solved. If you did well, the important problems are now solved and you didn't introduce any major new problems.
"You might miss one" is a really bad argument against switch statements. All compilers and even most linters can tell you if you exhaustively switched on all possible values.
Switch is amazing in imperative languages as it is super optimized for normal sized number of enum values. It is also used as one of the central concepts in functional programming. You are just wrong on this one Bob.
I think it depends, for enums I agree switch is good since you adding a new class will not solve anything unless all functionality around that enum is moved into the new classes which will then result in a completely different program structure and could very well be less efficient.
BUT it all depends on what that enum represents, for internal use within a module I think switch is perfectly fine for performance.
But I also agree with Bob on the concept of having the same switch statement spread over many different places in the code.
But also, inheritance is hard to do right and you can make much more hard to solve problems than you can with switch statements as long as you make sure either to use a compiler that complains of missing ones, or make sure EVERY switch statement have an default or else case to identify the problems.
Yes it will result in runtime errors but given where and what state was missing its not to hard to identify how to fix the problem.
If you use inheritance and build a bad structure that suddenly does not work for some types you need to add, then you might have to rebuild it all to solve the problem.
As mentioned above, composition is often a better solution but it still needs to be done right.
Switch statements for all their faults are more flexible :)
Uncle Bob is only wrong for bad and lazy programmers who don't want to understand polymorphism. You don't base your code design on what your IDE is able to do.
Learn strategy pattern.
@@CounterFragger "Learn strategy pattern" - As if that is some kind of authority to refer to. Yes, I have read GoF's book. Yes, I used to think it was something to strive towards but with experience I have also learned that a lot of it is not good advice.
You're assuming the switches are for enums. That's isn't always the case (pun intended).
@@CounterFragger
Bob himself would also disagree with you here, he used the fact that Jetbrains IDEs can easily inline functions as an argument when he was talking to Primeagen.
Good lecture. But still, even here people are hard attached to hate OOP and to hate learning new stuff. Makes me wonder about "engineers".
The OOP haters generally know OOP better, which is why they hate it. Dogmatically sticking to OOP is what makes you a bad engineer.
@@adambickford8720 Thank you very much for your hate.
Anybody that is not able to learn, like you, is a bad engineer. And you just want to justify your own behavior.
*Plonk*
@@adambickford8720No, the bad engineer is just you. OOP requires abstraction skills, which bad engineers don't have.
This seemed like such a bad take. I'm glad he arrived at the Polymorphism approach, but the first thing I thought was, "Why the hell are you checking class type in a condition like that instead of using inheritance?" Everything he said would basically be true for if-statements as well. I'm not even a big fan of switch-statements and 99% of the time prefer if-statements instead.
That's incorrect. Exhaustive switches are a completely legitimate, and arguable better, solution to the problem.
@@adambickford8720 Yeah exhaustive switch works fine if your language supports it. Switch and inheritance can both have the issue of missed handling of a given function. After all if you have rotate, resize, draw and so forth for a shape, it's possible in inheriting you may just forget to define a given case for your new shape and default to the base class function. And yes, in a good design paradigm, your code can be set up to force you to write certain functions for OOP, which is really the same as a language that supports exhaustive switch.
The main drawback of switch is that it's a closed system. If you're writing a library and you want there to be the possibility of user defined types, you definitely want inheritance because they allow for the user to add their own.
@@taragnor Completely agree with you on having extensibility vs switches. It's a tradeoff. IME w/the typical business app, exhaustive switches and immutable structs with pure functions are generally simpler.
@@taragnoryou'd inherit from an abstract class so that you'd be forced to implement the function in the child shape classes
@@divinecomedian2 Yep. You can set that up to force OOP implementation if your language supports it, much in the same way you can use exhaustive switches (if your language supports it) to force implementation using that methodology.
"this _can_ be used wrongly, so don't use it at all" Great teaching bro. no wonder why trump won when these are the teachers we have
Oh boy, trying to politicize engineering now? You sound like a GQP politician.
Do you teach the art of sophism ? Because it's one of the worst sophisms I have read in my life...
Lmao teacher bad so Trump bad. You've become what you despise.
Uncle Bob - the 2000s are calling and you can have languages that check exhaustiveness (Rust, F#, etc) or tooling that can handle this if you’re stuck with something like C++ or Java. Not saying you should use them everywhere, but the argument seems like a real straw man based on assumptions from the 90s.
Not all of us have the luxury of using those languages and tooling. It's still good advice.
@@divinecomedian2 Maybe, but only within that specific context, which is not the universal truth that this video paints.
Oh boo-hoo, the case for rotating a circle is empty in the switch statement.
Soo... How does polymorphism help there? You have a variable of type Shape, instantite it with a Circle, and call "shape.rotate"... What happens? A: Nothing happens, because nothing can happen. Rotating a circle doesn't make a differene, doesn't do anything at all, any more under polymorphism than it does in a switch statement.
As an example of "switch statement bad", that sucked.
And this is why most people really shouldn't listen to Bob. They're not at the point where they can grep what he's REALLY talking about and go of on wild crusades. Same as when Dijkstra made his case about GOTO's. And, what if you don't even have OOP? Not everyone has that "privilege". Oh, you write switches...
i mean OOP is a disease anyway, as long as you have first class functions you have a much more powerful polymorphism anyway
So don't even watch this video, he's not saying switch statements are bad, he's just saying enum-based switch statements are bad in OOP !
And goto is bad in every language, yeah !
@@CounterFragger Funny, i didn't see him specifically prefacing it with those premises. And yes, SOME people will infer that, but MOST will take it literally. Just like they did with "GOTO is bad", which wasn't what Dijkstra wrote, but what people presumed he meant.
Oh, and on that, does every language include asm? Because GOTO's are all you have down there. You did know that right? All control structures are based around conditional jumps.
Waiting for the inevitable "well, i meant all higher level languages". Which falls straight into Bob's category, ie, "What i meant wasn't what i literally said".
Why use IDE and easily update switch statements, just create new class (if you java new file for each class also) for each type in enum. So if you codebase has 100 enums with ~8 types in them - then just create 800 classes, that would be easily maintainable!1
First