Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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.

Minko Gechev

June 10, 2015
Tweet

More Decks by Minko Gechev

Other Decks in Programming

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/

    View Slide

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

    View Slide

  3. Lets talk about OOP!

    View Slide

  4. Why it was invented?

    View Slide

  5. https://www.flickr.com/photos/[email protected]/5676711167/

    View Slide

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

    View Slide

  7. View Slide

  8. Dealing with Complexity

    View Slide

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

    View Slide

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

    View Slide

  11. …more accurately
    code reuse

    View Slide

  12. Inheritance Code reuse

    View Slide

  13. Parent
    Child

    View Slide

  14. GRASP

    View Slide

  15. GRASP
    General Responsibility Assignment Software Principles

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  19. Coupling

    View Slide

  20. Loose coupling

    View Slide

  21. Parent
    Child

    View Slide

  22. Parent
    Child

    View Slide

  23. Tightly Coupled

    View Slide

  24. Code reuse practices
    • Composition
    • Inheritance
    • Mixins

    View Slide

  25. View Slide

  26. View Slide

  27. Lets talk about JavaScript!

    View Slide

  28. Inheritance in JavaScript
    (quick recap)

    View Slide

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

    View Slide

  30. Classical Inheritance
    (quick recap)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  38. Prototypal Inheritance
    (quick recap)

    View Slide

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

    View Slide

  40. object Parent
    object Child
    object
    __proto__
    object
    __proto__
    __proto__

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  46. Now…lets talk about
    Angular!

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  51. Tightly coupled with the directives
    interface

    View Slide

  52. • Complexity
    • Longer migration path to Angular 2

    View Slide

  53. Directives should be already
    considered as components

    View Slide

  54. Directive
    Component

    View Slide

  55. View Slide

  56. View Slide

  57. List
    Post
    Vote
    Label
    Button
    Label

    View Slide

  58. List
    Post
    Vote
    Label
    Button
    Label

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  65. Sample usage


    Click me!


    View Slide

  66. …what if we kill $scope

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  71. Controller as Syntax


    Click me!


    You can click here as well!


    View Slide

  72. • module.factory
    • module.service
    Services

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  77. View Slide

  78. Angular can’t pass dependencies
    to the parent service

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  82. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  92. …but what if have logging

    View Slide

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

    View Slide

  94. …everywhere…

    View Slide

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

    View Slide

  96. …and authorization…

    View Slide

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

    View Slide

  98. View Slide

  99. Sometimes the OOP is just
    not powerful enough

    View Slide

  100. View Slide

  101. S

    O

    L

    I

    D

    View Slide

  102. Single responsibility

    O

    L

    I

    D

    View Slide

  103. Single responsibility

    Open-closed principle

    L

    I

    D

    View Slide

  104. Single responsibility

    Open-closed principle

    Liskov substitution

    I

    D

    View Slide

  105. Single responsibility

    Open-closed principle

    Liskov substitution

    Interface segregation

    D

    View Slide

  106. Single responsibility

    Open-closed principle

    Liskov substitution

    Interface segregation

    Dependency Inversion

    View Slide

  107. Single responsibility

    Open-closed principle

    Liskov substitution

    Interface segregation

    Dependency Inversion

    View Slide

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

    View Slide

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

    View Slide

  110. Single responsibility

    Open-closed principle

    Liskov substitution

    Interface segregation

    Dependency Inversion

    View Slide

  111. Single responsibility

    Open-closed principle

    Liskov substitution

    Interface segregation

    Dependency Inversion

    View Slide

  112. Single responsibility

    Open-closed principle

    Liskov substitution

    Interface segregation

    Dependency Inversion

    View Slide

  113. Single responsibility

    Open-closed principle

    Liskov substitution

    Interface segregation

    Dependency Inversion

    View Slide

  114. Single responsibility

    Open-closed principle

    Liskov substitution

    Interface segregation

    Dependency Inversion

    View Slide

  115. Single responsibility

    Open-closed principle

    Liskov substitution

    Interface segregation

    Dependency Inversion

    View Slide

  116. Single responsibility

    Open-closed principle

    Liskov substitution

    Interface segregation

    Dependency Inversion

    View Slide

  117. Single responsibility

    Open-closed principle

    Liskov substitution

    Interface segregation

    Dependency Inversion

    View Slide

  118. Single responsibility

    Open-closed principle

    Liskov substitution

    Interface segregation

    Dependency Inversion

    View Slide

  119. Single responsibility

    Open-closed principle

    Liskov substitution

    Interface segregation

    Dependency Inversion

    View Slide

  120. Single responsibility

    Open-closed principle

    Liskov substitution

    Interface segregation

    Dependency Inversion

    View Slide

  121. 2 out of 5

    View Slide

  122. …what if we could…

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  126. …and define the additional
    non-business logic…

    View Slide

  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: /.*/
    }]
    });
    });

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  132. “aroundLogger” is defined as a
    separate service!

    View Slide

  133. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  137. Just like Batman has his Robin…

    View Slide

  138. OOP has its AOP

    View Slide

  139. AngularAOP

    View Slide

  140. …but what about Angular 2

    View Slide

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

    View Slide

  142. ES7 Decorators != Angular Annotations

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  152. View Slide

  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

    View Slide

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

    View Slide