تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Recognizing bloated classes0:00

If you are inheriting an existing project and you're not familiar with it yet, a really good exercise is to visit both the User'sController as well as the User model to get an idea of what you're working with. Because the truth is, for most projects, these are the two classes that are most likely to become bloated. And that's why, for example, the User model often gets referred to as a God object, because it's just sort of the place that we throw things when we don't exactly understand where else to put them. And initially in a project, this is fine. The class isn't very big. But then after a year of usage, you realize that both the User'sController and the User model have become incredibly bloated. And that's exactly how you end up with classes just like this. When you started, User'sController only needed, how about the UserRepository? That's all you needed. But then you needed some additional

you end up with classes just like this. When you started, User'sController only needed, how about the UserRepository? That's all you needed. But then you needed some additional things, like you wanted to log some data. So you pulled that in. No problem, right? But then you also found out that when a user signs up, you want to record that this event took place. So maybe you add a new repository for user events. And then later, you decide that maybe we're going to introduce billing in some situations. So you reference a biller here, a concrete one at that. And then further down the line, we realize, oh, we actually need to send out mail. So you add the mailer object. And before you know it, it just keeps growing and growing. And this is how you end up with especially controllers that have way too many instance variables. Now, when I use this term instance variable, I am referring to a property on an

Instance variable limit guideline1:36

And this is how you end up with especially controllers that have way too many instance variables. Now, when I use this term instance variable, I am referring to a property on an object that itself is another object. So in this case, we are using dependency injection to pass in about seven dependencies. Each of these are objects, so we can refer to them as instance variables. All right, so let's get back to object calisthenics. What would the rule be in this case? And actually, I think we're going to have to migrate it a little bit for PHP. The official rule, or more in fact, guideline from Jeff Bay, who created these exercises, is that an object should have at most two instance variables. Now, the truth is, this might make a little more sense in, for example, Java. But with PHP, I think that might be a little too extreme, maybe not the most practical thing. So we can see that in some

this might make a little more sense in, for example, Java. But with PHP, I think that might be a little too extreme, maybe not the most practical thing. So we can see that in some situations, these guidelines need to be adapted or tweaked just a little bit depending upon the language. Now, in our PHP community, there are developers who have worked to translate some of these guidelines to fit our environment. And likely the most notable in this group would be Rafael, who has done a number of talks on this very subject. Now, his recommendation is to up that number from two to five. So at most, you can have five instance variables. And realistically, that's probably a fine limit. However, for me personally, just my own thoughts, I think five is probably too much. At least from my own experiences, whenever I have a class that has five instance variables, it's almost always an indication that I've missed something. And yes,

is probably too much. At least from my own experiences, whenever I have a class that has five instance variables, it's almost always an indication that I've missed something. And yes, there will always be exceptions to this guideline. But as a general rule of thumb, I set this number to four. As much as I possibly can, I do not exceed that limit. But still, that begs the question, what do we do about this? How can we clean this up to adhere to the guideline? Well, one thing that you begin to learn as you dive into all of these various patterns and techniques and principles is that they're all very much linked to one another. So for example, consider the most popular principle in our field, single responsibility principle. Well, a User'sController is responsible for accepting requests and returning responses, right? But actually, we can see here, it's actually doing a bunch of stuff. And yes,

Avoiding generic service classes4:05

principle. Well, a User'sController is responsible for accepting requests and returning responses, right? But actually, we can see here, it's actually doing a bunch of stuff. And yes, it's good that it's deferring to other objects to handle the specifics. But that still doesn't change the fact that it just has way too much awareness as to what's going on here. So let's see some steps that would help us clean this up. The very first thing I see is that we have a UserService. And a quick note on that, be very careful of UserService, just like you're careful about User'sController and your User model, you can fall into the exact same trap. And that's why I tend to avoid classes called UserService, because it's too general. A service for a user in your system, well, that could be countless things. So once again, your class gets too bulky, and you start breaking these exact same rules again. So keep that in mind. And on that note,

