I was reading http://misko.hevery.com/attachments/Guide-Writing%20Testable%20Code.pdf (see, especially, page8) and watching Misko's Youtube videos on writing testable code, and it occurs to me that the way Angular does DI forces you to break the law of Demeter.
Simplifying from the PDF, an example of a Java constructor breaking the law of Demeter:
class AccountView {
boolean isAdmin;
AccountView(AccountService) {
isAdmin = AccountService.getCurrentUser().getProfile().isAdmin();
}
}
because the class only needs whether the user is an admin, and not the AccountService.
It seems like Angular forces you to break the law of Demeter with it's DI. I can't see an alternative to the following:
.controller('AccountServiceController',
['AccountService',
function(AccountService) {
this.user = AccountService.getCurrentUser().getProfile().isAdmin();
}]
);
We could inject a user if this were a controller that was controller a router with resolve parameters, but not for the general case. Any thoughts? Note that I'm assuming that only AccountService is a singleton, and each subsequent object is an instance (and cannot be DI'd).
I'm not sure the Angular example you provided or Angular DI in general is breaking the law of demeter. Angular dependency injection allows you to write very testable code.
Let's assume the real
AccountService.getCurrentUser();
is a very expensive network operation. We definitely do not want to call this method in a test. With Angular's dependency injection we can mockAccountService
so we don't have to call the real one.Let's write a test for
AccountServiceController
:controller
test
We have avoided the expensive network operation and the controller is not coupled in any way to the
AccountService
's internals.