AngularJS in Patterns (Part 2). Services.
Edit · Jun 8, 2014 · 11 minutes read · Follow @mgechev
This publication aims to provide a more theoretical overview of some of the AngularJS components in order to show you how the things you are already familiar with (like different Object-Oriented Design Patterns) fit in the picture.
Link to the first part of the series.
This blog post continues the series “AngularJS in Patterns”. You can find the original GitHub repository, which contains all the information at github.com/mgechev/angularjs-in-patterns. If you like my work you can follow me at github.com/mgechev.
Services
In this sub-chapter we are going to take a look at the design and architectural patterns used in AngularJS’ services.
Note: Some of the described patterns are used in other components as well but their usage is almost equivalent so they are explained here.
Singleton
The singleton pattern is a design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. The concept is sometimes generalized to systems that operate more efficiently when only one object exists, or that restrict the instantiation to a certain number of objects.
In the UML diagram below the singleton design pattern is illustrated.
When given dependency is required by any component, AngularJS resolves it using the following algorithm:
- Takes its name and makes a lookup at a hash map, which is defined into a lexical closure (so it has a private visibility).
- If the dependency exists AngularJS passes it as parameter to the component, which requires it.
- If the dependency does not exist:
- AngularJS instantiates it by calling the factory method of its provider (i.e.
$get
). Note that instantiating the dependency may require recursive call to the same algorithm, for resolving all the dependencies required by the given dependency. This process may lead to circular dependency. - AngularJS caches it inside the hash map mentioned above.
- AngularJS passes it as parameter to the component, which requires it.
- AngularJS instantiates it by calling the factory method of its provider (i.e.
We can take better look at the AngularJS’ source code, which implements the method getService
:
function getService(serviceName) {
if (cache.hasOwnProperty(serviceName)) {
if (cache[serviceName] === INSTANTIATING) {
throw $injectorMinErr('cdep', 'Circular dependency found: {0}', path.join(' <- '));
}
return cache[serviceName];
} else {
try {
path.unshift(serviceName);
cache[serviceName] = INSTANTIATING;
return cache[serviceName] = factory(serviceName);
} catch (err) {
if (cache[serviceName] === INSTANTIATING) {
delete cache[serviceName];
}
throw err;
} finally {
path.shift();
}
}
}
We can think of each service as a singleton, because each service is instantiated no more than a single time. We can consider the cache as a singleton manager. There is a slight variation from the UML diagram illustrated above because instead of keeping static, private reference to the singleton inside its constructor function, we keep the reference inside the singleton manager (stated in the snippet above as cache
).
This way the services are actually singletons but not implemented through the Singleton pattern, which provides a few advantages over the standard implementation:
- It improves the testability of your source code
- You can control the creation of singleton objects (in our case the IoC container controls it for us, by instantiating the singletons lazy)
For further discussion on this topic Miško Hevery’s article in the Google Testing blog could be considered.
Factory Method
The factory method pattern is a creational pattern, which uses factory methods to deal with the problem of creating objects without specifying the exact class of object that will be created. This is done by creating objects via a factory method, which is either specified in an interface (abstract class) and implemented in implementing classes (concrete classes); or implemented in a base class, which can be overridden when inherited in derived classes; rather than by a constructor.
Lets consider the following snippet:
myModule.config(function ($provide) {
$provide.provider('foo', function () {
var baz = 42;
return {
//Factory method
$get: function (bar) {
var baz = bar.baz();
return {
baz: baz
};
}
};
});
});
In the code above we use the config
callback in order to define new “provider”. Provider is an object, which has a method called $get
. Since in JavaScript we don’t have interfaces and the language is duck-typed there is a convention to name the factory method of the providers that way.
Each service, filter, directive and controller has a provider (i.e. object which factory method, called $get
), which is responsible for creating the component’s instance.
We can dig a little bit deeper in AngularJS’ implementation:
//...
createInternalInjector(instanceCache, function(servicename) {
var provider = providerInjector.get(servicename + providerSuffix);
return instanceInjector.invoke(provider.$get, provider, undefined, servicename);
}, strictDi));
//...
function invoke(fn, self, locals, serviceName){
if (typeof locals === 'string') {
serviceName = locals;
locals = null;
}
var args = [],
$inject = annotate(fn, strictDi, serviceName),
length, i,
key;
for(i = 0, length = $inject.length; i < length; i++) {
key = $inject[i];
if (typeof key !== 'string') {
throw $injectorMinErr('itkn',
'Incorrect injection token! Expected service name as string, got {0}', key);
}
args.push(
locals && locals.hasOwnProperty(key)
? locals[key]
: getService(key)
);
}
if (!fn.$inject) {
// this means that we must be an array.
fn = fn[length];
}
return fn.apply(self, args);
}
From the example above we can notice how the $get
method is actually used:
instanceInjector.invoke(provider.$get, provider, undefined, servicename)
The snippet above calls the invoke
method of instanceInjector
with the factory method (i.e. $get
) of given service, as first argument. Inside invoke
’s body annotate
is called with first argument the factory method. Annotate resolves all dependencies through the dependency injection mechanism of AngularJS, which was considered above. When all dependencies are resolved the factory method is called: fn.apply(self, args)
.
If we think in terms of the UML diagram above, we can call the provider a “ConcreteCreator” and the actual component, which is being created a “Product”.
There are a few benefits of using the factory method pattern in this case, because of the indirection it creates. This way the framework can take care of the boilerplates during the instantiation of new components like:
- The most appropriate moment, when the component needs to be instantiated
- Resolving all the dependencies required by the component
- The number of instances the given component is allowed to have (for services and filters only a single one but multiple for the controllers)
Decorator
The decorator pattern (also known as Wrapper, an alternative naming shared with the Adapter pattern) is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class.
AngularJS provides out-of-the-box way for extending and/or enchanting the functionality of already existing services. Using the method decorator
of $provide
you can create “wrapper” of any service you have previously defined or used by a third-party:
myModule.controller('MainCtrl', function (foo) {
foo.bar();
});
myModule.factory('foo', function () {
return {
bar: function () {
console.log('I\'m bar');
},
baz: function () {
console.log('I\'m baz');
}
};
});
myModule.config(function ($provide) {
$provide.decorator('foo', function ($delegate) {
var barBackup = $delegate.bar;
$delegate.bar = function () {
console.log('Decorated');
barBackup.apply($delegate, arguments);
};
return $delegate;
});
});
The example above defines new service called foo
. In the config
callback the method $provide.decorator
is called with first argument "foo"
, which is the name of the service, we want to decorate and second argument factory function, which implements the actual decoration. $delegate
keeps reference to the original service foo
. Using the dependency injection mechanism of AngularJS, reference to this local dependency is passed as first argument of the constructor function.
We decorate the service by overriding its method bar
. The actual decoration is simply extending bar
by invoking one more console.log statement
- console.log('Decorated');
and after that calls the original bar
method with the appropriate context.
Using this pattern is especially useful when we need to modify the functionality of third party services. In cases when multiple similar decorations are required (like performance measurement of multiple methods, authorization, logging, etc.), we may have a lot of duplications and violate the DRY principle. In such cases it is useful to use aspect-oriented programming. The only AOP framework for AngularJS I’m aware of could be found at github.com/mgechev/angular-aop.
Facade
A facade is an object that provides a simplified interface to a larger body of code, such as a class library. A facade can:
- make a software library easier to use, understand and test, since the facade has convenient methods for common tasks;
- make the library more readable, for the same reason;
- reduce dependencies of outside code on the inner workings of a library, since most code uses the facade, thus allowing more flexibility in developing the system;
- wrap a poorly designed collection of APIs with a single well-designed API (as per task needs).
There are a few facades in AngularJS. Each time you want to provide higher level API to given functionality you practically create a facade.
For example, lets take a look at how we can create an XMLHttpRequest
POST request:
var http = new XMLHttpRequest(),
url = '/example/new',
params = encodeURIComponent(data);
http.open("POST", url, true);
http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
http.setRequestHeader("Content-length", params.length);
http.setRequestHeader("Connection", "close");
http.onreadystatechange = function () {
if(http.readyState == 4 && http.status == 200) {
alert(http.responseText);
}
}
http.send(params);
$http
service we can:
$http({
method: 'POST',
url: '/example/new',
data: data
})
.then(function (response) {
alert(response);
});
$http.post('/someUrl', data)
.then(function (response) {
alert(response);
});
Even higher level of abstraction is being created by $resource
, which is build over the $http
service. We will take a further look at this service in Active Record and Proxy sections.
Proxy
A proxy, in its most general form, is a class functioning as an interface to something else. The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate.
We can distinguish three different types of proxy:
- Virtual Proxy
- Remote Proxy
- Protection Proxy
In this sub-chapter we are going to take a look at AngularJS’ implementation of Virtual Proxy.
In the snippet below, there is a call to the get
method of $resource
instance, called User
:
var User = $resource('/users/:id'),
user = User.get({ id: 42 });
console.log(user); //{}
console.log
would output an empty object. Since the AJAX request, which happens behind the scenes, when User.get
is invoked, is asynchronous, we don’t have the actual user when console.log
is called. Just after User.get
makes the GET request, it returns an empty object and keeps reference to it. We can think of this object as virtual proxy (a simple placeholder), which would be populated with the actual data once the client receives response by the server.
How does this works with AngularJS? Well, lets consider the following snippet:
function MainCtrl($scope, $resource) {
var User = $resource('/users/:id'),
$scope.user = User.get({ id: 42 });
}
<span ng-bind="user.name"></span>
user
of the $scope
object will be with value equal to an empty object ({}
), which means that user.name
will be undefined and nothing will be rendered. Internally AngularJS will keep reference to this empty object. Once the server returns response for the GET request, AngularJS will populate the object with the data, received from the server. During the next $digest
loop AngularJS will detect change in $scope.user
, which will lead to update of the view.
Active Record
The Active Record object is an object, which carries both data and behavior. Usually most of the data in these objects is persistent, responsibility of the Active Record object is to take care of the communication with the database in order to create, update, retrieve or delete the data. It may delegate this responsibility to lower level objects but calls to instance or static methods of the active record object cause the database communication.
AngularJS defines a service called $resource
. In the current version of AngularJS (1.2+) it is being distributed in module outside of the AngularJS’ core.
According to the AngularJS’ documentation $resource
is:
A factory which creates a resource object that lets you interact with RESTful server-side data sources. The returned resource object has action methods which provide high-level behaviors without the need to interact with the low level $http service.
Here is how $resource
could be used:
var User = $resource('/users/:id'),
user = new User({
name: 'foo',
age : 42
});
user.$save();
The call of $resource
will create a constructor function for our model instances. Each of the model instances will have methods, which could be used for the different CRUD operations.
This way we can use the constructor function and its static methods by:
User.get({ userid: userid });
The code above will immediately return an empty object and keep reference to it. Once the response has been successfully returned and parsed, AngularJS will populate this object with the received data (see proxy).
You can find more details for $resource
The magic of $resource and AngularJS’ documentation.
Since Martin Fowler states that
responsibility of the Active Record object is to take care of the communication with the databse in order to create…
$resource
does not implement exactly the Active Record pattern, since it communicates with RESTful service instead of the database. Anyway, we can consider it as “Active Record like RESTful communication”.