Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

Minko Gechev github.com/mgechev twitter.com/mgechev { "job": "Freelancer", "hobbies": [ "open source", "blogging", "teaching", "sports" ], "communityEvents": [ "SofiaJS", "BeerJS Sofia" ] }

Slide 3

Slide 3 text

Lets talk about OOP!

Slide 4

Slide 4 text

Why it was invented?

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

Dealing with Complexity

Slide 9

Slide 9 text

Four main principles • Abstraction • Encapsulation • Inheritance • Polymorphism

Slide 10

Slide 10 text

Today we’re mostly going to talk about inheritance

Slide 11

Slide 11 text

…more accurately code reuse

Slide 12

Slide 12 text

Inheritance Code reuse

Slide 13

Slide 13 text

Parent Child

Slide 14

Slide 14 text

GRASP

Slide 15

Slide 15 text

GRASP General Responsibility Assignment Software Principles

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

– Wikipedia “In software engineering, coupling is the manner and degree of interdependence between software modules” Coupling

Slide 19

Slide 19 text

Coupling

Slide 20

Slide 20 text

Loose coupling

Slide 21

Slide 21 text

Parent Child

Slide 22

Slide 22 text

Parent Child

Slide 23

Slide 23 text

Tightly Coupled

Slide 24

Slide 24 text

Code reuse practices • Composition • Inheritance • Mixins

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

Lets talk about JavaScript!

Slide 28

Slide 28 text

Inheritance in JavaScript (quick recap)

Slide 29

Slide 29 text

Two main ways… • Classical Inheritance • Prototypal Inheritance

Slide 30

Slide 30 text

Classical Inheritance (quick recap)

Slide 31

Slide 31 text

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; };

Slide 32

Slide 32 text

new  Child("",  42) function Parent function Child object object prototype prototype __proto__ __proto__

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

Prototypal Inheritance (quick recap)

Slide 39

Slide 39 text

var Parent = { age: 42, getAge: function () { return this.age; } }; var Child = Object.create(Parent); Child.name = 'foobar'; Child.getName = function () { return this.name; };

Slide 40

Slide 40 text

object Parent object Child object __proto__ object __proto__ __proto__

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

Now…lets talk about Angular!

Slide 47

Slide 47 text

Code reuse in… • Directives • Controllers • Services

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

Inheritance… Directive: • Compile function • Controller • Pre-link • Post-link • Link

Slide 51

Slide 51 text

Tightly coupled with the directives interface

Slide 52

Slide 52 text

• Complexity • Longer migration path to Angular 2

Slide 53

Slide 53 text

Directives should be already considered as components

Slide 54

Slide 54 text

Directive Component

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

List Post Vote Label Button Label

Slide 58

Slide 58 text

List Post Vote Label Button Label

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

angular.extend module.controller('ChildCtrl', function ($scope, $controller) { // Initialize the super class and extend it. angular.extend(this, $controller('ParentCtrl', { $scope: $scope })); });

Slide 61

Slide 61 text

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);

Slide 62

Slide 62 text

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);

Slide 63

Slide 63 text

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);

Slide 64

Slide 64 text

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);

Slide 65

Slide 65 text

Sample usage Click me!

Slide 66

Slide 66 text

…what if we kill $scope

Slide 67

Slide 67 text

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);

Slide 68

Slide 68 text

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);

Slide 69

Slide 69 text

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);

Slide 70

Slide 70 text

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);

Slide 71

Slide 71 text

Controller as Syntax Click me! You can click here as well!

Slide 72

Slide 72 text

• module.factory • module.service Services

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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; });

Slide 75

Slide 75 text

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; });

Slide 76

Slide 76 text

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; });

Slide 77

Slide 77 text

No content

Slide 78

Slide 78 text

Angular can’t pass dependencies to the parent service

Slide 79

Slide 79 text

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);

Slide 80

Slide 80 text

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);

Slide 81