system, well, that could be countless things. So once again, your class gets too bulky, and you start breaking these exact same rules again. So keep that in mind. And on that note, that's why I often use classes like this. If my class is really just encapsulating some kind of unique behavior, well, by naming the class in this way, it forces me to be that much more sensitive to the single responsibility principle. And that way, if I realize that registerUser is actually doing things that aren't related to registering a user, it becomes much more clear, right, that I'm missing something. So that's a different lesson. But file that away as something to think about. For now, we're going to keep it as UserService. Alright, well, the next thing I see here is that we are still referencing repositories here. So we have a service class. But then we're also accessing our database adapter in the same class, which is a little weird, right? Could we instead

Moving repositories into services5:44

is that we are still referencing repositories here. So we have a service class. But then we're also accessing our database adapter in the same class, which is a little weird, right? Could we instead just defer to the UserService? And that way down here, UserService would have UserRepository as a dependency, like this userRepository. And I will initialize those fields. So now for any interactions with a repository from your controller here, well, we can just filter that through a middleman here, the UserService. That means I could remove this. And you know what, we could probably remove UserEventRepository as well. And add that as a dependency here. And let's go ahead and add that. And I will do my macro once again to initialize it. Alright, so now you see, we are extracting responsibilities. So once again, we're trying to adhere to a SOLID principle. But in the process, it's also allowing us to better adhere

Splitting controllers by responsibility6:39

Alright, so now you see, we are extracting responsibilities. So once again, we're trying to adhere to a SOLID principle. But in the process, it's also allowing us to better adhere to these various exercises. So that's what I mean when I say these are all linked. Now let's see what else we can see we have a RegistrationService here. So presumably, that's because the User'sController is responsible for maybe having a register method that allows the user to sign up. And then maybe additionally, things like cancel, all of that stuff would use the RegistrationService. And again, at the beginning of a project, maybe that's fine. But after it grows and grows, it starts to break down. So instead, maybe you should have a different controller to handle that stuff. For example, what if you instead had an AuthController? And here is where you would have your register method and anything related to that.

controller to handle that stuff. For example, what if you instead had an AuthController? And here is where you would have your register method and anything related to that. Now, AuthController would have a reference to your RegistrationService. Let's initialize that. And now, once again, we move all of the controller methods to this new one, which allows us to remove yet another dependency. So that means he's gone, he's gone, and he's gone. Next, we have Stripe. So presumably, when you sign up, you get billed in some way. So notice I said sign up or register. So it sounds like that too should be part of RegistrationService. So we should extract that accordingly. It's no longer relevant here, so I can remove that. Next, for things like mail and logger, you have a couple options, of course. You could keep that here. You could move that to the service class, if that makes sense to you. Or you could

Using events for side effects8:21

that. Next, for things like mail and logger, you have a couple options, of course. You could keep that here. You could move that to the service class, if that makes sense to you. Or you could fire an event. If a user gets an email when a user signs up, then if you wanted to, you could just use an event listener. Listen for when a user registers and fire off an email. And then identically the same here, listen for when a user signs up and then log this data. So that means if we're using events here, I could remove those two as well, which allows us to clean up our UsersController that much more. And I think that looks a lot better. So notice that once again, I said this in the very last video, it's not about blindly following rules. When I'm browsing a project and I come across four or five instance variables, that's a code smell. Now remember, code smell doesn't mean fix this right away or you die. It means there might be something going

project and I come across four or five instance variables, that's a code smell. Now remember, code smell doesn't mean fix this right away or you die. It means there might be something going on here and you might want to look into it more. Now, after you do look into it, you may decide, no, in this special case, it makes sense for all of them to be there. So I'm going to keep it as it is. No problem there whatsoever. But many times, once you dig in, you realize that you have way too many jumbled responsibilities and you instead need to extract some classes, move some dependencies and behavior elsewhere. And in doing so, yes, we adhere to this guideline, but more importantly, we've yet again simplified our code.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