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

Star Trek Captain Example0:35

to knowledge. The knowledge that one object has of another object. So why don't we do this? First, let me show you the silly but easy to understand example, and then we will move on to something a bit more real world. So imagine that we are in the world of Star Trek. So we probably have a class Captain. Now what should a Captain be able to do? Well, probably they should hire crew members. So what do we do here?

that worker is dependent upon. And as you can imagine, that may not be ideal. There's too much knowledge leaking out here. But let's continue on. So we set up our worker. Now what can a worker do? Well, of course they can work, and then maybe they can also sleep, just as two quick examples. So now we can call any of those methods directly off of the worker object that is passed into the hire method. Or maybe we can call it manage.

the hire method. Or maybe we can call it manage. So when you manage, you have them work, but then you also have them sleep, because everyone lives on that starship. So at this point, everything looks good, right? But now, what about when the Captain starts hiring android crew members? Well now that's going to change a little bit, because an android doesn't sleep. So you might think, alright, well I can handle this. I know that I should code to an interface. So you set up one.

Problem with Fat Interface2:12

I know that I should code to an interface. So you set up one. WorkerInterface, HumanWorker, WorkerInterface. Now within here, you specify that yes, a Worker should work, and also a Worker should sleep. Now we could say class HumanWorker and have them implement WorkerInterface. Now if I were to remove these entirely, don't forget, it'll let me know, hey, you have to conform to that contract. Alright, that looks fine. But now we set up AndroidWorker, and he too will implement WorkerInterface. But now we come to this same issue, where AndroidWorker has to implement the same methods.

But now we set up AndroidWorker, and he too will implement WorkerInterface. But now we come to this same issue, where AndroidWorker has to implement the same methods. So the first method is fine. We could just say return AndroidWorking. However, what about sleep? Well in the human version, we can say return HumanSleeping, whatever logic would be required to sleep. But now, an android does not sleep. So maybe we just return null, because it doesn't apply to an android. Now what you see right here, this is what the Interface Segregation Principle is all

Split Interfaces by Role3:37

So how could we improve this? Well, if we follow this principle, instead we should divide this into smaller chunks. Remember, even an interface that contains a single method is perfectly fine. The thing you should be worried about, in fact, is the opposite. We refer to those as FAT interfaces. And as you can imagine, a FAT interface breaks the Single Responsibility Principle. So once again, these principles are all linked. Instead, why don't we do this? Let's change this to WorkableInterface, and it will just have the work method. Next, we will do another one called SleepableInterface, and that will just have the other method.

Let's change this to WorkableInterface, and he will just have the work method. Next we will do another one called SleepableInterface, and that will just have the other method. So now HumanWorker will implement WorkableInterface as well as SleepableInterface. Now though, an AndroidWorker does not sleep, so he only needs to implement WorkableInterface. Now we can remove that sleep method entirely, and we are now conforming to the Interface Segregation Principle. Now let's come back to our Captain. So before, he had a dependency upon Worker and whatever dependencies Worker had. But now he's not really interested in the specifics. He just wants something that implements WorkableInterface.

Manage Without Type Checks4:46

But now he's not really interested in the specifics. He just wants something that implements WorkableInterface. And now if we wanted to, we could remove the sleep method, and this would all work. However, why don't we make a quick callback to the OpenClosed Principle? That states that a class should be open for extension but closed for modification. So how do we do this? Do we do something like we discussed in earlier lessons, where we say if Worker is instance of AndroidWorker, then in that case we should simply return, otherwise we will have the worker sleep. Is that what we do?

worker sleep. Is that what we do? No. We've learned by now that, more often than not, that breaks the OpenClosed Principle. Instead, what you might think about is maybe saying something like ManageableInterface. Now if we were to set up ManageableInterface, maybe we could have a public function. Why don't I just call it bManage to be quick. And now if we come back to Captain, rather than calling these specific methods, where it may need to have an understanding of the type of object that was passed, we don't want to do that.

it may need to have an understanding of the type of object that was passed, we don't want to do that. Instead, we just want to say Worker $bManaged. Now each of these classes can implement that. ManageableInterface, do the same thing here. And finally, we implement those methods, $bManaged. And now for a Human Worker to be managed, well, they need to work, and they also need to sleep, and whatever else is required. However, for the Android Worker to be managed, they simply need to work. So hopefully that sort of makes sense.

Laravel PasswordBroker Case6:57

particular implementation of Worker. That's the key to understanding all of this. OK, let me show you a more real-world example, specifically in the Laravel source. And this is actually something that the creator of Laravel, Taylor Otwell, touches upon in his book Apprentice to Artisan. Definitely check that out. Now if we access a class called PasswordBroker, this is what Laravel uses behind the scenes to allow for the built-in password reminders functionality. Now if we access the sendReminder method, notice that we depend upon an implementation of RemindableInterface.

Now if we access the SendReminder method, notice that we depend upon an implementation of RemindableInterface. But notice that he called the variable name user. So as you can imagine, we're probably just expecting a User object that we can use to fetch any applicable data from. For example, the user's email address. Now you might be thinking, well why didn't he just do something like this? Why don't we just depend upon an instance of User? We know that every new installation of Laravel comes with a User Eloquent class, so why didn't he do that?

We know that every new installation of Laravel comes with a User Eloquent class, so why didn't he do that? Why not just say SendReminder is going to need an instance of User and be done with it? Well, the reason why he doesn't do this is because now, with this code, PasswordBroker is dependent upon User. But further, if we switch over to User, we can see that User is dependent upon Eloquent. So as such, PasswordBroker is now dependent upon Eloquent. So as you can see, even with a small change like that, there could be big ramifications in our project.

Avoid Ripple-Effect Dependencies8:28

So as you can see, even with a small change like that, there could be big ramifications in our project. And this is what we can refer to as ripple effects. You make some small change in one part of your application, and it ripples over to another part of your application that is dependent upon it. But if we switch back to PasswordBroker, think to yourself, think about knowledge. Why does PasswordBroker need to depend upon Eloquent? Why does it need to have knowledge of Eloquent? And the answer is, it really doesn't. It just needs to accept some kind of object that provides the necessary methods.

And the answer is, it really doesn't. It just needs to accept some kind of object that provides the necessary methods. And that's why he type-ins RemindableInterface rather than User specifically. Now PasswordBroker doesn't care whether we pass in a User Eloquent object or something else entirely. It simply cares that whatever is passed to the method conforms to the interface. So if we take a look at RemindableInterface, once again, check it out. It's a single method, and that's perfectly okay. That's the only thing that this method even required. It just needed some way to fetch the email.

That's the only thing that this method even required. It just needed some way to fetch the email. So if we come down, notice that we call getReminderEmail. If I go back to the User object, notice that it implements RemindableInterface. So if we scroll down, there it is. This is the only thing that PasswordBroker really cares about. It needs some way to fetch the user's email. So if that's really the only thing we need, why would we ever make a method depend upon a full object like User or even Eloquent, which User depends on? And the answer, of course, is, well, we shouldn't.

a full object like User or even Eloquent, which User depends on? And the answer, of course, is, well, we shouldn't. That's simply bad design. So in closing, once again, the Interface Segregation Principle states that a client should not be forced to implement an interface that it doesn't use. If we were to quickly come back to User, notice how it implements it here. But what if this interface was really big and it had all of these additional methods? Well, it's very possible that one implementation would end up having to create some method and return null because it's not even being used. Don't do that.

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