2

Speaking specifically about the DI features I'm not a fan of Angular, and I'm not really too convinced by the idea that Angular makes your code more testable, or even that it makes it easier to write more testable code.

It seems to me that you end up with a lot of extra layers of abstraction, and indeed indirection, simply to support some "magic" - that is, making function parameter names be automatically instantiated. Without that one feature - that is: magically getting an instance of $http, or $car, or whatever when your function is called - it seems that it's not providing all that much.

How much extra effort is it, and how much less indirection and magic is there, in making those calls explicit? What you end up with is a "global" (from your app's point of view) namespace ... so there really is only a bit of convenience in not doing the stuff by hand.

From the docs:

myModule.factory('greeter', function($window) {
  return {
    greet: function(text) {
      $window.alert(text);
    }
  };
});
function MyController($scope, greeter) {
  $scope.sayHello = function() {
    greeter.greet('Hello World');
  };
}

The magic here is that "greeter" is automatically injected based on the name of the parameter, right? And I'm assuming it's the same for $scope, although as I understand it, all controllers are expected to have $scope as the first parameter so that may not be the case.

An non-magical example:

function MyController() {
  var scope = myApp.get('scope');
  var greeter = myApp.get('greeter');
  scope.sayHello = function() {
    greeter.greet('Hello World');
  }
}

As you can see, I've simply added a ".get()" method for getting your dependencies. I understand that this is completely possible with Angular - but that isn't the main case suggested by the docs I've read.

A non-magical test:

function myControllerTest() {
  var scope = {};
  var greeter = { greet: function() {} };
  myApp.set('scope', scope);
  myApp.set('greeter', greeter);
  // ... run an actual test on MyController here
}

I've not really done anything significant with Angular, but as I'm getting older and more curmudgeonly, I find that I dislike magic more and more. I'm perfectly happy adding (let's say) 20% more lines code to avoid levels of indirection that mean I can't easily step through my code, no matter the framework or language.

Can someone give a reasonable example of what angular does with DI that isn't relatively problem-free to do by hand with some basic conventions? Is there a benefit I've missed for "magic" simply so that you avoid explicitly defining your dependencies?

5
  • myApp.get('scope') - And which scope might that be? ^^ Commented Apr 26, 2014 at 20:07
  • @zeroflagL Well I guess if one were to make an attempt at understanding the question in its own terms, the answer is: exactly the same one you would get by using the magic function argument feature. Commented Apr 27, 2014 at 6:42
  • No, that's the point. You pass arguments into a function for a reason. You would not expect window.alert() to get its argument by itself. There is nothing magical with angular's way of injecting dependencies. It's similar to your 'non-magical' example. Except for the scope and the fact that you have introduced an additional dependency. Btw: You should use the array notation. Commented Apr 27, 2014 at 7:31
  • Uhm ... am I wrong in thinking that it gets your dependency based on the name of the parameter? Commented Apr 27, 2014 at 17:17
  • In your case, yes. As long as you don't minify your code. Commented Apr 27, 2014 at 17:23

2 Answers 2

3

Dependency injection is a fancy and new way of saying what is called "isolation" in classic OOP literature. It means that objects should not know about the outside world. It's the complementing principle to encapsulation, which says that the outside world should not know about about the inner workings of an object. In practice, this means that if you use dependency injection, you don't use global variables. Dependency injection is not a framework, it's a design principle. Angulars DI framework is only there to make it easier. As you noted correctly, using DI oftentimes introduces indirection. That's the price you pay. If you don't agree with it, that's perfectly fine. DI certainly makes your code harder to understand many cases. It results in more composable and modular software though.

In your 'non magical' version you basically introduced a 'service locator'. This results in a dependency on myApp. From a DI standpoint, that kind of dependency is of the worst kind because it is hidden, indirect and global. 'Hidden' means that it is not a function argument. You only see it if you read the code itself. Indirect means, that the thing you really care about is the greeter but the thing you depend on is myApp. Global should be obvious. There are lots of arguments why global variables are bad in general, but for testing, the problem is that your tests share state with each other. You can't run tests in isolation this way. This may lead to 'test flakiness', meaning some test fails sometimes and sometimes not, depending on another test.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the answer. I find the terms a bit weird though, not that you're wrong. Clearly Angular has it's own magic service locator, I believe known as "injector", which you can arbitrarily call at will in much the same way as I have done. Normally, during bootstrapping, you set up your app to do that stuff for you though. This, to my way of thinking, is much more "hidden" that being explicit. Clearly without the hidden magic, your app isn't going to work - I remain unconvinced that this somehow "removes" the dependency on the service locator itself.
0

First of all, I totally understand your point.

Today I also find my self being very selective when it comes to using any of the js frameworks out there. They just seem to come out of nowhere and much of them, you'd really be better off with your 20% more and avoid those extra layers.

Since I'm relatively new to all this I won't try to argue with you here... let me just also give my opinion on Angular.

Dominating a technology like javascript can be quite challenging for a newbie and I think it's easy to start with the wrong foot without someone shedding a bit of light on the way. Specially if you're aiming for some level of complexity in your application.

We all know that good patterns provide a solid ground to start with and that's precisely where Angular caught my attention when I first looked at it.

It was all there. Simple, slim and fast, easy to start with and to understand. The concepts and patterns behind are well known and made all the sense to me (DI, MVC, TDD, modularity, good separation of concerns, powerful binding strategies), even though I didn't know exactly how to implement them with javascript at that time.

So basically it get me started, and get me started well. Today I know a bit more than before, and I watched a lot of the other frameworks say goodbye for the same reason you point out. But Angular stayed, and the initial architecture too and for me that's huge!!

Maybe if I had time I would go for an Angular of my own, just for the kick of it... but when I think of it I guess I wouldn't make it too much different. So why bother?

Also, the community is growing fast, the documentation is very good and help is available on every corner!

I know you're talking DI and testability here but that's a small part of what you get from Angular. For that purpose only, I'm sure you'd get by pretty well with your alternative :)

By the way, and talking about testability take a look at angular scenario runner, I know it's being discontinued (Lord knows why?!?) but...

It's a kind of magic!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.