 Hi, welcome to Visual Studio Toolbox. I'm your host, Robert Green, and with me is Phil Jepixi. Hey, Robert. How are you? Good. This is the last of our Design Patterns series. Well, the last time we were recording in this marathon. Yes, we've been in the studio yesterday and today. This is our ninth episode over the two days. By the time we publish this, who knows, there may be more. There are plenty of design patterns out there we haven't covered, so we'll go back to the drawing board. If we get a lot of requests for a specific pattern in the show notes in the comments, then we'll come back and do more. So we're going to cover the decorator pattern today. Correct. All right. What is that? So let's go back to Wikipedia for the definition. And the decorator pattern, according to the official definition, allows behavior to be added to an object without affecting the behavior of other objects of the same class. OK, in a lot of words, really the one I like better is provides an alternative to subclassing for extending functionality. So let's talk about Starbucks. OK. I typically only get one thing when I get to Starbucks, but I know that they have probably 2,000 different variations between the syrups and the size and the extra shots and soy milk versus regular milk. Everything you ask for is another attribute of that cup of coffee. It's another cost. So what you would hopefully not do is have a new class for every single possible permutation of a cup of coffee. Right. The other terrible end to that spectrum is a really big if statement, which I've seen. But if we want to be object-oriented, then we might say, well, we should create a coffee class and a coffee with creamer class and a coffee with an extra shot of espresso class. And a class with two shots of espresso and creamer and espresso and two shots of espresso and a particular temperature. And if you think about all the permutations. And we have just imploded on ourselves. Yes. We'll never be able to support this code and probably never get it shipped. Right. Or you could create a class with 2,000 properties. Most you never get used. Of which in any given cup of coffee, you might be using two or three of them. That seems kind of wasteful. And then you still have to have a big if statement to figure out how much it costs. Right. Right. So a for loop to loop through every property. Yeah. And how do you know what the property names are? And then I'm using reflection to go through the class to find out what the property name is. It's got to be a better way. There is a better way. But before we get into the better way, let me talk about a real life example where this would have saved me thousands of lines of code. So I did a project on JavaScript in the early days, like right before JavaScript was totally cool. And it was a configurator for a high end bicycle shop. So these are Tour de France style bicycles, right? Not the I'm going to go right around Seattle bicycles. And this is all in JavaScript, it was all client side. And base, for example, based on what frame you picked, you had to get wheels. But the wheels were limited to, you know, based on the frame and then based on what wheels you picked, there were brakes and brake shoes and all the parts of a bike. I mean, they literally were building these bikes from scratch. Yes. Because how I wrote that. A ginormous if statement. Started at the top. If they selected this, then show these options. And then continued on down the road. And my name's still on that code. I've been trying unsuccessfully to have my name removed from the code, so I don't want to take blame for it. But it was before I knew the decorator pattern. And this is, you know, many, many years ago. And once I started doing some really deep research into patterns, I saw a decorator, I went, oh, I'm going to save myself so much time by building a configurator. So let's talk about how the pattern works. So just on every episode, I'll say it again, just in case you're jumping in now, I tend not to use real life examples in my sample code. I just want to use silly things so that we talk about the pattern and not get hung up on banking or insurance or things like that. So I was watching Speed Racer, the movie, which wasn't as good as I remembered the show when I was a young kid. But they had different types of cars. Yes. They had cars that focused on attack, cars that focused on defense. Of course, Speed Racer, the number eight was number eight, wasn't it? I think it was number eight. It was the fastest car around, right? So I started thinking, how would I, I don't remember. Well, I totally messed that up, didn't I? Trying to bring in some pop culture and I screwed up. So I started thinking about how would I program those set of cars? Well, I could certainly make a base car. I could make a race car. I could make an armored car. And I could make an attack car. So I've got four cars. And they're all cars. And they're all cars. What if I wanted to put guns on something with armor? Now I have a fifth car. I have the armored attack car. Now I want to soup it up. I have the armored attack race car. Now I have six classes, right? Yeah. You see how this gets exponentially worse. Very, very quickly. What we want to do is we want to decorate the car with attributes. Like you said, we could have a whole bunch of properties and start throwing values in there. But then we're losing that single responsibility. I want the decorator for the armor, for example, to know what that does to a car. I don't want the base car to know what the armor does. Because what if it changes? So we're going to imagine an onion where we have our car at the center. And we're going to decorate that with armor. Decorate that with guns. Decorate it with a faster engine. And if we so desire, decorate it again with armor. So what we're talking about is basically building up this onion with decorators. So how does a decorator look? Well, let's talk about the iCar first. It just has three properties or three methods. Drive, attack, and defend. It's making an attack car. So notice an attack car also derives from iCar. Yep, because it's a car. Because it's a car. It gets passed into a constructor in iCar. So this is a perfect example of why we want to program to an interface. Because we don't know what kind of car it is. We just know it's a car. If I was programming to an attack car, then I could only pass in an attack car or an armored car. So we've opened up the possibilities by just having the interface there. The attack car knows that the drive, the speed, gets diminished a little bit. So it doesn't matter what the value was. Technically, these parts should be properties. But whatever, again, it's demo code. The attack vector goes up significantly and the defend drops. So I've encapsulated the entire logic of what an attack decorator does in the attack decorator. So nothing outside of the attack decorator needs to know. If I change it later and say, we are able to have lighter weight guns for the next race across the desert, then maybe the drive doesn't go down. And I only have to change it in one place. I've encapsulated that logic. So let's say we want to put armor around that. We then pass that decorated car, just an iCar, into the armored car decorator. And it then modifies the attributes. And then if you defend it again, you could pass that in again. And it would decrement the drive and increment the defend again. So I could have as many combinations of armor and guns and speed with only having those four classes. Let's say we come up with something new, the flying car. It has a jet pack and wings or something. Well, instead of having to now look at each of these individual cars and say, well, what would flying do to it? We just make a fly decorator. And we figure out how that affects the attributes of this car. And we're done. We've added one class and we've added a basically n factorial number of options. So in this example, each of the cars implements iCar, then has the same methods and properties. And you're incrementing or decrementing the drive, the attack, the defend. So couldn't you just change properties? You can. The problem would change in just changing the properties. And we'll look at the coffee example next, because I think everybody watching this shows probably a heavy coffee drinker anyway. Excuse me. Well, to get through the series, there's probably been a lot of coffee drunk. Or a lot of drunk coffee. Pardon me. All right. So I could just change the properties outside of this decorator. But then the problem is I lose the encapsulation. Every time I want to add armor to a car, I am then having to remember what that does to the properties. If I would then want to go, remember a simple factory? If I want to go and add a new type of car, now I have to go to every place in the code where I've updated the car's properties to make sure that I'm taking into account that new type of car. So this is all about encapsulation, but also extending a class while modifying it. OK. All right. So let's look at coffee. Everybody understands coffee. So we have cost and we have ingredients. So we'll start with the base coffee having a cost of $1.50. Any ingredients is just coffee. So here we're going to get around the issue of having a single class with thousands of properties or thousands of classes, which are all coffees, but they vary in various ways. Yes. OK? So here we have a list of ingredients and the cost. Sit that in the constructor. Now we're going to add a shot of espresso. OK. So we have this espresso shot decorator. Or decorator. Or decorator. You know what? To the magic of TV, we can fix that just like that. There we go. And we have our constructor, which takes an iCoffee. Yep. We increment the cost by $0.55. And we get all of the ingredients from the currently decorated car. Sorry. So on the car mode. Coffee. And then add espresso to it. OK. Now, if I wanted to have a double shot of espresso. You would just run that again. I'd just run it again. And this is not a complete implementation because obviously if you're making a coffee, you're going to have to somehow generate all the ingredients and the right ratios and things like that. But what I'm trying to show is just how the decorator will decorate those base classes, or not the base, but the past in interface, and build it up. And you would wind up with a decorator for everything you can do to this coffee. So you're still going to have a very long list of something. But they're all encapsulated and easy to work with. And then to add something to the car, the coffee, the superhero, whatever it is, you just call the decorator. So it's much easier to manipulate the object and add things to it, or potentially remove. I assume you could use decorators to take things out. Yes? Yeah. It's really not part of the pattern, but I don't see why not. I mean, obviously you would have to modify, like in our example here, modify the ingredients list. Might use a memento pattern to be able to rewind to a specific state. But you were talking about having to have all these classes for all things you can do, which is true. In the car example, we only had four classes. In this particular coffee example, all we can do is add espresso to it. If you're at a Starbucks or some other coffee place of your choice, there probably are well over 100 options you can do to a cup of coffee. But you're only writing one decorator for each option, as opposed to writing a class for each combination of options. As somebody comes in, orders a half-calf, double-D, calf, soy, latte, with cinnamon and chocolate. I don't know if that's the thing, but yeah. How do you build that class? You don't. You don't. It's a cup of coffee at the end of the day. It's a coffee. It's a coffee with different attributes. So this makes it dramatically easier to adjust the attributes and end up with a coffee. Because the coffee has cost, presumably. It has ingredients. There are basic elements that a coffee has. And instead of having some God class that knows what all these things do and then potentially have repeated code and other problems, you just say, oh, you know what? The cost of soy milk has gone up. So we have to change the soy decorator to instead of adding $0.50, we're now adding $1. And we're done. Done, right. And you also wind up with very efficient instances, because instead of having a coffee that doesn't have 997 things and has three things, you've got a coffee that has three things. Yes. Cool. So again, going back to my configurator for the bicycle system, had I known about this pattern, it could have been so much easier, because I would literally start with a bike. And I bike class, right? And ingredients would be frame, tires, brakes, gears, that type of stuff. And then just building up the cost as I go. There's still some additional logic you'll have to do. Like, for example, maybe you can't mix, going back to the car example, maybe you can't put a race car and an armored car together, right? So that's outside the pattern. But those are things that might happen. But you can certainly have a check saying, if I can cast this to an armored car, then throw an exception or something like that. Yeah, and it makes it easy to add new subsystems. New collections of things, whether it's to the bike or the car or the coffee or whatever. And you just keep decorating it and building it up to where you need to be. And you've got a very small set of classes that have infinite capabilities of mixing and matching. Cool. Very nice. And that's the decorator. Excellent. Oops, we're back on. Hold on. There we go. I'll figure this thing out eventually. Like we said in the other episodes, today we're shooting in the B channel. So I'm in charge of switching back and forth, which is why it's not perfect. So that is our series on design patterns. For now. For now. Let it be more. All right. Hope you enjoyed that. Really interested in your thoughts on this. And I'm sure we've received them by now. But keep them coming. And let us know what other types of things you'd like us to explore in episodes like this. Thank you so much for doing this. Thank you for having me out. This was awesome. This was a blast. All right. And we will see you next time on Visual Studio Toolbox. Thanks for watching.