Null Object Pattern – Design Patterns (ep 18)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome back to another video in this series about design patterns for object-oriented languages in this video we are going to talk about the null object pattern so just a quick reminder the keyword null or the value null is what we use to represent nothingness as in let's say that we declare that we have a string called name and if we don't say equals foo for example well if we do say equals foo for example then then the value that the name refers to or the string that is contained within the word name within the variable name here is food but if we didn't do this if we simply just declared the existence of the variable this would then in many languages mean that name equals equals know in other words is no and just as a reminder again there are in other languages we have for example the keyword nil which so in Ruby for example and that is effectively the same thing as no so the notion of null is the notion of not having a value the the notion of nothingness as an interesting side note if you're not already familiar with that the inventor of the idea of null is Tony Hoare a super famous computer scientist and he he has apparently apologized for inventing the null and has called it his billion dollar mistake so essentially the point is that null is tricky nothingness is tricky because whenever we have something which is knowable like let's say a string is knowable meaning it can either be the value of the thing that we put inside the string it can either be a string or it can be null so it can either not be a string or it can be a string which is not the same thing as saying that it's a string which is why for example some languages separate the notion of an int from unknowable a hint so if you say that it's an int it's definitely definitely a number you can't have null but if it's an honorable and it's either no or it's an int but if you haven't been thinking about why NOLA would be dangerous before you might now be thinking but why would it be dangerous to use null null is super powerful because sometimes I want to specify that something is nothing or sometimes you want to have a special value that you want to be able to check for so that you can terminate a loop or whatever it is you're doing but essentially the key problem here lies in the fact that we said or before so whenever we introduce nulls we either have a thing or we don't which means that there are necessarily two branches through the program if we are supposed to do anything useful with the contents of that variable so if something can be null that means there is one path through the program where we do something with it if it's not null and there is another path where we do something else if it is no presumably because I mean maybe in some scenarios you would treat them exactly the same but no that doesn't do a whole lot so you can't like add to normal for example but then again specifically what you can do with the null depends a lot on which language you're in so maybe in some languages you can actually concatenate with null because null would then probably be the same thing as empty string but either way the problem is conditionals the problem is branching the problem is that as soon as something is knowable we have to check whether it's no lor not and you're probably painfully familiar with this just think about every time you're writing a method and then you accept an argument and that argument is potentially notable and then you realize oh my god what if somebody sends null through this parameter okay and then you start off your method with just checking whether every value is null or not and then doing different things depending on whether it is or whether it's not but enter the null object pattern why do we need a thing such as the null object pattern well it addresses exactly this problem so in object-oriented programming I would say that one of the key things that we do is that we replace conditionals with polymorphism one of the major reasons for why we use objects is that we use objects because it gives us polymorphism it allows us to say that I have this thing which is of some general type but I'm not sure exactly what what specific concrete type it is but but if you treat it as something of this parent type then you can dispatch method calls you can send the messages to it and be sure that it somehow will respond because if it is of this parent type or behaves as this parent type it necessarily needs to respond to those methods so if we say that we have an animal class that is then inherited by or implemented by let's say that this is an interface and we have two implementers of this interface one which is called cat and one which is called dog and if the animal class or sorry if the animal interface specifies that the notion of being an animal or the notion of having animal Ness entails having a speak method that means that we can count on cats having a speak method and dogs having a speak method this is the whole point of object-oriented programming this polymorphism and this of course means then that we can design a method let's say that returns a string and it's called speak and take something of type animal let me call it a and I'll open up this method and what we do is that we maybe returned a thoughts speak and we concatenate that with an exclamation point I mean it's totally useless method my point is just that if we accept something of type animal rather than of type cat or of tight dog that means that of course we can we can send cats to this method and it works fine and we can send dogs to this method and it works fine as long as the interface specifies that animals need to speak method now this you are probably intimately familiar with so why am I saying this well the point is this if you think about what's happening here what's happening is that we are replacing some kind of conditional with polymorphism so instead of saying instead of in this method accepting this animal and keeping maybe a constant in the animal so let's say we didn't have the cat subclass I mean didn't have the dog subclass maybe we just have animal and then we have some kind of enum or or a constant or some some some string inside of here that specifies whether it is a cat or a dog and then here in the speak method we would say well okay if type equals can't then meow and if else--if type equals dog then move man so forth the whole point of having polymorphism is that we can avoid that we replace this conditional with polymorphism and the supergenius thing is that we can do the same thing when we have nulls so what we can do is that we could say well actually there might be cats there might be dogs let me actually draw it here instead and there may be no animals so Kant implements the interface animal dog implements the interface animal and no animal implements the interface animal and this sounds completely bonkers but that's because our scenario is completely hypothetical so we need to look at something a bit more concrete but the point is then that we would then supply an implementation within no animal for all of the methods that are required by the interface that it that it implements so if it implements the interface animal then uh no animal will implement all of these methods but it will simply do quotation marks nothing so in some sense yes in some sense this is just a normal old inheritance normal old interface implementation just normal polymorphism but I think it's very useful to have this word null object pattern because it reminds us that whenever we see a null there's a high likelihood that we can replace that null with no object there's a high chance that we can say okay wait now I'm talking about let's say an iterator or a strategy or let's say a flying strategy I think we actually talked about this a bit in the strategy pattern video in the first video in this series so if you have a flying strategy or a flying behavior as a sort of parent class or as an interface and then you have multiple implementers of that interface so flying with jet rockets or flying with wings then it's completely logical it's completely sensible to also have a no flying behavior you could say that maybe some set of different things in my application can be an down with a flying behavior but some of them will be endowed with a no flying behavior even if just temporarily so let's talk about a few examples let's start in this end let me just remove all of this stuff so let's say that we are building a game first example but saying that we've got this side-scroller of this dude running super fast and then there are sort of obstacles on the way and then we can move in in these different directions so we can move left I mean we can move right and we can move down and we can maybe jump so down what maybe then beat the Crouch so let's say these are connected these different arrows are these different directions these are hooked up to the arrow keys let's say so whenever we press the up arrow key the player jumps and whenever we press the right arrow key the player moves right and then left and then down and so forth so here's the thing there are multiple different ways we could think about this but maybe we could think about it this way maybe this is a moving behavior moving behavior moving behavior and this moving behavior essentially then is a class that implements some interface which is more general but if we only think about a concrete class maybe it then has a few public methods that are called for example on the right on the left on jump and on crouch and so forth or let's say actually on up and on down and then we have implementations for these precisely let's call this let's make this the interface I said this was the concrete class but let's say that this is an interface so there's an interface called moving behavior we open it up and these are the things that we need and these are all let's say void methods void void void and void and they take no arguments and then that's the interface that's it right and then we can have concretions of this one of them would for example than being normal moving behavior so maybe the moving behavior for own right changes the coordinates of the player slightly to the right and on left changes the coordinates of the player slightly to the left and then on up supplies force to the players so that it jumps and that is pulled down by gravity for example but what if then imagine that you have like power-ups in this you have power-ups in this game and then some of the power-ups are good and some of the power-ups are bad and then the player touches one of these power-ups and it turns out that it's a bad powerup and that maybe forces the player to freeze or makes the player freeze so in other words suddenly we can't use the arrow keys so whatever the whatever position the player is in maybe it freezes and and the world just keeps on scrolling but the player is freezed in that position so that would maybe be a no moving behavior right and suddenly you can start to see wait actually there are a lot of places where we could use this and sorry of course now I call it a no moving behaviour instead of an OLE moving behavior but you can see the parallel no no and here it and here the no makes sense because we're essentially saying you can't move the player can't move and I'm not sure entirely what the null would be in this scenario it's possible that if we tried to build some other solution we would end up with an old but it was not but it's not apparent to me where the null would reside if we didn't use this no moving behavior solution using this sort of no moving behavior strategy that we put inside the player or depending on how you think about it if you watch the video on state pattern you could think of this as a no moving state and then we could have a normal moving state or a powered-up moving state so the player can be in either of these states and on right on left on up on down has different implementations depending on which of these states we are in but crucially what we're talking about now is that it's completely allowed it's completely sensible in many scenarios to have a knowing state a state that says what this state actually does nothing because we want to avoid to do this null checking so we want to be able to inject something that behaves just as all of the other things behave but that simply does nothing or does sort of the default thing let's talk about a different example where the null is more obvious I just realized that the null that we would probably check for in the example that we just talked about is probably the existence or lack of existence in other words the absence of a moving behavior so we would probably say if you have a moving behavior execute methods on the moving behavior else don't move so we would remove the moving behavior but then again I mean it's kind of contrived because if if we realize that we can use moving behaviors and we would probably realize that we can also implement the no moving behavior but here's the less trivial example if you're new to this series let me just mention that what we do in this series though is that we talk about design patterns and I tend to recommend two great books this one which is called design patterns elements of reusable object-oriented software and this one which is called headfirst design patterns now this is a more sort of pedagogical book super-good if you are new to design patterns and this is the classic book that sort of put design pattern is for object-oriented software on the map or for programming on the map either way both of these books are great so if you get both of them are one of them I'm sure you won't regret your decision anyway let's move on the reason I show this now is that none of these books actually mention the null object pattern as a full-fledged pattern of its own in their chapters they talk about different patterns however this book design patterns it does mention something called a null iterator and essentially it's exactly the same idea as the null object pattern but it's not applied widely it's just in the context of an iterator and first design patterns builds on this idea and also talks about null null iterator but also talks about a no command used in command pattern and also mentions that some people actually consider this a full-fledged design pattern I don't really see any reason why we should not consider the null object pattern a fully fledged design pattern so let's consider it anyways let's talk about the example of having a null iterator so if you watch the video on iterator pattern you know that what the iterator pattern allows us to do is that it allows us to extract an iterator from an aggregate from an iterable so that we can iterate over the structure using the iterator so we have an iterable let me call it collection and then I say that we get that from somewhere so we have some kind of collection and then we can say iterate or let me call it lower case iterate or equals collection don't get iterator so that gets us an iterator specific for not collect that we can use to iterate through this collection and the reason we might want to have a null iterator is that consider this consider that you have a tree structure so now so now think about the composite pattern if you watch the composite pattern video so now we're really going all over the place with all of the different patterns but this also goes to show that null objects are extremely useful in many many different scenarios but if you have a composite using for example the composite pattern so you have this kind of tree structure then you know that the leaves the ones in the bottom let me change color the leaves here in the bottom they are fundamentally different from these other ones these nodes these branches or composite nodes because these red ones these non leaves these they all have children so this is known has two children this node has two children this is noticed in children this node has two children and so forth however these green nodes in the bottom they are leaves so they don't have any children so the reason I'm bringing up both of these at the same time is that if you have a composite you can use iterator pattern to iterate through the composite we didn't really talk about this and I'm not gonna dig into it in depth but just try to think about it intuitively anything can return an iterator if anything that is iterable can return an iterator so let's say that we don't just say that all of these things are components and think about the composite pattern we said that the red ones are composites and the green ones are leaves but both composites and leaves or components and that's how we can treat them uniformly or treat them as if they were the same but let's say that both of these don't just implement the interface component they also implement the interface iterable so we can ask any of these we can say okay this node don't get iterator so using recursion using this composite structure we can ask the outermost note the root node we can ask that for its iterator and it will somehow ask this for its iterator and this for its iterator and they will in turn ask their children for their iterators and they will in turn ask their children for their iterators when they are iterating over when we are iterating over the structure so if you think about it what will happen when we ask this route composite for its iterator well it will return an iterator that is a composite iterator presumably these green ones the leaves would return a leaf iterator but we'll get to that so the composite here the route composite because it's a composite would return a composite iterator and a composite iterator is not super trivial because what it needs to do is that it can't just ask this composite for its left child and it's right child or essentially all of its children what it has to do is that it has to dig downwards so it has to yes ask for its left child but then if we're going in this particular order right as we talked about before there are multiple orders of traversing a tree or multiple ways of traversing a tree but if we go in this particular order it has a yes return it's let's say left child or first child here but then also ask for the iterator for for that component so it has to ask that component for for its iterator by calling get iterator and then if we dig down in the same direction what that iterator has to do what that iterator that now traverses this composite so you can sort of view it as as that this composite node now is the root node so we're starting from here and then it has to do the same thing this iterator so it has to say well okay I'll first give you reference to my left child but then I'll also initiate my iterator or sorry not my iterator the the iterator of this composite will initiate the iterator for this this child so we will call get iterator on that and then that iterator will do the same thing so again it's as if we were starting from here so we have an iterator that's traversing this structure essentially only as if this was the root and then that would simply return these two children but if we wanted that kind of logic where it only returns these two children then we somehow need to have an if somewhere we need to have an if that's if what I'm referencing is a composite in other words this iterator that that starts from this composite when it looks at this node then it looks at this node it has to say is that node a composite if so then return its iterator if not then simply do nothing but continue to but continue to the next because when we're down here we have to return something if we've called get iterator on this thing if we called get iterator here then that thing needs to respond with some kind of iterator and then Tata is is where we get the no iterator or maybe sorry maybe this we should call a null iterator and this no iterator the only thing that does is that if you think back to the iterator pattern video we had methods such as has next or is done that determine whether the iteration should continue or not well the null iterator simply always returns false if the question is has next or always returns true if the question is is done because we have nothing to return because this is not the composite structure so this value has already been returned by this iterator one level above it has already been returned by this composite iterator so this since it is not a composite it simply doesn't have to return anything so the iterator responds with well I don't have anything to iterate over I am done or I have no next element and this is kind of ingenious because if you think about it when we use composite pattern we avoided if statements we avoided switching we avoided branching by simply saying that a composite is fundamentally different from a leaf but we say that both are components which means that we can treat them uniformly so in some sense from the outside we don't know whether we have a composite or whether we have a leaf but whenever we happen to call a method upon a composite or a leave because of polymorphism we are able to do different things without having to introduce an if statement which is really powerful which then means that when we call get iterator when we when these things implement a get iterator method you can think of that as a factory method then these can produce different iterators both the let's say we didn't write that but let's say that we have a let's call it the null iterator and let's say that we have a composite iterator and a null iterator so the null iterator would be returned by leaves and the composite iterator would be returned by composites and suddenly our iterators don't need if statements either because they are using polymorphism so that's one another example of how we can use the null object pattern let's look at one final example let me remove this stuff and this example is from the command pattern so think back to the command pattern video if you don't really remember the command pattern is a way of loading metaphorically loading buttons with commands so let's say that we have this dialog box with an X button here some kind of button here and some kind of button here and some text here so this button maybe says ok and this says cancel purely hypothetically you could probably come up with better examples for this but what we then do with command pattern is that we load these buttons with commands so maybe we would look we would load this okay with an accept command and we would load this cancel with a cancel command and then both of these would have something like an execute method because both of these are commands so we have an interface which is called command and how we could use a null object pattern is by having a no command command so maybe for some dialog boxes we don't need the cancel button but for some absurd reason it's difficult for us to remove the cancel button so we can then simply know the cancel button with a no command now clearly if we end up in this situation maybe we should think about re-architecture in different pieces of the application so we don't end up in this situation but I'm just taking a sort of hypothetical example just to emphasize that command pattern is also very susceptible to the idea of null objects and it's very natural to have a no command command but you could think of it this way maybe for some reason we need to disable the cancel button and instead of then having a switch where we check whether disabled or not disabled we simply use something like a no command that we can load into this button to make it disabled but again back to state pattern maybe this is more like state patterns we would say that this button is in a non pressable state so it is in a disabled state because that would probably give us the possibility to also render this disabled Ness of the button or essentially render this in so that the user can visually see that it's non clickable and hopefully you can see how again the the null object pattern is actually really powerful because it gives us the ability to handle nothingness in the same way that we handle something else so one of the powers of command pattern is for example that we can do this sort of chaining where you can apply you can stack sort of a command after a command after a command and then execute them but then you keep something like a stack or a list of them and then you pop them pop them out in the reverse order which means that you can undo your way back to some particular state so the example I like to bring up all the time is if we think about applications visual applications for imaging processing applications such as Adobe Photoshop for example then maybe this is a blurring filter and then another blurring filter and then a sharpening filter I don't know why you would blur and then sharpen but you see my point so you apply let's say command number one command number two command number three and command number four and then you have changed your if it's an image for example that you're working on you've changed the image from this to let's say this and maybe this happened whatever I mean and the point is that you've now effectively moved to a new stage you now have a new picture you've computed a new state but using command pattern you can trace your way back you can undo all the commands backwards back to this original picture so every action has an has an inverse of itself so you have so every action has a reverse or has an unexcused every du has an undo and the reason I'm bringing up that again more about that in the command video but the reason I'm bringing up that is that if you then use no commands then you can stack no commands here if for some reason you need something which is knowable somewhere along this chain so let's say here or even maybe this three instead of these three being a three it's it's a no command so that means that you don't have to think about when you're when you're traversing this list or when you're popping off of this stack you don't have to think about whether a particular command turn out to be null or whether it's or like you don't have to ask yourself the question do I have a command or don't I have a command you know that you always have a command because if you don't you would use the no command command and that means that whenever you stick your no command into this stack of commands you can pop it off safely and just treat it as a command because it will definitely have the execute and the unexcused methods or the do and the undo methods and the same goes for when we use it in state pattern or when we use it in iterator pattern or in completely other contexts so that's all I wanted to say about the null object pattern according to Wikipedia the null object pattern was originally introduced in the book pattern languages of program design I haven't read that so I can't say anything about that book but I'll link that book in the description if you want to check it out but that's it for this video if you have any questions or comments or statements that you want to make do shoot them in the comments also if you have other examples of when the null object pattern is useful please do absolutely shoot down in the comments it'd be really interesting to hear and if you want to venture down into the rabbit hole ask yourself this question is it not possible to replace every conditional with polymorphism and isn't it possible to replace every instance of a null with a null object now I don't think that that statement is true and I think I have good reasons for not believing that it's true but either way I would say that it's a really interesting idea to think about because if we really think about it in many of the scenarios where we use nulls or conditionals we could actually buy we architectural replace them with polymorphism there are a lot of nulls that we can get rid of by instead using the null object pattern and the power of polymorphism so that's it thank you super much for watching and I will see you in the next wait also there is also this great talk by sanding myths called nothing is something if you want to know more about the null object pattern definitely check it out but now I'll see you in the next one
Info
Channel: Christopher Okhravi
Views: 41,590
Rating: 4.9326839 out of 5
Keywords: null object pattern, null object, design pattern, pattern, sandi metz, nothing is something, null, nil, design patterns, head first design patterns, design patterns elements of reusable object oriented software
Id: rQ7BzfRz7OY
Channel Id: undefined
Length: 28min 46sec (1726 seconds)
Published: Sun Dec 24 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.