your talks are very clean, consise and contain good explanations. I blogged about 'avoiding if's in OOP' a few months ago before seeing this presentations. Right on the money... many people who aren't in the industry (posting comments) may not see the point in writing code this way but believe me... there are reasons.
For those arguing that the computation graph was over engineered: suppose ValueNode represents a complex type, such as a matrix. Misko's approach separates the responsibility of each operator out of the main class. For me the main take away was just that: separate responsibility.
The problem with such blanket prescriptions, as in this talk, is that it cleverly evades interfacing with other systems where you have little control. Assume we have a polymorphic container of various shapes, circles, squares, etc. We would like to draw the shapes across a range of graphics engines. On one graphics engine, we can implement an abstract Draw(graphics.context) method with a device context passed in as a parameter. On another graphics engine, we have no access to the device context, instead we must call a specific shape draw function passing in the coordinates as parameters, for instance graphics.DrawCircle(circle.r, circle.x, circle.y). The former case avoids conditional statements, but should it be responsible for drawing itself, especially if it can do so on only one platform? The latter case leaves this responsibility to the graphics engine which must use a series of conditionals to determine how to draw the shapes. Which one do you choose?
*Here is a quick summary:* - methods with less if statements have less possible paths of execution going through them - thus they are easier to think about and easier to test - they are easier to test because if there is only 1 path of execution going through a method, you only need to test that one path of execution (i.e. one test case). If there are 4 paths of execution going through the method (due to different conditions for the if statements) you need to test all 4 of these paths, thus you need about 4 test cases - normally, an if statement inside a method does a different thing based on some condition somewhere (can be state of a member variable or a global state) - what if instead, we create a class hierarchy such that we put the different behaviors in different classes? - for example, if we have a class called Car and a method called drive(), and the drive() method will either drive the car really fast or really slow based on the value of a member variable which tells us whether the car is a sports car, or a big truck, etc. Also, consider if there is another variable called broken, and if this variable is true, the drive() method does nothing. - instead of putting a bunch of if statements in the drive() method, we can simply create a bunch of different classes with a drive() method. For example a SportsCar's drive() method will make it drive super fast where as a BrokenCar's drive() method does nothing. Of course these classes would need to inherit from a base Car class with a perhaps abstract drive() method. - ok, so we know *how* to take if statements out of our methods, but when should we do it? What are the guidelines? - switch statements in your methods are a HUGE hint that you should strongly consider polymorphism - if you have the same switch statement, or the same if structure, in several of your methods, this is a strong hint that you should consider polymophism instead - if an object behaves different based on its state, consider using polymorphism (this is called the state pattern btw) - if you have if statements (or switch statements) that do different things based on a variable called "type" or something similar, this screams polymorphism! Why are you keeping track of the type of this object via a member variable? Why not just make sub classes? *cool/helpful hints:* - you can use the Null object pattern to really reduce the number of null checks your client has to do. Normally your client calls your function, then checks to make sure he did not get null back, and if not, he calls methods on that object. Basically what he is thinking is "if the object returned by this function is not null, i want to do some work, otherwise no work". If instead of returning null we return an object whose methods do nothing when called, we achieve the same thing! The client calls your function, then starts calling methods on the object you returned. If the object you returned is a Null object, nothing happens anyways! - you want the behavior of your system to be determined by the objects and how you link them together. If you wanna change the behavior of your system, you want to link different objects together, or remove one object and insert another one in its spot. This is the type of design that you tend to get if you favor polymorphism over if/switches. *main overarching message:* As with most things in software engineering (and life in general), it is all about balance. You cannot completely remove if/switch statements, and that is not what you should aim to do. Just know that in certain cases (especially when your class consistently changes its behavior based on the value of its member variables) you should *consider* polymorphism. Just know that there is a relatively easy way to take a class whose methods are riddled with similarly structured if/switch statements and instead create a class hierarchy whose methods are nice and simple (but of course you end up with more classes). Hope that was helpful! Thanks so much for the talk, really enjoyed it!
Notice he states right away this is done more to increase testability and reduce errors. If you are repeatedly using the same if statements for the same reason then use this. But, If you just need ifs a few times, then it's not necessary or particularly desirable. By making everything smaller pieces it reduces the chance of errors from incorrectly branching, reduces the time it takes to figure out what is going on, and makes test paths and procedures clearer as well.
you all prolly dont give a shit but does someone know of a trick to get back into an Instagram account?? I was stupid forgot the password. I love any tricks you can offer me.
@Leandro Griffin thanks so much for your reply. I found the site thru google and im waiting for the hacking stuff now. I see it takes quite some time so I will reply here later when my account password hopefully is recovered.
Thanks for this lovely session. Just wanted to add a point on mathematical tree that you presented. In the given example you are assuming that all the operands will have double operands/values but sometimes we can have single operand/value operator like negation (-). So, in case of single operand operator we will have one of the child as null either left child or right child.
The 1 + 2 * 3 model is fantastic. The first node sketch is the ideal model. We then see that the simple approach (no polymorphism) results in a model that is far from ideal. It's full of waste and an engineers job isn't done until there is nothing left to take away. He should have driven home the point by showing how close the final code (using polymorphism) is so close to the ideal model by having them side by side.
i'm a complete noob... so i'm trying to learn how to organize my programs/applications into a collection of classes... and wrap my head around object oriented programming... i think this was helpful for understanding polymorphism (even to a beginner)... And it really makes me think about different ways to go about coding... The only thing i didn't understand was Guice... i'll have to look it up and learn more about it.
Only on interview. :D Actually all this solid principles works good by theory, but not in practice. Come on who will check at deadline, liskov principle is applied?
@TheKeinash And what of my initial example of a health insurance client who has a database record with many attribute fields such that to subclass all the permutations would involve ~1000 subclasses? Isn't there a practical limit to 'open/closed' design?
This subject (and much more...) is essentially a "design pattern" issue in all OOP world. A good book : "Design Patterns - Elements of Reusable Object-Oriented Software", Erich Gamma/Richard Helm/Ralph Johnson/John Vlissides (1995), you'll find all answers you need about all these basic design patterns (creational, structural and behavioural patterns). A must for any serious OOP programmer.
Very simple examples, but as Misko points out, many experienced developers are abusing if/switch statements, and making their code difficult to maintain.
For subclassing Update ... what happens when you want to extend the functionality again? How do you reuse the code from both? Extend both? Then for each flag, your number of classes grows exponentially. For the node example, you can't remove the if, just move them. And I would argue that using switch(operator){ case '*': node = new MultiplyOperator(); ... is less readable than node = new Operator(operator), and will have to be duplicated more in any code that uses Operator.
Uncle Bob's Clean Code got published right before this talk, you can see "Don't return null" part is showing in this video as "to be if free", amazing! But that's outdated since we already have Optional in Java, oh BTW this series of talk is heavily focused on JAVA instead of other languages
In your first example with the birds, could Bird be an interface with the method getSpeed rather than an abstract class? What if Norwegian etc implement the Bird interface rather than inherit from it? This would allow us to give other kinds of objects (trucks, planes) a getSpeed() operation without forcing them to inherit from an abstract Bird class. As a rule, I prefer composition over inheritance. Do you think this is a good place for composition? If not, why not?
@Hax0rPr0n You're completely missing the point of the object oriented method and in particular an OO principle called the Open Closed Principle which states that software modules should be open for extension but closed for modification. This property allows us to add capabilities to a software system by adding new modules (i.e new subclasses) without incurring the risk of changing existing code. That's the main reason to favor inheritance over conditionals, it has nothing to do with clarity.
It seems like the examples were specifically chosen so that polymorphism could work. What happens if the state changes? You can't have one subclass instance magically turn into a different subclass. Not all switch statements switch over constants.
Is there another way of dealing with the OpNode? Instead of creating subclasses of OpNode, I was thinking of creating a NumericOperator interface. This interface would have one method: calculate(double operand1, double operand2). I was then thinking that OpNode would have a NumericInterface field which would be assigned one concrete implementation of the interface. It would then be able to use calculate() with whatever value is in its child nodes. Anyone have any thoughts on this or tips for improvement?
Some of the best advice given to me back a few years ago but you actually can remove _if_ statements entirely. He mentions SmallTalk... how did you think the language did without? It borrowed the conceptual idea of predicates from the functional programming world... predicates can be provided in Java _with_ a hidden _if_ clause for the three functional map features. More specifically, a reusable function and it's part-with typing can be created to allow Java programmers to use predicates.
You can add new Node classes to your code, without having access at source code, but... you still have to modify the factory class to support new types, right? How you extend a constructor if you don t have access to source code?
I can see how using subclassing with well chosen names would make your code much cleaner. With the flags example I suppose the flags must not change during the lifespan of the object ( with the example they probably won't) , I assume you don't want to cast the object when the flag changes.
So if I have object state flags where A=2 states, B=3 states, C=7, D=15 then all I need is 630 subclasses, then when the client comes along with another 4 state flag, I just have to write 1890 MORE subclasses. I suppose I should name them Obj_A1_B3_C6_D1_E3 and so on? Then build an interpreter to transcode that name into some human readable form like - Obj_Homeowner_Canadian_Single_SalaryMoreThan40k_MetLife_Smoker?
@mindprism Along the same lines maybe we could reduce code clutter by simplifying everything into Main() and DoSomething(XML systemwidestate, String verb, int intparm, int intparm2, double dblparm....):Object
I agree with the "copious amounts of state inspection using conditionals is bad" part, but it becomes really obvious how an algebraic datatype would be so much simpler and more efficient and clean to use, rather than a complicated class hierarchy. full implementation of the problem statement in haskell: --- data Expr a where Lit :: a -> Expr a (:+:) :: Expr a -> Expr a -> Expr a (:*:) :: Expr a -> Expr a -> Expr a deriving (Show, Eq) evaluate :: (Num a) => Expr a -> a evaluate (Lit n) = n evaluate (l :+: r) = evaluate l + evaluate r evaluate (l :*: r) = evaluate l + evaluate r --- example output: evaluate (Lit 2 :+: (Lit 3 :*: Lit 2)) 7 no inheritance, no ifs, when adding new ops you get full compiler support with warnings and errors in incomplete pattern matches, everything in one single file, and everything in 9 lines of code.
So, he doesn't answer the question: How to implement ToString() method without if statements. Does anyone know how we can solve this problem? The only solution that I know is to put a precedence operator field inside operation node, and then ask if precedence of node is greater than precedence of the child node put parenthesis around child.ToString.
So, nested conditionals could be realized using polymorphism but how would multiple conditionals be expressed? Like if([conditional 1] && [conditional 2]), how would you create a class encapsulating two different states?
What would the final Node code look like ? I can see how all this is cool, but the switch statement comes because we need a condition, how do we manage that with those new classes ? Does Node even exist anymore ?
+zHqqrdz the condition has been moved up in the object graph. the conditional doesn't decide how a node should be evaluated, the only decision to be made is "what node should we create?" this clearly separates the decision of WHAT should we do and HOW do we do that, when we made the decision.
I completely agree about Polymorphism. Unfortunately, I have a better idea about a programming language's basic functionality than I do how to construct classes, the basic construct statemetns, if I can overload functions... etc. I do like Polymorphism enough to use it. "No if statements"-- what a challenge! I already love the challenge of hitting the DB as few times as possible. :3
Never return null but Null Objects: * null is your friend in test but not production code * If you want to get rid of "ifs" Replace switch and if with polymorphism
@Tiddo1000 what you should do is you create a facade (i recommend using DI also) .. consisting of every object toggled by the flags .. 1 flag is for 1 inheritance hierarchy, so if you have multiple flags, that means your class have too many responsibility .. divide it to multiple responsibility and at the last stage, create a facade to aggregate all those responsibility into 1 class ..
Watched the video. Just wondering does it mean rather than going through a if statement helll, we are having alot of sub class objects for us to execute our stuffs and testing? Will it be for each if statement, we create a subclass for it?
+dominic tay yes, that's the kludge. Rather than having an if statement with 10 elses, you have 10 classes. And the factory that creates instances of those classes has a big if statement in it... Or more often the if is replaced with a switch statement which is really just an if statement in disguise (though for long trains it is easier to read). And rather than have 10 tests for that method, one for each condition, you have 10 tests, one for each of the classes. Of course for non-trivial cases you may end up replacing 100 of those if trees with a single constructor creating the class instances, which does improve readability and testability.
I see that's very helpful. Been doin that in my code nowadays. Even though there is more classes to manage but debugging has been made more easier with each behaviour being isolated to a class by itself
dominic tay oh yes. Small, isolated pieces of functionality beat god objects any day. And don't forget not to reinvent the wheel. Had to fight to be allowed to use Apache commons stuff, the standard "we don't trust 3rd party software libraries" which was only overruled when I told them it'd take 5 minutes to include the library or 2 weeks to reimplement the bits of them we'd need to the same standards of quality...
Also, getting rid of all if statements in a program is easy with a combination of template metaprogramming and lambda calculus. That doesn't mean it's easier to read though.
@jystic That's not very OO. Objects should have specific behavior. If the behavior of the object changes on state, then it's obvious that behavior belongs in it's own object. Your suggestion leads to brittle design, which is harder to maintain, test, and extend.
* Switch Statement is always begging for polymorphism(almost a Rule) * IFs are not always... i like the way the "IFs are hidden" using the Factory pattern. Also agree with the fact that, wiring the factory, order of invocation are boring and writing business logic is fun stuff
wouldn't it be asinine to replace a structure that is less complicated in syntax, as well as more efficient in compiled operations with one that is more complicated in syntax and requires more operations in compiled code? Seems like turning a trade-off into a no-win situation. Not to mention that a decent compiler would likely reduce your loop to the same output as the if condition because they are logically equivalent.
if we think about replacing polymorphism with if-else, then the "if-else type of polymorphism" depends on the source code instead of the language itself, this talk seems under the influence of trying to make OO pure instead of finding a perfect combination of procedure and OO
I think there's really no reason to use a subclass for every op-type (depending on your language). I'd prefer to make the OpNode constructor take a function pointer/delegate to a function of type int -> int -> int (or more preferably, make the tree classes generic over types T and have the operations be arbitrary binary operations over T (T -> T -> T).
function pointers are basically a different type of polymorphism. Those functions look all the same, but have different behaviour... So it would be basically the same.
I once had the experience of a test we apply at work. My base implementation took about 10 seconds to process a 10000 records data set, which was in average good compared to another similar, OO engineered approaches. Then, a student which didnt have a lot of experience with OO tackled the test, made the thing work and delivered it. It ran the same dataset in less than 1 second.
@mindprism if you have a lot of logic inside Employee, that depends on Relationship status it is a sign that you should subclass. But it would not be logical to subclass Employee, but rather to create RelationshipStatus class and subclass it into Single, Married and so on.
@TheKeinash {With subclassing you will have more readable code.} Code is not more readable if its spread across a lot of files. I can see what he is saying, but IMHO it doesnt get you very far.
Sorry, I do not agree. Polymorphism is not just better that switch statements, just think about the SDL_Events. How would you do that polymorphic? And having a virtual render has also two problems. How do you implement a second renderer like DirectX/Opengl? Also query on visibility is not best done like that. There is even a very popular concept for that, it is called "model view controller".
To be fair, he did say that there were exceptions. to the rule. If you've got lots of if statements testing the same thing, or one big if statement with tons of code inside it, if you're not testing a primitive (for example, making sure that a given function didn't return null in order to make sure it executed properly) then polymorphism makes your code much easier to read. It all depends on how you're using them. In the case of game programming, you'd have a switch statement to determine which event is happening (if any), which would then call a method in an object where the actual code would lie. The rendering would presumably involve an abstract class, which in turn has a sub class for each individual renderer (one for DirectX and one for OpenGL in this case). The code that's common to both would be contained in the abstract class, while the code that's different would be done in the subclasses. This would also make it easier to, for example, implement Mantle once that comes out; instead of having to edit multiple if statements and switches, you create a new subclass and edit the if statement that selects which renderer is being used in the first place. Either way, the main game logic wouldn't need to change to accommodate this, because it's always calling the same methods via the abstract class.
I guess with today's hardware performance raw code speed isn't such a major issue anymore. You aren't writing for 8Mhz CPUs. Polymorphism compiles to many more steps than an if statement. Would it work for a tight loop? The dependency injection business in Java relies on reflection, which is slow. But easy to maintain code is important too, life is a tradeoff
Highly debatable quote. You are telling it like this is some kind of rule, so lets leverage it for a simple case: Does using ints instead of strings where needed is premature optimization? Because you actually can represent any basic data with string. Why use ints? It is a optimization (utilizes less memory, faster in execution etc), but is it bad? Ofcourse everybody would agree that it is not.
Now you're just trying to get your point across with non-arguments, you don't use ints instead of strings because you're optimizing for something. And ofcourse it's a debatable quote, I think that's pretty obvious. But if you're sacrificing readability because you think your code will run faster where there is no apparent need, you should reconsider.
And polymorphism?! We try to avoid vtables wherever we can! Sublassing, yes, but you shouldn't use polymorphism if concepts achieve the same. And in most cases, they do.
In defense of this talk I would say that you should use this approach in business logic. This logic is usually never needs any performance since DB is the bottleneck in this whole system. If you write your code for real time high-loaded systems then polymorphism is not a good idea, true.
You would have to change the object-type variables exactly as many times as you would anyway have to change the flag-type variables. That you are using objects makes it no worse and no better.
your talks are very clean, consise and contain good explanations. I blogged about 'avoiding if's in OOP' a few months ago before seeing this presentations. Right on the money... many people who aren't in the industry (posting comments) may not see the point in writing code this way but believe me... there are reasons.
For those arguing that the computation graph was over engineered: suppose ValueNode represents a complex type, such as a matrix. Misko's approach separates the responsibility of each operator out of the main class. For me the main take away was just that: separate responsibility.
The problem with such blanket prescriptions, as in this talk, is that it cleverly evades interfacing with other systems where you have little control. Assume we have a polymorphic container of various shapes, circles, squares, etc. We would like to draw the shapes across a range of graphics engines. On one graphics engine, we can implement an abstract Draw(graphics.context) method with a device context passed in as a parameter. On another graphics engine, we have no access to the device context, instead we must call a specific shape draw function passing in the coordinates as parameters, for instance graphics.DrawCircle(circle.r, circle.x, circle.y). The former case avoids conditional statements, but should it be responsible for drawing itself, especially if it can do so on only one platform? The latter case leaves this responsibility to the graphics engine which must use a series of conditionals to determine how to draw the shapes. Which one do you choose?
*Here is a quick summary:*
- methods with less if statements have less possible paths of execution going through them
- thus they are easier to think about and easier to test
- they are easier to test because if there is only 1 path of execution going through a method, you only need to test that one path of execution (i.e. one test case). If there are 4 paths of execution going through the method (due to different conditions for the if statements) you need to test all 4 of these paths, thus you need about 4 test cases
- normally, an if statement inside a method does a different thing based on some condition somewhere (can be state of a member variable or a global state)
- what if instead, we create a class hierarchy such that we put the different behaviors in different classes?
- for example, if we have a class called Car and a method called drive(), and the drive() method will either drive the car really fast or really slow based on the value of a member variable which tells us whether the car is a sports car, or a big truck, etc. Also, consider if there is another variable called broken, and if this variable is true, the drive() method does nothing.
- instead of putting a bunch of if statements in the drive() method, we can simply create a bunch of different classes with a drive() method. For example a SportsCar's drive() method will make it drive super fast where as a BrokenCar's drive() method does nothing. Of course these classes would need to inherit from a base Car class with a perhaps abstract drive() method.
- ok, so we know *how* to take if statements out of our methods, but when should we do it? What are the guidelines?
- switch statements in your methods are a HUGE hint that you should strongly consider polymorphism
- if you have the same switch statement, or the same if structure, in several of your methods, this is a strong hint that you should consider polymophism instead
- if an object behaves different based on its state, consider using polymorphism (this is called the state pattern btw)
- if you have if statements (or switch statements) that do different things based on a variable called "type" or something similar, this screams polymorphism! Why are you keeping track of the type of this object via a member variable? Why not just make sub classes?
*cool/helpful hints:*
- you can use the Null object pattern to really reduce the number of null checks your client has to do. Normally your client calls your function, then checks to make sure he did not get null back, and if not, he calls methods on that object. Basically what he is thinking is "if the object returned by this function is not null, i want to do some work, otherwise no work". If instead of returning null we return an object whose methods do nothing when called, we achieve the same thing! The client calls your function, then starts calling methods on the object you returned. If the object you returned is a Null object, nothing happens anyways!
- you want the behavior of your system to be determined by the objects and how you link them together. If you wanna change the behavior of your system, you want to link different objects together, or remove one object and insert another one in its spot. This is the type of design that you tend to get if you favor polymorphism over if/switches.
*main overarching message:*
As with most things in software engineering (and life in general), it is all about balance. You cannot completely remove if/switch statements, and that is not what you should aim to do. Just know that in certain cases (especially when your class consistently changes its behavior based on the value of its member variables) you should *consider* polymorphism. Just know that there is a relatively easy way to take a class whose methods are riddled with similarly structured if/switch statements and instead create a class hierarchy whose methods are nice and simple (but of course you end up with more classes).
Hope that was helpful!
Thanks so much for the talk, really enjoyed it!
Notice he states right away this is done more to increase testability and reduce errors. If you are repeatedly using the same if statements for the same reason then use this. But, If you just need ifs a few times, then it's not necessary or particularly desirable. By making everything smaller pieces it reduces the chance of errors from incorrectly branching, reduces the time it takes to figure out what is going on, and makes test paths and procedures clearer as well.
Good
you all prolly dont give a shit but does someone know of a trick to get back into an Instagram account??
I was stupid forgot the password. I love any tricks you can offer me.
@Leandro Griffin thanks so much for your reply. I found the site thru google and im waiting for the hacking stuff now.
I see it takes quite some time so I will reply here later when my account password hopefully is recovered.
@Leandro Griffin it worked and I now got access to my account again. Im so happy!
Thanks so much you saved my account !
@Rogelio Beau No problem =)
Thanks for this lovely session. Just wanted to add a point on mathematical tree that you presented. In the given example you are assuming that all the operands will have double operands/values but sometimes we can have single operand/value operator like negation (-). So, in case of single operand operator we will have one of the child as null either left child or right child.
I have seen this video for about 10 times now. I am using this as a reference.
Love to see a discussion about inheritance vs composition, in relation to this talk...
Again, nice speaker! This stuff turns on your brain to start thinking what you have been doing all of these years... :)
Great talk on why "I will not abuse conditionals in polymorphic language"
The jump was from non-structured to structured which enforces constraints that are supposed to eliminate spaghetti code.
The 1 + 2 * 3 model is fantastic. The first node sketch is the ideal model. We then see that the simple approach (no polymorphism) results in a model that is far from ideal. It's full of waste and an engineers job isn't done until there is nothing left to take away. He should have driven home the point by showing how close the final code (using polymorphism) is so close to the ideal model by having them side by side.
i'm a complete noob... so i'm trying to learn how to organize my programs/applications into a collection of classes... and wrap my head around object oriented programming... i think this was helpful for understanding polymorphism (even to a beginner)... And it really makes me think about different ways to go about coding... The only thing i didn't understand was Guice... i'll have to look it up and learn more about it.
Does anyone have an example of a large code base (Perhaps on Github) that uses these design principles?
Patriot no pe
Here's a small project you may have heard of that uses this pattern extensively: github.com/tensorflow/tensorflow
Only on interview. :D Actually all this solid principles works good by theory, but not in practice. Come on who will check at deadline, liskov principle is applied?
Also Eigen and OpenCV uses this pattern a lot
I love this guy's work.
thanks Misko, I enjoy your talks
please post more!
@TheKeinash And what of my initial example of a health insurance client who has a database record with many attribute fields such that to subclass all the permutations would involve ~1000 subclasses?
Isn't there a practical limit to 'open/closed' design?
Polymorphism can be described as an equation in Algebra I such as a+b = c. It's basically plugging in variables.
This subject (and much more...) is essentially a "design pattern" issue in all OOP world.
A good book : "Design Patterns - Elements of Reusable Object-Oriented Software", Erich Gamma/Richard Helm/Ralph Johnson/John Vlissides (1995), you'll find all answers you need about all these basic design patterns (creational, structural and behavioural patterns). A must for any serious OOP programmer.
Very simple examples, but as Misko points out, many experienced developers are abusing if/switch statements, and making their code difficult to maintain.
For subclassing Update ... what happens when you want to extend the functionality again? How do you reuse the code from both? Extend both? Then for each flag, your number of classes grows exponentially.
For the node example, you can't remove the if, just move them. And I would argue that using
switch(operator){
case '*':
node = new MultiplyOperator();
...
is less readable than node = new Operator(operator), and will have to be duplicated more in any code that uses Operator.
Another great talk from Misko Hevery.
10x
Good one Misko Hevery, Thanks.
Uncle Bob's Clean Code got published right before this talk, you can see "Don't return null" part is showing in this video as "to be if free", amazing! But that's outdated since we already have Optional in Java, oh BTW this series of talk is heavily focused on JAVA instead of other languages
Shouldn't we favour Composition over inheritance?
inheritance is easier to work with, and alot cleaner
Yes, but that doesn't mean that you never should use inheritance.
Notorious Canadian Over a simple if statement? Down that road lies... Java.
+Siddharth Gargate Indeed. Please see 35:36, and specifically 37:07 :)
Misko is the man!
you still need to check the operator and new up the appropriate subclass so, how are getting rid of if statements? am i missing something?
In your first example with the birds, could Bird be an interface with the method getSpeed rather than an abstract class? What if Norwegian etc implement the Bird interface rather than inherit from it? This would allow us to give other kinds of objects (trucks, planes) a getSpeed() operation without forcing them to inherit from an abstract Bird class.
As a rule, I prefer composition over inheritance. Do you think this is a good place for composition? If not, why not?
@Hax0rPr0n You're completely missing the point of the object oriented method and in particular an OO principle called the Open Closed Principle which states that software modules should be open for extension but closed for modification. This property allows us to add capabilities to a software system by adding new modules (i.e new subclasses) without incurring the risk of changing existing code. That's the main reason to favor inheritance over conditionals, it has nothing to do with clarity.
What about laden vs. unladen swallows? How can I effectively model that with single inheritance?
It seems like the examples were specifically chosen so that polymorphism could work. What happens if the state changes? You can't have one subclass instance magically turn into a different subclass. Not all switch statements switch over constants.
Is there another way of dealing with the OpNode? Instead of creating subclasses of OpNode, I was thinking of creating a NumericOperator interface.
This interface would have one method: calculate(double operand1, double operand2).
I was then thinking that OpNode would have a NumericInterface field which would be assigned one concrete implementation of the interface. It would then be able to use calculate() with whatever value is in its child nodes.
Anyone have any thoughts on this or tips for improvement?
Some of the best advice given to me back a few years ago but you actually can remove _if_ statements entirely. He mentions SmallTalk... how did you think the language did without? It borrowed the conceptual idea of predicates from the functional programming world... predicates can be provided in Java _with_ a hidden _if_ clause for the three functional map features. More specifically, a reusable function and it's part-with typing can be created to allow Java programmers to use predicates.
You can add new Node classes to your code, without having access at source code, but... you still have to modify the factory class to support new types, right? How you extend a constructor if you don t have access to source code?
There is a design pattern called "Visitor pattern" that addresses this issue. I learned it from Crafting Interpreters book.
@@ethernet764 thanks for reply. I will take a look
I can see how using subclassing with well chosen names would make your code much cleaner. With the flags example I suppose the flags must not change during the lifespan of the object ( with the example they probably won't) , I assume you don't want to cast the object when the flag changes.
This I learned at the seconds course of my education at the university... Why is pressures time and efforts invested in this?
@SuperiorMind I'm pretty new to OOP. Do you think you might be able to give a concrete example of this abuse/over-use of OOP?
brilliant! would've liked to see the client of the Guice code
isnt it better to use interfaces instead of extending classes? Once you extend a class, you cant extend again in Java.
debatable, great presentation
Does polymorphism affect runtime performance if you use this type of programming?
So if I have object state flags where A=2 states, B=3 states, C=7, D=15 then all I need is 630 subclasses, then when the client comes along with another 4 state flag, I just have to write 1890 MORE subclasses.
I suppose I should name them Obj_A1_B3_C6_D1_E3 and so on? Then build an interpreter to transcode that name into some human readable form like - Obj_Homeowner_Canadian_Single_SalaryMoreThan40k_MetLife_Smoker?
@mindprism Along the same lines maybe we could reduce code clutter by simplifying everything into Main() and DoSomething(XML systemwidestate, String verb, int intparm, int intparm2, double dblparm....):Object
I agree with the "copious amounts of state inspection using conditionals is bad" part, but it becomes really obvious how an algebraic datatype would be so much simpler and more efficient and clean to use, rather than a complicated class hierarchy.
full implementation of the problem statement in haskell:
---
data Expr a where
Lit :: a -> Expr a
(:+:) :: Expr a -> Expr a -> Expr a
(:*:) :: Expr a -> Expr a -> Expr a
deriving (Show, Eq)
evaluate :: (Num a) => Expr a -> a
evaluate (Lit n) = n
evaluate (l :+: r) = evaluate l + evaluate r
evaluate (l :*: r) = evaluate l + evaluate r
---
example output:
evaluate (Lit 2 :+: (Lit 3 :*: Lit 2))
7
no inheritance, no ifs, when adding new ops you get full compiler support with warnings and errors in incomplete pattern matches, everything in one single file, and everything in 9 lines of code.
Very nice talk! insightful
@mindprism You mean you have 630 'if' statements in one method?
So, he doesn't answer the question: How to implement ToString() method without if statements. Does anyone know how we can solve this problem? The only solution that I know is to put a precedence operator field inside operation node, and then ask if precedence of node is greater than precedence of the child node put parenthesis around child.ToString.
So, nested conditionals could be realized using polymorphism but how would multiple conditionals be expressed?
Like if([conditional 1] && [conditional 2]), how would you create a class encapsulating two different states?
If (a && B) = if (a) if (b)
Not sure if he is saying pragmatic or programmatic. both kinda fit most of the time.
What would the final Node code look like ? I can see how all this is cool, but the switch statement comes because we need a condition, how do we manage that with those new classes ? Does Node even exist anymore ?
+zHqqrdz the condition has been moved up in the object graph. the conditional doesn't decide how a node should be evaluated, the only decision to be made is "what node should we create?" this clearly separates the decision of WHAT should we do and HOW do we do that, when we made the decision.
Nice talk
@Tiddo1000 One way to achieve this is to use the decorator pattern to decorate your conditional with another conditional.
very good session. thanks
I completely agree about Polymorphism. Unfortunately, I have a better idea about a programming language's basic functionality than I do how to construct classes, the basic construct statemetns, if I can overload functions... etc.
I do like Polymorphism enough to use it. "No if statements"-- what a challenge! I already love the challenge of hitting the DB as few times as possible. :3
Never return null but Null Objects:
* null is your friend in test but not production code
* If you want to get rid of "ifs"
Replace switch and if with polymorphism
@Tiddo1000 what you should do is you create a facade (i recommend using DI also) .. consisting of every object toggled by the flags .. 1 flag is for 1 inheritance hierarchy, so if you have multiple flags, that means your class have too many responsibility .. divide it to multiple responsibility and at the last stage, create a facade to aggregate all those responsibility into 1 class ..
14:30 He wrote "abstract class Node" then he reversed it down a few lines of code as "class abstract OpNode extends Node". Was this intentional ?
Is this link what he refers to when said "Java Reviewer Guide" ? google.github.io/styleguide/javaguide.html
Watched the video. Just wondering does it mean rather than going through a if statement helll, we are having alot of sub class objects for us to execute our stuffs and testing? Will it be for each if statement, we create a subclass for it?
+dominic tay yes, that's the kludge. Rather than having an if statement with 10 elses, you have 10 classes. And the factory that creates instances of those classes has a big if statement in it...
Or more often the if is replaced with a switch statement which is really just an if statement in disguise (though for long trains it is easier to read).
And rather than have 10 tests for that method, one for each condition, you have 10 tests, one for each of the classes.
Of course for non-trivial cases you may end up replacing 100 of those if trees with a single constructor creating the class instances, which does improve readability and testability.
I see that's very helpful. Been doin that in my code nowadays. Even though there is more classes to manage but debugging has been made more easier with each behaviour being isolated to a class by itself
dominic tay oh yes. Small, isolated pieces of functionality beat god objects any day.
And don't forget not to reinvent the wheel. Had to fight to be allowed to use Apache commons stuff, the standard "we don't trust 3rd party software libraries" which was only overruled when I told them it'd take 5 minutes to include the library or 2 weeks to reimplement the bits of them we'd need to the same standards of quality...
Also, getting rid of all if statements in a program is easy with a combination of template metaprogramming and lambda calculus. That doesn't mean it's easier to read though.
Can I have the Bart slide?
Great video
@jystic That's not very OO. Objects should have specific behavior. If the behavior of the object changes on state, then it's obvious that behavior belongs in it's own object. Your suggestion leads to brittle design, which is harder to maintain, test, and extend.
Why is that better than an if statement?
Great talk!
Where to download the presentation ppt?
* Switch Statement is always begging for polymorphism(almost a Rule)
* IFs are not always...
i like the way the "IFs are hidden" using the Factory pattern.
Also agree with the fact that, wiring the factory, order of invocation are boring and writing business logic is fun stuff
Seems like a good opportunity to use Strategies. IStrategy: AdditionStrategy, MultiplicationStrategy, etc. Instead of doing everything.
wouldn't it be asinine to replace a structure that is less complicated in syntax, as well as more efficient in compiled operations with one that is more complicated in syntax and requires more operations in compiled code? Seems like turning a trade-off into a no-win situation. Not to mention that a decent compiler would likely reduce your loop to the same output as the if condition because they are logically equivalent.
How did Misko ever bear writing JS... no polymorphism here! Did an awesome job with he DI container though :)
Could not prototype inheritance solve the problems mentioned in the talk..
Super
Sub1.prototype = Super
Sub2.prototype = Super
if we think about replacing polymorphism with if-else, then the "if-else type of polymorphism" depends on the source code instead of the language itself, this talk seems under the influence of trying to make OO pure instead of finding a perfect combination of procedure and OO
We nicely saw: Template Method DP, Composite DP
this was refreshing, ty
The Norwegian Blue is not a swallow (7:30) but a parrot (dead parrot sketch)
I think there's really no reason to use a subclass for every op-type (depending on your language). I'd prefer to make the OpNode constructor take a function pointer/delegate to a function of type int -> int -> int (or more preferably, make the tree classes generic over types T and have the operations be arbitrary binary operations over T (T -> T -> T).
function pointers are basically a different type of polymorphism. Those functions look all the same, but have different behaviour...
So it would be basically the same.
I'm only 11 minutes in and my mind is blown.
I am very good at writing obfuscated code, though it is not my intention :)
the question around 10 minutes is a typical implementation of the interpreter pattern...
Change the object that represents that state, in the same way that you would change the flag that represent that state.
this is the exact meaning of over-engineering. i've one principle, "do it when you REALLY need it, and probably you don't"
I once had the experience of a test we apply at work. My base implementation took about 10 seconds to process a 10000 records data set, which was in average good compared to another similar, OO engineered approaches.
Then, a student which didnt have a lot of experience with OO tackled the test, made the thing work and delivered it. It ran the same dataset in less than 1 second.
how did that student do it in less than 1 second? Sorry that I am asking this question after 10 years.
@Tiddo1000 In some languages you have multiple inheritance (python), or mixins (ruby). If not, you're a bit out of luck in that case.
what is MPE?
Null Pointer Exception
superb thought to avoid NPE
With polymorphic way, it becomes easier to test.
@mindprism if you have a lot of logic inside Employee, that depends on Relationship status it is a sign that you should subclass. But it would not be logical to subclass Employee, but rather to create RelationshipStatus class and subclass it into Single, Married and so on.
@TheKeinash {With subclassing you will have more readable code.}
Code is not more readable if its spread across a lot of files. I can see what he is saying, but IMHO it doesnt get you very far.
Thanks, Very relevant.
No if statements but inline ifs are still allowed? makes no sense
Sorry, I do not agree. Polymorphism is not just better that switch statements, just think about the SDL_Events. How would you do that polymorphic? And having a virtual render has also two problems. How do you implement a second renderer like DirectX/Opengl? Also query on visibility is not best done like that. There is even a very popular concept for that, it is called "model view controller".
To be fair, he did say that there were exceptions. to the rule. If you've got lots of if statements testing the same thing, or one big if statement with tons of code inside it, if you're not testing a primitive (for example, making sure that a given function didn't return null in order to make sure it executed properly) then polymorphism makes your code much easier to read. It all depends on how you're using them. In the case of game programming, you'd have a switch statement to determine which event is happening (if any), which would then call a method in an object where the actual code would lie.
The rendering would presumably involve an abstract class, which in turn has a sub class for each individual renderer (one for DirectX and one for OpenGL in this case). The code that's common to both would be contained in the abstract class, while the code that's different would be done in the subclasses. This would also make it easier to, for example, implement Mantle once that comes out; instead of having to edit multiple if statements and switches, you create a new subclass and edit the if statement that selects which renderer is being used in the first place. Either way, the main game logic wouldn't need to change to accommodate this, because it's always calling the same methods via the abstract class.
Sounds like there's something wrong with the language you're using.
Miško Hevery is like the Richard Dawkins of code. Refusing to confuse pragmatism with laziness :)
re expression modeling interview question: bonus points for visitor pattern :P
I guess with today's hardware performance raw code speed isn't such a major issue anymore. You aren't writing for 8Mhz CPUs. Polymorphism compiles to many more steps than an if statement. Would it work for a tight loop? The dependency injection business in Java relies on reflection, which is slow. But easy to maintain code is important too, life is a tradeoff
+alb12345672 premature optimalization is the root of all evil! :)
Have you ever optimized anything in your life?
Highly debatable quote. You are telling it like this is some kind of rule, so lets leverage it for a simple case: Does using ints instead of strings where needed is premature optimization? Because you actually can represent any basic data with string. Why use ints? It is a optimization (utilizes less memory, faster in execution etc), but is it bad? Ofcourse everybody would agree that it is not.
Now you're just trying to get your point across with non-arguments, you don't use ints instead of strings because you're optimizing for something. And ofcourse it's a debatable quote, I think that's pretty obvious. But if you're sacrificing readability because you think your code will run faster where there is no apparent need, you should reconsider.
***** thats not what the quote says.
Speaker works at Google!! No doubt
And polymorphism?! We try to avoid vtables wherever we can! Sublassing, yes, but you shouldn't use polymorphism if concepts achieve the same. And in most cases, they do.
I agree with jsmartin00, these should be available on a different channel so I can subscribe!
In defense of this talk I would say that you should use this approach in business logic. This logic is usually never needs any performance since DB is the bottleneck in this whole system. If you write your code for real time high-loaded systems then polymorphism is not a good idea, true.
That was realy realy realy good advice, good job
32:38 Our code is full of this kind of replicated crud. We're eradicating it as we can using these methods.
You would have to change the object-type variables exactly as many times as you would anyway have to change the flag-type variables. That you are using objects makes it no worse and no better.