مرور الگوی متد قالب (Template Method)0:30
In fact, it's probably something that you do all of the time, you just didn't realize that there was a name for it. Let me show you. As always, we will illustrate this in two different steps. To start, I'll show you a simple example that anyone can understand, and then after that, I'll show you some production code where we implement this very pattern. Now, to begin, you'll see I have an index.php file. I've set up Composer here just for the autoloading capabilities. And then finally, we have a source directory that's empty. All right.
Project Setup and Autoloading0:59
And then finally, we have a source directory that's empty. All right. So the first step is we're going to use autoloading here. So I'm going to pull in vendor/autoload.php. So index.php will just be our entry point. So that means we can get started with a couple classes. All right. To begin, simple example. Imagine that you're building some kind of sub sandwich shop, and you have all of these different classes
Building TurkeySub Example1:19
Imagine that you're building some kind of sub sandwich shop, and you have all of these different classes that represent the different kinds of sandwiches you can order, like a TurkeySub or a VeggieSub. So why don't we start with the TurkeySub Idea? And let's just flesh it out. So in order to make a TurkeySub, and let's call the method make here, what would we need to do? So how about we say,
And let's say that's all that's required here. All right. So that means we need to add a handful of methods. Lay bread. And we will just simulate this with a var_dump, laying down the bread. And then I will do the same thing for these other three methods, which I won't make you watch. All right. So I've added those remaining methods.
All right. So I've added those remaining methods. We lay some bread. We add some lettuce. We add some turkey. And we add some sauce. But now there's one problem here. Can you see it? If we're trying to use this format here, that means we must return this after each method.
If we're trying to use this format here, that means we must return this after each method. Like so. Okay. Now we can chain them in that way. So why don't we just dive in, run this, and see if it works. And by the way, first, we need to give it a namespace. So let's switch over to our index.php,
And by the way, first, we need to give it a namespace. So let's switch over to our index.php, and say, new, app, turkey sub, and make one. All right. Let's run this from the terminal. php index.php.
Adding VeggieSub Duplication2:50
Let's run this from the terminal. php index.php. And sure enough, we have sort of our little algorithm for how we create a turkey sub. So all this looks great. No problems. But now we're going to build a veggie sub. And that's a little bit different. But then at the same time, it's the same. So let's think about that. So source veggie sub.php.
So let's think about that. So source veggie_sub.php. And many times a telltale sign will be that you just copy and paste a lot of it here. And then you just modify bits and pieces. So keep that in mind. We have our veggie_sub. And now, of course, we don't want to add turkey. We want to add just lots of veggies. All right. So these are the same.
Introducing Template Method3:36
All right. So these are the same. All of the stuff is the same, except for this one. But nonetheless, this will do the trick. So if we now try to make a veggieSub, and if we run it, now we have, once again, an algorithm for how a veggieSub is created. So now let's introduce the templateMethod pattern. And this particular design pattern is especially useful when we are worried about code duplication. Exactly this scenario. We have all of these things that are the same from class to class.
Exactly this scenario. We have all of these things that are the same from class to class. Well, we want to dry up our code as best as possible. So the way we do that using the template method pattern is we extract a bunch of this to an abstract class. But then when it comes to specific differences, well, we just defer to a subclass. So from that point of view, the VeggieSub subclass should be responsible for really only the things that are unique to making a veggie sub. And the same would be true for the TurkeySub subclass. So why don't we tackle this in two steps, starting with the most easiest option?
Extracting Abstract Sub Class4:36
And the same would be true for the Turkey subclass. So why don't we tackle this in two steps, starting with the most easiest option? One, well, we have a bunch of methods that are identical. No difference. So those definitely need to go to the parent class. Extend Sub. I will create that. Sub.php. Namespace is app. This will be our class Sub.
Namespace is app. This will be our class Sub. But it will be an abstract class. OK, so if we come back, add lettuce and add bread. Those are identical. So they can go within here. Next, what else? Add sauces. That too will be the same. So I can place that within the parent class.
That too will be the same. So I can place that within the parent class. All right, if I come back to our veggieSub, already this is starting to look better. Next, I'm going to do the same thing for turkeySub. These two are no longer required. And addSauce is not as well. Finally, it will extend that abstract class. All right, so if we come back and run this again, we still get the same output. But we've removed some duplication using basic inheritance. But we're not quite done yet.
Defining Abstract Hook Method6:49
sub. So already I'm liking this. But if I go back to sub now, what do we do right here? Well, why don't we do this? Let's generalize this name and then simply defer to an abstract method. That way, this template method is in complete control of actually triggering these methods. However, it doesn't need to be responsible for defining their behavior. So let's try this out. Why don't we generalize this to something like addPrimaryToppings? That way, a hamSub would simply add ham.
Why don't we generalize this to something like addPrimaryToppings? That way, a HamSub would simply add ham. But a VeggieSub would add veggies. A TurkeySub would add turkey. Now we can add that method and make it abstract. And by the way, these can be protected. OK, protected, abstract function, addPrimaryToppings. Now, if you're not overly familiar with abstract methods, remember, we're saying that this class is going to require a subclass to offer this method. So for example, if this did not exist here and you just trusted your subclasses to offer
is going to require a subclass to offer this method. So for example, if this did not exist here and you just trusted your subclasses to offer that method, well, very easily things could break. Because we tried to reference our template method here, we try to add primary toppings. And if that subclass doesn't offer the method, then everything blows up. So what we do here is we say, no, any subclass is required to offer this method. I need that method in order to do my job. Think of it like that. So now we've generalized this. That means within veggie sub, we can change this to add primary toppings.
Production Authentication Example9:28
If we switch over to the repository for our GitHub authentication lesson, which we have right here at LayerCasts, let me dig in and show you something. So we go into the app directory, and within our account providers, we could presumably have many different authentication providers. That way, you could log in with Twitter or GitHub or Facebook. But as you can imagine, if we want to add all of that functionality, a lot of this stuff will be the same from class to class. So to prevent duplication, we use the template method pattern, like this. Within our contracts, you'll see our provider interface just says that we need to authorize a person using this service, like Twitter or Facebook, and we also need to grab their
Within our contracts, you'll see our provider interface just says that we need to authorize a person using this service, like Twitter or Facebook, and we also need to grab their profile information. That's all that each provider needs to offer. So if we come back and take a look at this, notice that we have an abstract class here. Now, let's see how this works. We tried to remove duplication, so these two methods that are defined in our interface are stored on this class, not the GitHub class or the Facebook class or the Twitter class. So once again, how do we deal with these unique things that could change from class to class? Well, we defer to an abstract method.
So once again, how do we deal with these unique things that could change from class to class? Well, we defer to an abstract method. For example, right here, when we request an access token from the service, well, we need to know which URL we're making a POST request to. So rather than duplicating all of this code and placing it within each of those subclasses, we instead use an abstract method. And if I scroll right up, you'll see that a subclass is required to give us this method. We need that in order to do our job. So if we come back and look at GitHub, right down here, we have that functionality. All right, let's go back and continue on.
Now, for Twitter or Facebook, you might have to do something a little bit different. So it doesn't matter. They'll just have to implement their own functionality. All right, let's go back to Provider and finish up. So now we have our user object. But that's going to look a little bit different, once again, from class to class. Maybe the keys that Facebook uses is not the same as the ones that Twitter uses when it returns that information. So once again, we create an abstract method and let the subclass fill in that missing information.
So once again, we create an abstract method and let the subclass fill in that missing information. So we scroll up. We have an abstract method. And if we see how GitHub handles it, we scroll down. It simply accepts the array of data. And in the case of GitHub, they give you keys of login, email, and avatar_url. So we simply map that to username, email, and avatar. So maybe in the case of Facebook, their keys are totally different. And it probably is.
So maybe in the case of Facebook, their keys are totally different. And it probably is. But that doesn't matter. The Facebook class will simply be responsible for specifying how we map to a User. So that means if we take a look at this, our class is actually really, really simple. Our algorithm, quote unquote, on the other hand, is contained within the abstract class, like so. And even on that note, what a lot of people would recommend doing is adding the final keyword. So you would do final public function user.
keyword. So you would do final public function user. And that way, you still have your public interface, but subclasses are not allowed to change how this works. So that's something to keep in mind. All right, everybody. That's been your review of the template method design pattern.
