Conditional Registration Paths0:00
Take a look at this RegistrationController I have here. This is mostly pseudocode, but it should be enough to get the point across. So you'll notice that we are inspecting a parameter, or some item in the request. And based upon its value, we are proceeding in one of three different ways. So for example, if the User wants a business plan, there are a certain number of steps that we must follow. And in this case, I've decided, well, you've got to create the User. And you've got to create a new Team for the User. And you have to invite the team members to that Team. Maybe they fill out a text area, and they enter lots of email addresses that should
So we're going to add an additional step. Before you can leave a comment or participate in the forum, you must first verify your email address. OK. So this represents three different ways that we could register a User. And one of the downsides is, let's say down the line there is a fourth or a fifth different way that we register. What you'll end up doing is adding additional checks here, and additional private methods to the controller. And it all just starts to feel kind of gross and error-prone.
Refactor Using Strategies1:45
to the controller. And it all just starts to feel kind of gross and error-prone. So what options do we have to fix this? There's a few options. I'll show you one or two. Here's one that I often recommend. If in a class you have multiple methods that have the same prefix, in this case that prefix would be register. So notice, registerTeam, or registerSubscriber, or registerGuest. Now often when this is the case, it's a sign that the prefix should be its own method.
So notice, registerTeam, or registerSubscriber, or registerGuest. Now often when this is the case, it's a sign that the prefix should be its own method. So we'd have some kind of class with a method called register. All right. So how do we know what the class should be called? Well, often, again, that's very easy. Whatever comes after the prefix is usually the class. So in this case, we'd have Team, Subscriber, and Guest. Now the key thing to understand here is that each of those classes should conform to the same contract, the same interface.
Now the key thing to understand here is that each of those classes should conform to the same contract, the same interface. So if we took that approach, we'd have a class Team, a class Subscriber, and then finally a class Guest. So again, notice they all conform to the same contract, and that contract states you register a User in some form or another. So what you'll see here is, yes, this is basic polymorphism, and it's also a form of the strategy pattern. But one issue here is that in your application, you probably will have an Eloquent model called Team and maybe an Eloquent model called Subscriber or User.
Avoid Model Name Collisions3:09
But one issue here is that in your application, you probably will have an Eloquent model called Team and maybe an Eloquent model called Subscriber or User. And you may not want to have that overlap because really, when it comes to registering a User, it's not model-level behavior. Maybe you are still working with the request. Maybe you are performing database queries. Maybe you are sending an email. Maybe you are dispatching a job. Maybe you are logging something. So it's almost like we need a layer in between the controller and the model.
Maybe you are logging something. So it's almost like we need a layer in between the controller and the model. So it sounds like we just need a different naming convention for the class, and we should be good to go. So if I scroll down, let's think, what would the name be? Well, again, if we're dealing with an implementation of the strategy pattern, and by the way, if strategy pattern sounds confusing to you, it's really not. It's very simple to understand. And think of it in the same way you would think of maybe a video game. Maybe you need to steal something in a video game.
Okay, so why don't we use that approach here? Registers team, registers subscriber, and then finally registers guest. Next, we can stick with this method called register here, or if you want to stick with common Laravel conventions, you might go with a handle method. So now think about it. If we scroll down, when we register a team, all of these steps would go here. When I register a subscriber, all of these steps would go there. And then finally, when I register a guest, these steps would go in that implementation. Now you have three different strategies for registering a user in your system. So let's think about how we can now clean this up.
Factory for Strategy Selection5:44
Now you have three different strategies for registering a User in your system. So let's think about how we can now clean this up. If I scroll back down, we still need some way to determine which strategy we will adopt, and we'll do that in a minute. But we do know I no longer need these methods directly in my Controller, and I consider that a win. Okay, so let's figure out. It sounds like we need a method called getRegistrationStrategy, and you can call this whatever you want. I'm just being very specific here. So if I were to take everything we had here, think about it.
I'm just being very specific here. So if I were to take everything we had here, think about it. All we'd have to do is return, and we already have this set up, so I would just return a new RegistersTeam instance. This would be a new RegistersSubscriber instance. And then finally, a new RegistersGuest instance. And that's it. So really, what this ends up being is a simple factory method. It's a method that inspects some parameter, or in our case, something from the request, and based upon it, it returns the necessary class instance that we need.
Enforce Common Interface6:45
It's a method that inspects some parameter, or in our case, something from the request, and based upon it, it returns the necessary class instance that we need. So now think about it. When I call getRegistrationStrategy, we don't care which object is returned. It doesn't matter. All we care about is that we receive an instance that can be handled. And that's why it's essential that we conform to a common interface here. That's the secret sauce. And by the way, if you want to make that explicit, then fine, set up an interface. And then maybe you'll have your handle method here.
And by the way, if you want to make that explicit, then fine, set up an interface. And then maybe you'll have your handle method here. Okay, now this would implement RegistersUser. So yeah, it's the same thing here. We are simply enforcing that contract. Nonetheless, it's still the exact same end result. So let's go over this. If I scroll back down, now imagine the User signs up, and they do hit the button to register a business account. All right, while we hit the store method, we detect our registration strategy.
Walkthrough Strategy Examples7:49
a business account. All right, while we hit the store method, we detect our registration strategy. Is the plan business? Yes. So this now returns an instance of RegistersTeam. We then call a handle method on the team, and we do the necessary steps to register a team. Okay, here's another example. The User signs up as a guest account. They're not paying at all.
The user signs up as a guest account. They're not paying at all. All right, we get our registration strategy, and that returns an instance of RegistersGuest. And then again, we call a handle method. So now, we create the guest User, we fire off a welcome email, and then a second verification email. And that's all there is to it. So really, if you break it down, even though this is a simple refactor, we're touching upon a number of different things.
