Code Reusability in Angular

Code Reusability in Angular

The presentation illustrates how we can achieve code reuse through different practices in our AngularJS applications.
In its first chapter it introduce some foundational concepts in the object-oriented programming.
In its second part it illustrates how we can take advantage of the standard JavaScript inheritance patterns in our AngularJS components.
The third part of the presentation, introduces the concept of cross-cutting concerns and how we can modularize them using aspect-oriented programming.

This talk was presented at Angular Berlin.

82bafb0432ce4ccc9dcc26f94d5fe5bc?s=128

Minko Gechev

June 10, 2015
Tweet

Transcript

  1. Code Reusability in Angular Minko Gechev github.com/mgechev twitter.com/mgechev blog.mgechev.com https://www.flickr.com/photos/sandy-townsend/6451483999/

  2. Minko Gechev github.com/mgechev twitter.com/mgechev { "job": "Freelancer", "hobbies": [ "open

    source", "blogging", "teaching", "sports" ], "communityEvents": [ "SofiaJS", "BeerJS Sofia" ] }
  3. Lets talk about OOP!

  4. Why it was invented?

  5. https://www.flickr.com/photos/80497765@N00/5676711167/

  6. https://www.flickr.com/photos/alloyjared/16745571959/

  7. None
  8. Dealing with Complexity

  9. Four main principles • Abstraction • Encapsulation • Inheritance •

    Polymorphism
  10. Today we’re mostly going to talk about inheritance

  11. …more accurately code reuse

  12. Inheritance Code reuse

  13. Parent Child

  14. GRASP

  15. GRASP General Responsibility Assignment Software Principles

  16. GRASP • Low Coupling • High Cohesion • Controller •

    Creator • Indirection • Information Expert • Polymorphism • Protected Variations • Pure Fabrication
  17. GRASP • Low Coupling • High Cohesion • Controller •

    Creator • Indirection • Information Expert • Polymorphism • Protected Variations • Pure Fabrication
  18. – Wikipedia “In software engineering, coupling is the manner and

    degree of interdependence between software modules” Coupling
  19. Coupling

  20. Loose coupling

  21. Parent Child

  22. Parent Child

  23. Tightly Coupled

  24. Code reuse practices • Composition • Inheritance • Mixins

  25. None
  26. None
  27. Lets talk about JavaScript!

  28. Inheritance in JavaScript (quick recap)

  29. Two main ways… • Classical Inheritance • Prototypal Inheritance

  30. Classical Inheritance (quick recap)

  31. function Parent(age) { this.age = age; } Parent.prototype.getAge = function

    () { return this.age; }; function Child(name, age) { Parent.call(this, age); this.name = name; } Child.prototype = Object.create(Parent.prototype); Child.prototype.getName = function () { return this.name; };
  32. new  Child("",  42) function Parent function Child object object prototype

    prototype __proto__ __proto__
  33. function Parent function Child object object prototype prototype __proto__ __proto__

    new  Child("",  42).getAge(); new  Child("",  42)
  34. new  Child("",  42) function Parent function Child object object prototype

    prototype __proto__ __proto__ new  Child("",  42).getAge();
  35. function Parent function Child object object prototype prototype __proto__ __proto__

    new  Child("",  42) new  Child("",  42).getAge();
  36. function Parent function Child object object prototype prototype __proto__ __proto__

    new  Child("",  42) new  Child("",  42).getAge();
  37. function Parent function Child object object prototype prototype __proto__ __proto__

    new  Child("",  42) new  Child("",  42).getAge(); 42
  38. Prototypal Inheritance (quick recap)

  39. var Parent = { age: 42, getAge: function () {

    return this.age; } }; var Child = Object.create(Parent); Child.name = 'foobar'; Child.getName = function () { return this.name; };
  40. object Parent object Child object __proto__ object __proto__ __proto__

  41. object Parent object Child object __proto__ object __proto__ __proto__ console.log(Child.getAge());

  42. object Parent object Child object __proto__ object __proto__ __proto__ console.log(Child.getAge());

  43. object Parent object Child object __proto__ object __proto__ __proto__ console.log(Child.getAge());

  44. object Parent object Child object __proto__ object __proto__ __proto__ console.log(Child.getAge());

  45. object Parent object Child object __proto__ object __proto__ __proto__ console.log(Child.getAge());

     //  42
  46. Now…lets talk about Angular!

  47. Code reuse in… • Directives • Controllers • Services

  48. Directives Code reuse in directives: • Inheritance/mixins • Composition

  49. var myModule = angular.module(...); myModule.directive('directiveName', function factory(injectables) { var directiveDefinitionObject

    = { // ... controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, // ... compile: function compile(tElement, tAttrs, transclude) { return { pre: function preLink(scope, iElement, iAttrs, controller) { ... }, post: function postLink(scope, iElement, iAttrs, controller) { ... } } // or // return function postLink( ... ) { ... } }, // or // link: { // pre: function preLink(scope, iElement, iAttrs, controller) { ... }, // post: function postLink(scope, iElement, iAttrs, controller) { ... } // } // or // link: function postLink( ... ) { ... } }; return directiveDefinitionObject; }); Directive Definition
  50. Inheritance… Directive: • Compile function • Controller • Pre-link •

    Post-link • Link
  51. Tightly coupled with the directives interface

  52. • Complexity • Longer migration path to Angular 2

  53. Directives should be already considered as components

  54. Directive Component

  55. None
  56. None
  57. List Post Vote Label Button Label

  58. List Post Vote Label Button Label

  59. Controllers • angular.extend • Classical Inheritance • Controller as Syntax

  60. angular.extend module.controller('ChildCtrl', function ($scope, $controller) { // Initialize the super

    class and extend it. angular.extend(this, $controller('ParentCtrl', { $scope: $scope })); });
  61. Classical Inheritance function BaseCtrl($scope, $location, ...) { $scope.keyupHadler = function

    () { // body... }; $scope.answer = 42; } BaseCtrl.prototype.clickHelper = function () { // body... }; function SectionCtrl($scope, $location) { BaseCtrl.call(this, $scope, $location); $scope.clickHandler = function () { this.clickHelper(); }.bind(this); } SectionCtrl.prototype = Object.create(BaseCtrl.prototype); //Register SectionCtrl as AngularJS controller myModule.controller('SectionCtrl', SectionCtrl);
  62. Classical Inheritance function BaseCtrl($scope, $location, ...) { $scope.keyupHadler = function

    () { // body... }; $scope.answer = 42; } BaseCtrl.prototype.clickHelper = function () { // body... }; function SectionCtrl($scope, $location) { BaseCtrl.call(this, $scope, $location); $scope.clickHandler = function () { this.clickHelper(); }.bind(this); } SectionCtrl.prototype = Object.create(BaseCtrl.prototype); //Register SectionCtrl as AngularJS controller myModule.controller('SectionCtrl', SectionCtrl);
  63. Classical Inheritance function BaseCtrl($scope, $location, ...) { $scope.keyupHadler = function

    () { // body... }; $scope.answer = 42; } BaseCtrl.prototype.clickHelper = function () { // body... }; function SectionCtrl($scope, $location) { BaseCtrl.call(this, $scope, $location); $scope.clickHandler = function () { this.clickHelper(); }.bind(this); } SectionCtrl.prototype = Object.create(BaseCtrl.prototype); //Register SectionCtrl as AngularJS controller myModule.controller('SectionCtrl', SectionCtrl);
  64. Classical Inheritance function BaseCtrl($scope, $location, ...) { $scope.keyupHadler = function

    () { // body... }; $scope.answer = 42; } BaseCtrl.prototype.clickHelper = function () { // body... }; function SectionCtrl($scope, $location) { BaseCtrl.call(this, $scope, $location); $scope.clickHandler = function () { this.clickHelper(); }.bind(this); } SectionCtrl.prototype = Object.create(BaseCtrl.prototype); //Register SectionCtrl as AngularJS controller myModule.controller('SectionCtrl', SectionCtrl);
  65. Sample usage <section ng-controller="SectionCtrl"> <button ng-click="clickHandler()"> Click me! </button> </section>

  66. …what if we kill $scope

  67. Controller as Syntax function BaseCtrl($location, ...) { } BaseCtrl.prototype.clickHelper =

    function () { // body... }; function SectionCtrl($location) { BaseCtrl.call(this, $location); } SectionCtrl.prototype = Object.create(BaseCtrl.prototype); SectionCtrl.prototype.clickHandler = function () { this.clickHelper(); // body ... }; //Register SectionCtrl as AngularJS controller myModule.controller('SectionCtrl', SectionCtrl);
  68. Controller as Syntax function BaseCtrl($location, ...) { } BaseCtrl.prototype.clickHelper =

    function () { // body... }; function SectionCtrl($location) { BaseCtrl.call(this, $location); } SectionCtrl.prototype = Object.create(BaseCtrl.prototype); SectionCtrl.prototype.clickHandler = function () { this.clickHelper(); // body ... }; //Register SectionCtrl as AngularJS controller myModule.controller('SectionCtrl', SectionCtrl);
  69. Controller as Syntax function BaseCtrl($location, ...) { } BaseCtrl.prototype.clickHelper =

    function () { // body... }; function SectionCtrl($location) { BaseCtrl.call(this, $location); } SectionCtrl.prototype = Object.create(BaseCtrl.prototype); SectionCtrl.prototype.clickHandler = function () { this.clickHelper(); // body ... }; //Register SectionCtrl as AngularJS controller myModule.controller('SectionCtrl', SectionCtrl);
  70. Controller as Syntax function BaseCtrl($location, ...) { } BaseCtrl.prototype.clickHelper =

    function () { // body... }; function SectionCtrl($location) { BaseCtrl.call(this, $location); } SectionCtrl.prototype = Object.create(BaseCtrl.prototype); SectionCtrl.prototype.clickHandler = function () { this.clickHelper(); // body ... }; //Register SectionCtrl as AngularJS controller myModule.controller('SectionCtrl', SectionCtrl);
  71. Controller as Syntax <section ng-controller="SectionCtrl"> <button ng-click="clickHandler()"> Click me! </button>

    <button ng-click="clickHelper()"> You can click here as well! </button> </section>
  72. • module.factory • module.service Services

  73. module.factory module.factory('name', function () { return { // ... };

    });
  74. module.factory inheritance var Collection = (function () { var totalItems

    = 0; return { add: function (item) { // body }, remove: function (item) { // body } }; }()); var ArticleCollection = Object.create(Collection); ArticleCollection._items = []; module.factory('ArticleCollection', function ($http) { ArticleCollection.fetchAll = function () { $http.get('/articles') .then(function () { // ... }); }; return ArticleCollection; });
  75. module.factory inheritance var Collection = (function () { var totalItems

    = 0; return { add: function (item) { // body }, remove: function (item) { // body } }; }()); var ArticleCollection = Object.create(Collection); ArticleCollection._items = []; module.factory('ArticleCollection', function ($http) { ArticleCollection.fetchAll = function () { $http.get('/articles') .then(function () { // ... }); }; return ArticleCollection; });
  76. module.factory inheritance var Collection = (function () { var totalItems

    = 0; return { add: function (item) { // body }, remove: function (item) { // body } }; }()); var ArticleCollection = Object.create(Collection); ArticleCollection._items = []; module.factory('ArticleCollection', function ($http) { ArticleCollection.fetchAll = function () { $http.get('/articles') .then(function () { // ... }); }; return ArticleCollection; });
  77. None
  78. Angular can’t pass dependencies to the parent service

  79. module.factory inheritance function CollectionFactory($log) { return { // API };

    } module.factory('Collection', CollectionFactory); function ArticleCollectionFactory(Collection, $http) { var ArticleCollection = Object.create(Collection); ArticleCollection._items = []; ArticleCollection.fetchAll = function () { $http.get('/articles') .then(function () { // ... }); }; return ArticleCollection; } module.factory('ArticleCollection', ArticleCollectionFactory);
  80. module.factory inheritance function CollectionFactory($log) { return { // API };

    } module.factory('Collection', CollectionFactory); function ArticleCollectionFactory(Collection, $http) { var ArticleCollection = Object.create(Collection); ArticleCollection._items = []; ArticleCollection.fetchAll = function () { $http.get('/articles') .then(function () { // ... }); }; return ArticleCollection; } module.factory('ArticleCollection', ArticleCollectionFactory);
  81. module.factory inheritance function CollectionFactory($log) { return { // API };

    } module.factory('Collection', CollectionFactory); function ArticleCollectionFactory(Collection, $http) { var ArticleCollection = Object.create(Collection); ArticleCollection._items = []; ArticleCollection.fetchAll = function () { $http.get('/articles') .then(function () { // ... }); }; return ArticleCollection; } module.factory('ArticleCollection', ArticleCollectionFactory);
  82. None
  83. But what if we don’t want to register the base

    service?
  84. module.factory inheritance function CollectionFactory($log) { return { // API };

    } function ArticleCollectionFactory($injector, $http) { var Collection = $injector.invoke(CollectionFactory); var ArticleCollection = Object.create(Collection); ArticleCollection._items = []; ArticleCollection.fetchAll = function () { $http.get('/articles') .then(function () { // ... }); }; return ArticleCollection; } module.factory('ArticleCollection', ArticleCollectionFactory);
  85. module.factory inheritance function CollectionFactory($log) { return { // API };

    } function ArticleCollectionFactory($injector, $http) { var Collection = $injector.invoke(CollectionFactory); var ArticleCollection = Object.create(Collection); ArticleCollection._items = []; ArticleCollection.fetchAll = function () { $http.get('/articles') .then(function () { // ... }); }; return ArticleCollection; } module.factory('ArticleCollection', ArticleCollectionFactory);
  86. module.service module.service('name', function () { this.bar = 'baz'; this.alertAnswer =

    function () { alert(42); }; });
  87. module.service inheritance function Media($http) { // ... } Media.prototype.fetch =

    function () { // ... }; Media.$inject = ['$http']; function Video($http, CODECS) { Media.call(this, $http); } Video.prototype = Object.create(Media.prototype); Video.prototype.getScreenshot = function (time) { // ... }; Video.$inject = ['$http', 'CODECS']; angular.module('demo').service('Media', Media); angular.module('demo').service('Video', Video); angular.module('demo').value('CODECS', […]);
  88. module.service inheritance function Media($http) { // ... } Media.prototype.fetch =

    function () { // ... }; Media.$inject = ['$http']; function Video($http, CODECS) { Media.call(this, $http); } Video.prototype = Object.create(Media.prototype); Video.prototype.getScreenshot = function (time) { // ... }; Video.$inject = ['$http', 'CODECS']; angular.module('demo').service('Media', Media); angular.module('demo').service('Video', Video); angular.module('demo').value('CODECS', […]);
  89. module.service inheritance function Media($http) { // ... } Media.prototype.fetch =

    function () { // ... }; Media.$inject = ['$http']; function Video($http, CODECS) { Media.call(this, $http); } Video.prototype = Object.create(Media.prototype); Video.prototype.getScreenshot = function (time) { // ... }; Video.$inject = ['$http', 'CODECS']; angular.module('demo').service('Media', Media); angular.module('demo').service('Video', Video); angular.module('demo').value('CODECS', […]);
  90. module.service inheritance function Media($http) { // ... } Media.prototype.fetch =

    function () { // ... }; Media.$inject = ['$http']; function Video($http, CODECS) { Media.call(this, $http); } Video.prototype = Object.create(Media.prototype); Video.prototype.getScreenshot = function (time) { // ... }; Video.$inject = ['$http', 'CODECS']; angular.module('demo').service('Media', Media); angular.module('demo').service('Video', Video); angular.module('demo').value('CODECS', […]);
  91. module.service inheritance function Media($http) { // ... } Media.prototype.fetch =

    function () { // ... }; Media.$inject = ['$http']; function Video($http, CODECS) { Media.call(this, $http); } Video.prototype = Object.create(Media.prototype); Video.prototype.getScreenshot = function (time) { // ... }; Video.$inject = ['$http', 'CODECS']; angular.module('demo').service('Media', Media); angular.module('demo').service('Video', Video); angular.module('demo').value('CODECS', […]);
  92. …but what if have logging

  93. Logging module.factory('ArticleCollection', function ($log, $resource) { return { getArticle: function

    (id) { $log.log('getArticle called with', arguments); try { // body... } catch (e) { $log.error('getArticle threw an error', e); } finally { $log.log('getArticle execution completed'); } }, getArticles: function () {}, removeArticle: function (id) {}, updateArticle: function (id, article) {} }; });
  94. …everywhere…

  95. Logging module.factory('ArticleCollection', function ($log, $resource) { return { getArticle: function

    (id) { $log.log('getArticle called with', arguments); try { // body... } catch (e) { $log.error('getArticle threw an error', e); } finally { $log.log('getArticle execution completed'); } }, getArticles: function () { $log.log('getArticles called with', arguments); try { // body... } catch (e) { $log.error('getArticles threw an error', e); } finally { $log.log('getArticles execution completed'); } }, removeArticle: function (id) { $log.log('removeArticle called with', arguments); try { // body... } catch (e) { $log.error('removeArticle threw an error', e); } finally { $log.log('removeArticle execution completed'); } }, updateArticle: function (id, article) { $log.log('updateArticle called with', arguments); try { // body... } catch (e) { $log.error('updateArticle threw an error', e); } finally { $log.log('updateArticle execution completed'); } } }; });
  96. …and authorization…

  97. Logging & Authorization module.factory('ArticleCollection', function ($log, $resource, Authorization) { return

    { getArticle: function (id) { $log.log('getArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('getArticle threw an error', e); } finally { $log.log('getArticle execution completed'); } }, getArticles: function () { $log.log('getArticles called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('getArticles threw an error', e); } finally { $log.log('getArticles execution completed'); } }, removeArticle: function (id) { $log.log('removeArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('removeArticle threw an error', e); } finally { $log.log('removeArticle execution completed'); } }, updateArticle: function (id, article) { $log.log('updateArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('updateArticle threw an error', e); } finally { $log.log('updateArticle execution completed'); } } }; });
  98. None
  99. Sometimes the OOP is just not powerful enough

  100. None
  101. S
 O
 L
 I
 D

  102. Single responsibility
 O
 L
 I
 D

  103. Single responsibility
 Open-closed principle
 L
 I
 D

  104. Single responsibility
 Open-closed principle
 Liskov substitution
 I
 D

  105. Single responsibility
 Open-closed principle
 Liskov substitution
 Interface segregation
 D

  106. Single responsibility
 Open-closed principle
 Liskov substitution
 Interface segregation
 Dependency Inversion

  107. Single responsibility
 Open-closed principle
 Liskov substitution
 Interface segregation
 Dependency Inversion

  108. Logging & Authorization module.factory('ArticleCollection', function ($log, $resource, Authorization) { return

    { getArticle: function (id) { $log.log('getArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('getArticle threw an error', e); } finally { $log.log('getArticle execution completed'); } }, getArticles: function () { $log.log('getArticles called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('getArticles threw an error', e); } finally { $log.log('getArticles execution completed'); } }, removeArticle: function (id) { $log.log('removeArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('removeArticle threw an error', e); } finally { $log.log('removeArticle execution completed'); } }, updateArticle: function (id, article) { $log.log('updateArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('updateArticle threw an error', e); } finally { $log.log('updateArticle execution completed'); } } }; });
  109. Logging & Authorization module.factory('ArticleCollection', function ($log, $resource, Authorization) { return

    { getArticle: function (id) { $log.log('getArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('getArticle threw an error', e); } finally { $log.log('getArticle execution completed'); } }, getArticles: function () { $log.log('getArticles called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('getArticles threw an error', e); } finally { $log.log('getArticles execution completed'); } }, removeArticle: function (id) { $log.log('removeArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('removeArticle threw an error', e); } finally { $log.log('removeArticle execution completed'); } }, updateArticle: function (id, article) { $log.log('updateArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('updateArticle threw an error', e); } finally { $log.log('updateArticle execution completed'); } } }; });
  110. Single responsibility
 Open-closed principle
 Liskov substitution
 Interface segregation
 Dependency Inversion

  111. Single responsibility
 Open-closed principle
 Liskov substitution
 Interface segregation
 Dependency Inversion

  112. Single responsibility
 Open-closed principle
 Liskov substitution
 Interface segregation
 Dependency Inversion

  113. Single responsibility
 Open-closed principle
 Liskov substitution
 Interface segregation
 Dependency Inversion

  114. Single responsibility
 Open-closed principle
 Liskov substitution
 Interface segregation
 Dependency Inversion

  115. Single responsibility
 Open-closed principle
 Liskov substitution
 Interface segregation
 Dependency Inversion

  116. Single responsibility
 Open-closed principle
 Liskov substitution
 Interface segregation
 Dependency Inversion

  117. Single responsibility
 Open-closed principle
 Liskov substitution
 Interface segregation
 Dependency Inversion

  118. Single responsibility
 Open-closed principle
 Liskov substitution
 Interface segregation
 Dependency Inversion

  119. Single responsibility
 Open-closed principle
 Liskov substitution
 Interface segregation
 Dependency Inversion

  120. Single responsibility
 Open-closed principle
 Liskov substitution
 Interface segregation
 Dependency Inversion

  121. 2 out of 5

  122. …what if we could…

  123. Logging & Authorization module.factory('ArticleCollection', function ($log, $resource, Authorization) { return

    { getArticle: function (id) { $log.log('getArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('getArticle threw an error', e); } finally { $log.log('getArticle execution completed'); } }, getArticles: function () { $log.log('getArticles called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('getArticles threw an error', e); } finally { $log.log('getArticles execution completed'); } }, removeArticle: function (id) { $log.log('removeArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('removeArticle threw an error', e); } finally { $log.log('removeArticle execution completed'); } }, updateArticle: function (id, article) { $log.log('updateArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('updateArticle threw an error', e); } finally { $log.log('updateArticle execution completed'); } } }; });
  124. Logging & Authorization module.factory('ArticleCollection', function ($log, $resource, Authorization) { return

    { getArticle: function (id) { $log.log('getArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('getArticle threw an error', e); } finally { $log.log('getArticle execution completed'); } }, getArticles: function () { $log.log('getArticles called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('getArticles threw an error', e); } finally { $log.log('getArticles execution completed'); } }, removeArticle: function (id) { $log.log('removeArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('removeArticle threw an error', e); } finally { $log.log('removeArticle execution completed'); } }, updateArticle: function (id, article) { $log.log('updateArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('updateArticle threw an error', e); } finally { $log.log('updateArticle execution completed'); } } }; });
  125. cross-cutting concerns module.factory('ArticleCollection', function ($resource) { return { getArticle: function

    (id) { // body... }, getArticles: function () { // body... }, removeArticle: function (id) { // body... }, updateArticle: function (id, article) { // body... } }; });
  126. …and define the additional non-business logic…

  127. Aspect definition module.config(function ($provide, executeProvider) { executeProvider.annotate($provide, { ArticleCollection: [{

    jointPoint: executeProvider.AROUND, advice: 'aroundLogger', methodPattern: /.*/ }, { jointPoint: executeProvider.ON_THROW, advice: 'onThowAdvice', methodPattern: /.*/ }] }); });
  128. module.config(function ($provide, executeProvider) { executeProvider.annotate($provide, { ArticleCollection: [{ jointPoint: executeProvider.AROUND,

    advice: 'aroundLogger', methodPattern: /.*/ }, { jointPoint: executeProvider.ON_THROW, advice: 'onThowAdvice', methodPattern: /.*/ }] }); }); Aspect definition
  129. module.config(function ($provide, executeProvider) { executeProvider.annotate($provide, { ArticleCollection: [{ jointPoint: executeProvider.AROUND,

    advice: 'aroundLogger', methodPattern: /.*/ }, { jointPoint: executeProvider.ON_THROW, advice: 'onThowAdvice', methodPattern: /.*/ }] }); }); Aspect definition
  130. module.config(function ($provide, executeProvider) { executeProvider.annotate($provide, { ArticleCollection: [{ jointPoint: executeProvider.AROUND,

    advice: 'aroundLogger', methodPattern: /.*/ }, { jointPoint: executeProvider.ON_THROW, advice: 'onThowAdvice', methodPattern: /.*/ }] }); }); Aspect definition
  131. module.config(function ($provide, executeProvider) { executeProvider.annotate($provide, { ArticleCollection: [{ jointPoint: executeProvider.AROUND,

    advice: 'aroundLogger', methodPattern: /.*/ }, { jointPoint: executeProvider.ON_THROW, advice: 'onThowAdvice', methodPattern: /.*/ }] }); }); Aspect definition
  132. “aroundLogger” is defined as a separate service!

  133. None
  134. – Wikipedia “In computing, aspect-oriented programming (AOP) is a patented

    programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns.”
  135. – Wikipedia “In computing, aspect-oriented programming (AOP) is a patented

    programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns.”
  136. Logging & Authorization module.factory('ArticleCollection', function ($log, $resource, Authorization) { return

    { getArticle: function (id) { $log.log('getArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('getArticle threw an error', e); } finally { $log.log('getArticle execution completed'); } }, getArticles: function () { $log.log('getArticles called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('getArticles threw an error', e); } finally { $log.log('getArticles execution completed'); } }, removeArticle: function (id) { $log.log('removeArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('removeArticle threw an error', e); } finally { $log.log('removeArticle execution completed'); } }, updateArticle: function (id, article) { $log.log('updateArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { $log.error('updateArticle threw an error', e); } finally { $log.log('updateArticle execution completed'); } } }; });
  137. Just like Batman has his Robin…

  138. OOP has its AOP

  139. AngularAOP

  140. …but what about Angular 2

  141. – github.com/wycats/javascript-decorators “Decorators make it possible to annotate and modify

    classes and properties at design time.” ES7 Decorators
  142. ES7 Decorators != Angular Annotations

  143. ES7 Decorators != Angular Annotations Annotations are implemented with decorators

  144. export default class ArticleCollection { getArticle(id) { logger.log('getArticle called with',

    arguments); try { Authorization.authorize(); // body... } catch (e) { logger.error('getArticle threw an error', e); } finally { logger.log('getArticle execution completed'); } } getArticles() { logger.log('getArticles called with', arguments); try { Authorization.authorize(); // body... } catch (e) { logger.error('getArticles threw an error', e); } finally { logger.log('getArticles execution completed'); } } removeArticle(id) { logger.log('removeArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { logger.error('removeArticle threw an error', e); } finally { logger.log('removeArticle execution completed'); } } updateArticle(id, article) { logger.log('updateArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { logger.error('updateArticle threw an error', e); } finally { logger.log('updateArticle execution completed'); } } }
  145. @Wove export default class ArticleCollection { getArticle(id) { logger.log('getArticle called

    with', arguments); try { Authorization.authorize(); // body... } catch (e) { logger.error('getArticle threw an error', e); } finally { logger.log('getArticle execution completed'); } } getArticles() { logger.log('getArticles called with', arguments); try { Authorization.authorize(); // body... } catch (e) { logger.error('getArticles threw an error', e); } finally { logger.log('getArticles execution completed'); } } removeArticle(id) { logger.log('removeArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { logger.error('removeArticle threw an error', e); } finally { logger.log('removeArticle execution completed'); } } updateArticle(id, article) { logger.log('updateArticle called with', arguments); try { Authorization.authorize(); // body... } catch (e) { logger.error('updateArticle threw an error', e); } finally { logger.log('updateArticle execution completed'); } } }
  146. Angular 2 Service @Wove export default class ArticleCollection { getArticle(id)

    { // body... } getArticles() { // body... } removeArticle(id) { // body... } updateArticle(id, article) { // body... } }
  147. Angular 2 Service @Wove export default class ArticleCollection { getArticle(id)

    { // body... } getArticles() { // body... } removeArticle(id) { // body... } updateArticle(id, article) { // body... } }
  148. AOP.js Aspect @Aspect class LoggingAspect { @Before(/.*/, /.*/) before(args) {

    console.log(`${args.name} called with ${args.args}`); } @After(/.*/, /.*/) after(args) { console.log(`${args.name} execution completed`); } @OnThow(/.*/, /.*/) errorHandler(args) { console.error(`${args.name} threw an error`, args.error); } }
  149. AOP.js Aspect @Aspect class LoggingAspect { @Before(/.*/, /.*/) before(args) {

    console.log(`${args.name} called with ${args.args}`); } @After(/.*/, /.*/) after(args) { console.log(`${args.name} execution completed`); } @OnThow(/.*/, /.*/) errorHandler(args) { console.error(`${args.name} threw an error`, args.error); } }
  150. AOP.js Aspect @Aspect class LoggingAspect { @Before(/.*/, /.*/) before(args) {

    console.log(`${args.name} called with ${args.args}`); } @After(/.*/, /.*/) after(args) { console.log(`${args.name} execution completed`); } @OnThow(/.*/, /.*/) errorHandler(args) { console.error(`${args.name} threw an error`, args.error); } }
  151. AOP.js Aspect @Aspect class LoggingAspect { @Before(/.*/, /.*/) before(args) {

    console.log(`${args.name} called with ${args.args}`); } @After(/.*/, /.*/) after(args) { console.log(`${args.name} execution completed`); } @OnThow(/.*/, /.*/) errorHandler(args) { console.error(`${args.name} threw an error`, args.error); } }
  152. None
  153. References • AngularJS Inheritance Patterns • Aspect-Oriented Programming • Aspect-Oriented

    Programming in AngularJS • SOLID • GRASP • aop.js • ECMAScript 7 Decorators • Object-Oriented Analysis and Design
  154. Thank you! github.com/mgechev twitter.com/mgechev blog.mgechev.com