Transcript from the "Exercise 4 Solution" Lesson
[00:00:51] And then show how you can instantiate those Buttons to have them on the screen. So, I will show you what we start out with. This is the empty file. By the way, just as a reminder both with the exercises from yesterday as well as the exercises for today.
[00:01:07] I recommend that you have a separate copy of that folder so that when you make changes to those files, you can go back at some point later and have pristine copies. Yeah.
>> Speaker 2: Question early today. Derek is wondering if chrome 34 will be good enough for the exercises, and not Canary since it's not available on Linux.
>> Kyle Simpson: Yeah. Chrome 34 is fine. We just don't want somebody on Chrome 28 or some old version. But Chrome 34's fine.
>> Kyle Simpson: Okay so, the way we would start out the first part of this task is to do this using the traditional class design approach. This is a parent class and a child class.
[00:01:49] Class orientation tells us that we should have a render method on our parent class that's sort of an abstract or base generic render. And then of course on our Button class we're gonna override the render and add additional behavior to it. So let's start by first declaring or defining our Button constructor you see here, so I will uncomment this code.
[00:02:10] This will be our Button constructor, our Button class definition. And you're going to need if you read in the readme, you're gonna need three parameters that you instantiate a Button with. First is a width, a height, and a label. Label obviously being the visible text that you're going to have on your Button.
[00:02:52] So we have to do so using sort of a manual polymorphic type of call. We can say the widget function, we want to make sure we pass in this binding that is our current one, and then pass along the width and height to our parent constructor.
>> Kyle Simpson: Next, since the parent constructor doesn't know anything about a label, we need to save off our label.
[00:03:16] So we can say this.label is equal to the label parameter that was passed in. And finally I've shown you here how you construct a button element. This hasn't been added to the DOM yet because the parent class is going to do that with the appendTo function. But we need to create our button element so that it can be added to DOM.
[00:03:37] Next we need to start adding our methods to our public API of our class. The way we do that in the classical approach is Button.prototpye. And in this case we're gonna add in render. If I can type today, render will be a function that accepts a parameter that tells it where to render it to.
[00:03:54] In this case we're probably gonna use the body. We need to call the parent render. So and again in a similar way to calling the widget parent constructor, we need to do something similar. So we're gonna have to do it manually, widget.prototype.render. And then we need to make sure to override to this binding.
[00:04:14] So, it's r this binding. And pass along that where function, so it knows where to append it to. And then we need to add in our click handler. So the, this sorry, this.$elem. We're gonna bind the click handler for our Button to call our Button.onClick, which we haven't defined, yet, but we will in a moment.
[00:04:41] And so we will call that this.onClick. But if we pass it along like that we know it's gonna lose our this binding, so we need to do a hard binding here to make sure that it will have the proper this context. Next, to define that we're going to add this also to the prototype.
[00:05:04] It's a function event. Now we want to console log out, and I think it specifies in the readme what you should say Button and then its name and then clicked. So we're going to console.log out the statement Button. And we can say this.label, the reason the this will work is because we made sure that this onClick is always called with our proper context.
[00:05:29] So Button, I'll put it probably in quotes, just to be most clear and then we say that it was clicked. Now we've defined our parent and our child classes using the classical approach. We come down here we want to instantiate it, it's probably what you're expecting, you would say new Button.
[00:05:48] I'm gonna give it a 100 width, a 50 height and I'll say "Hello" as my first label. My second Button that I will add to the page, I'll say Button and I will make it twice as big, actually four times as big, twice as wide, twice as high.
[00:06:05] Now if we will save off this code which I won't save my files since I'm not making local modifications here. But if were gonna do that or to open up the fixed version of this code to see what it should actually look like. Oops, I didn't get to the right spot, did I.
[00:06:20] All right. Let's see here. Day1, exercise4. If I open up the fix.html, you note there that of course, I have my Hello World Buttons that have been added to my page. And you'll also note if you watch the console, that when I click the Hello Button it indeed indicates which Button has been clicked.
>> Kyle Simpson: Any questions about how we did the classical approach to the widget button parent class coding? I need some semicolons I guess. All right, so what we wanna now do is, starting with this as our starting point. How would we then approach this, instead of in a classical style, how might we approach in the OLOO style?
[00:07:10] And just to save some time, so we don't get too bogged down this morning, I'm just gonna show you the fixed better file. I'm gonna show you how it's OLOO and talk you through the different design perspective that I have here. Rather than thinking about it as a parent and a child where the child extends the parent, and redefines certain base methods.
[00:07:28] And overrides them with polymorphism, and rather than thinking about that as our design pattern. And instead we could think about widget as sort of a utility object. That every widget on the page has certain common tasks that they would like to delegate to that widget utility object for it to handle.
[00:07:46] And so we would have a Button object, that obviously is going to handle creating Button widgets. And we would have the Button widget configured to delegate to the widget Utility object. And delegation of course, as we talked about yesterday, it's a different mechanism because there isn't a copying going.
[00:08:05] It's a different mechanism to class oriented code. So, here's how I might approach that. I would first start with making widget an object rather than as a constructor function. So here, I take advantage of the object literal syntax for short-handing and I declare that I'm gonna have an init function here on line two.
>> Kyle Simpson: That's sort of taking the place of the initialization code that a constructor used to do.
>> Kyle Simpson: You don't have to think about it as a constructor anymore because now we have the power to construct objects separately from when we initialized them. But this is an initializer that we can delegate because all widgets need to be able to do this base amount of delegation of initialization.
[00:08:47] We would say all of our UI widgets have at least a width and a height and have at least an element reference to the dom object. Now, here's where things start to be a little bit different because rather than trying to come up with a generic name like render, which I would intentionally shadow, and try to overwrite, and then cause myself polymorphic headaches.
[00:09:05] Instead, I came up with different names for trying to represent more specifically what the different tasks are doing. So my generic utility on my widget class I called insert. Because really, all he's doing, he is setting up some CSS so we might have been able to come up with a name that sort of stylized an insert or something like that.
[00:09:23] But his main job is to call the appendTo function and add it to the doc. So he's inserting our element into the page. So I came up with insert, you could have called add to page or insert. You could have called it whatever you want, but rather than using that generic name render that's gonna cause polymorphic shadowing headaches, I came up with a more specific name.
[00:09:43] Now my, moving onto the button object, which again, it's not gonna be a child class. It's gonna be a peer that we'll be able to delegate to the widget utility object. We have button = object.create(widget). We saw that yesterday. That's just the simple way of creating one object that's linked for delegation to another object.
[00:10:02] And rather than having to add anything with prototypes I can add methods directly to my button object. I can say button.setup, button.build and so forth. So my setup function is going to call, it's going to call the delegated initialization function that all widgets have the ability to delegate to.
[00:10:21] But you'll notice that now I can take advantage of delegation because the names are different. I can just say this.init. I don't have to say something like widget.init call this or any of that awkwardness. I can just say this.init and it will properly delegate out to the utility object.
[00:10:36] I call my this.label my this.element the same as I did before. Now I also need a build function and the build function is going to in addition to calling the delegated insert call, it's also going to attach our click handler. The this.elem.click and attaching it to our bind function.
[00:10:59] Finally my button.click looks exactly the same as before with the this.label. The only now difference is we're gonna have slightly a little bit more code, but more flexibility as well down here, because now we're going to have one step where we create a button. Btn1 will delegate to the button object, so we have btn1 that's delegating to it, but now we also need to set up our btn1.
[00:11:26] So here I'm giving it some parameters like 125, 30, 150, 40, that sort of thing. And then we call build rather than render to add those things to our page. So, key takeaways here are that there's a lot less code complexity in terms of not needing to manage constructors, prototype references, doing those manual polymorphic calls, things like that.
[00:11:49] We just have objects that are linked to other objects. And, you note that we have button one, and button two. We could have a thousand buttons, that we're all delegating to the same singular button object. So we're not limited in terms of anything we've had before. We're still creating in that sense instances, but these are instances that delegate, rather than instances that receive a copy, of their parent class or something like that.
[00:12:14] So we're not limited in any way in terms of our performance or something like that. We just have the flexibility now to deal only with objects, the simplicity to only deal with objects, and we have the flexibility to construct separately from initialization. Yes.
>> Speaker 2: The question online if there's some kind of equivalent to super.method or super.apply argument.
[00:12:34] It's like, in this pattern where you're basically having a button be a, I don't want to say subclass, but I think that's the thinking of the question.
>> Kyle Simpson: So, delegator or delegatee or delegate is the right way to say it in behavior delegation terms. But to answer the question, the question is, what if I really wanted to insert the shadowing?
[00:12:58] What if I really wanted to use the word render as the method name on both of these objects? What if that was really the appropriate thing, what would I do? Well, really your only option in that case if this, if you had Button.init and a Delegate.init obviously this ".init" isn't gonna work so you're gonna to have to do it manually, you're gonna to have to do the Widget.init.call, and make sure that you override the this binding.
[00:13:54] But that requires you then to opt in fully into the class design mechanism. It requires you to set aside any of the questions that you may have. I've written a bunch about this in that second title. The first appendix of that book, I deal directly with the class syntax of ES6, and I point out why there are some things that they've certainly made better.
[00:14:15] But there's a whole bunch of things that we already talked about yesterday that they really didn't make much better. So it's kind of a choice if you want to keep going with classes, you can choose something like super. But outside of classes there won't be a super mechanism.
[00:14:30] You'll either have to chose the explicit polymorphism like here or choose to use different names. Now I would make the advocation one final time that picking different names is better proper design, more descriptive names, rather than using a generic name like render which would mean a hundred different things at different levels.
[00:14:51] Being able to choose a specific name I actually think makes software design better. So, you just have to set aside that idea that you've been told that you need to create general and specific overridings for class designs. But hopefully that answers the question about super. Yeah?
>> Speaker 2: Could you setup widget and button in kind of a module pattern so that you could create private members as well?
>> Kyle Simpson: Sure. The setting up of a, so to indulge that question for just a moment, if I were to have something like widget is equal to an efe, and I were to have this stuff on a public API that I returned out.
>> Kyle Simpson: So now we did some module.
[00:15:48] Right that's all I had to do to turn it into a module. Now it's a module that has these two members on it. And you might ask well what if I wanted to have some kind of internal tracking? It doesn't make a lot of sense to have Internal data on the widget utility because it's really just the utility.
[00:16:02] But if we did the same thing with Button, if we instead had a var Button = and we called it and we did an if with the module pattern, and we cut that guy out. And in all these cases if these were.
>> Kyle Simpson: If these were methods on our public API,
>> Kyle Simpson: And we returned our public API. So now we've got two modules. You can see how quickly that is to change between the styles when you understand the syntax. So now we've got two modules. And you might ask, well, what about these members, here, like the width and the height?
[00:16:45] So it would be maybe nice if I could have a private width and a private height and a private label variable, rather than adding those things to some public thing like a this, wouldn't it be nice if I could make references to these things or, I'd also need an elem.
>> Kyle Simpson: All right so if we go down that path, if we started dealing with this module pattern with the closure so that we can track these things privately. There's a benefit now because we have the encapsulation, we're hiding details, there's all of that nicety. But I think you're gonna start to see why these two mechanisms are sort of orthogonal.
[00:17:29] It's not very common that you mix modules and delegation. Because, what happen with this this.init call? What we're hoping to do is delegate to an init method on some other module and have that guy start dealing with our things. So theoretically, we would like it to be able to do something like setting our private members.
[00:17:48] But he's in an entirely different module in an entirely different closure and he has no access to those properties. So, the usefulness of being able to delegate to a general initializer is completely lost, because we hid it. So, we would have to define our entire set of behaviors manually.
[00:18:07] We'd have to manually call width equals width, and height equals height, and so forth. So, it's not that there's no call for delegation with modules, but when you start talking about I want to use a module because I want the privacy of my variables, that significantly cuts off your ability to use delegation.
[00:18:26] So they are sort of orthogonal patterns.
>> Speaker 2: Could you set your publicAPI equal to object.createWidget and then add setup, and then you would also have the option to have private members if you wanted as well?
>> Kyle Simpson: Yeah, so if what you're asking is, rather than just making this an object could I make?
[00:18:43] And I have done this before, although there's only a few limited times, could I make my publicAPI delegate up to widget? Well I mean that's essentially what we would have done, I just hadn't gone that far. But if we did that then and we said rather than calling these things private, we were basically saying publicAPI.width, because that's the only way we could delegate up to this.init.
>> Kyle Simpson: Was that rather than these being private variables, they'd have to be variables on our public API, cuz that's the only object that the two would share. And now, all of a sudden, we've gone exactly back to what we were doing before, where these are public properties rather than private.
[00:19:25] So you could, theoretically, use this pattern, I kind of call this Frankenstein, but you can, theoretically use this pattern to track some things private and other things public. But what you're gonna inexorably come to the conclusion is, if I wanna delegate it has to be public. So you do get limitations where I wanna keep this thing private, but I also wanna delegate.
[00:19:44] You can't do that. So it's kind of just choice. That's why I say that they're othogonal.
>> Kyle Simpson: But yeah, you absolutely can do this. There have been a couple of times we've done that. Those are great questions. Any other questions about the comparison between class orientation and Olu?
>> Speaker 3: What's the reason behind, on line, well the Button?
>> Kyle Simpson: Do I need to go back to the original one?
>> Speaker 3: The Button.build.
>> Kyle Simpson: Let me reset myself just a moment.
>> Kyle Simpson: Okay. The question was what?
>> Speaker 3: On line 29, when you have the this.onClick.
>> Kyle Simpson: Mm-hm.
>> Speaker 3: What's the reasoning behind separating that out into its own method there?
[00:20:35] Like when you have the this.onClick.bind, instead of like an anonymous function there or-
>> Kyle Simpson: You're asking why wouldn't I just put an anonymous function?
>> Speaker 3: Right.
>> Kyle Simpson: Even if I did it an anonymous function, I'd still have the this binding issue. So I could do an anonymous function here that did the console.log in it.
[00:20:54] But his this binding would still need to be overwritten. So I'd still have to do the .bind(this) on my anonymous function. And that's a little bit of an awkward pattern to do binds on anonymous. Whether anonymous or not but it's a little bit of a weird pattern to do binds on function expressions and line function expressions.
[00:21:12] You can, but it's not very common for people to approach it that way. So the question that you're asking is why would I have my own separate handler? Well proper software design says separation of concerns, methods should do one tasks. So my onClick handler is its own method separate from the builder that did the attaching, but you could do it with an inline function.
[00:21:32] It wouldn't solve any of the problem, you'd still have to do your bindings. Does that make sense?
>> Speaker 3: Mm-hm.
>> Kyle Simpson: Yeah.
>> Speaker 2: There was the question whether strict mode would have an effect on any of these patterns? I think in particular, this one is the question, but I'm not positive.
>> Kyle Simpson: The only things that strict mode would change is what happens with the default bindings, which we're not relying upon the default binding in any of these cases. We're using at worst the implicit binding, because everywhere that we make a reference to a method, we always reference it by objectName.method which uses the implicit binding.
[00:22:09] Strict mode changes how the default binding rule is but it doesn't change any of the other rules. So strict mode should not change anything about how you'd use Olu.