Slide 81 text

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);

Slide 82

Slide 82 text

No content

Slide 83

Slide 83 text

But what if we don’t want to register the base service?

Slide 84

Slide 84 text

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);

Slide 85

Slide 85 text

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);

Slide 86

Slide 86 text

module.service module.service('name', function () { this.bar = 'baz'; this.alertAnswer = function () { alert(42); }; });

Slide 87

Slide 87 text

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', […]);

Slide 88

Slide 88 text

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', […]);

Slide 89

Slide 89 text

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', […]);

Slide 90

Slide 90 text

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', […]);

Slide 91

Slide 91 text

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', […]);

Slide 92

Slide 92 text

…but what if have logging

Slide 93

Slide 93 text

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) {} }; });

Slide 94

Slide 94 text

…everywhere…

Slide 95

Slide 95 text

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'); } } }; });

Slide 96

Slide 96 text

…and authorization…

Slide 97

Slide 97 text

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'); } } }; });

Slide 98

Slide 98 text

No content

Slide 99

Slide 99 text

Sometimes the OOP is just not powerful enough

Slide 100

Slide 100 text

No content

Slide 101

Slide 101 text

S
 O
 L
 I
 D

Slide 102

Slide 102 text

Single responsibility
 O
 L
 I
 D

Slide 103

Slide 103 text

Single responsibility
 Open-closed principle
 L
 I
 D

Slide 104

Slide 104 text

Single responsibility
 Open-closed principle
 Liskov substitution
 I
 D

Slide 105

Slide 105 text

Single responsibility
 Open-closed principle
 Liskov substitution
 Interface segregation
 D

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

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'); } } }; });

Slide 109

Slide 109 text

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'); } } }; });

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

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

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

2 out of 5

Slide 122

Slide 122 text

…what if we could…

Slide 123

Slide 123 text

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'); } } }; });

Slide 124

Slide 124 text

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'); } } }; });

Slide 125

Slide 125 text

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... } }; });

Slide 126

Slide 126 text

…and define the additional non-business logic…

Slide 127

Slide 127 text

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

Slide 128

Slide 128 text

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

Slide 129

Slide 129 text

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

Slide 130

Slide 130 text

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

Slide 131

Slide 131 text

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

Slide 132

Slide 132 text

“aroundLogger” is defined as a separate service!

Slide 133

Slide 133 text

No content

Slide 134

Slide 134 text

– 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.”

Slide 135

Slide 135 text

– 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.”

Slide 136

Slide 136 text

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'); } } }; });

Slide 137

Slide 137 text

Just like Batman has his Robin…

Slide 138

Slide 138 text

OOP has its AOP

Slide 139

Slide 139 text

AngularAOP

Slide 140

Slide 140 text

…but what about Angular 2

Slide 141

Slide 141 text

– github.com/wycats/javascript-decorators “Decorators make it possible to annotate and modify classes and properties at design time.” ES7 Decorators

Slide 142

Slide 142 text

ES7 Decorators != Angular Annotations

Slide 143

Slide 143 text

ES7 Decorators != Angular Annotations Annotations are implemented with decorators

Slide 144

Slide 144 text

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'); } } }

Slide 145

Slide 145 text

@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'); } } }

Slide 146

Slide 146 text

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

Slide 147

Slide 147 text

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

Slide 148

Slide 148 text

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); } }

Slide 149

Slide 149 text

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); } }

Slide 150

Slide 150 text

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); } }

Slide 151

Slide 151 text

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); } }

Slide 152

Slide 152 text

No content

Slide 153

Slide 153 text

References • AngularJS Inheritance Patterns • Aspect-Oriented Programming • Aspect-Oriented Programming in AngularJS • SOLID • GRASP • aop.js • ECMAScript 7 Decorators • Object-Oriented Analysis and Design

Slide 154

Slide 154 text

Thank you! github.com/mgechev twitter.com/mgechev blog.mgechev.com