RSS

OOP in Angular

21 יול

אחד הדברים שאני מקפיד עליהם ב- Code Review שאני עושה בפרויקטים של Angular, זה לא להשתמש ב- $scope, במקום זה להשתמש ב- this. ראו פוסט בנושא. בעקבות שיחות רבות עם לקוחות הבנתי שאני צריך יותר לשכנע ולהראות דוגמא מלאה. בפוסט זה אני אראה איך יוצרים ירושה של controller? ולמה צריך ירושה, אם יש לנו DI?

למה צריך ירושה ב- controller? אנחנו יכולים להזריק לו איזה שרות שאנחנו רוצים.

למעשה השאלה כאן למה להשתמש בירושה אם אפשר להשתמש בהכלה?

 

תשובה:

בו נניח שיש לנו כמה סוגים של proxies, כמו למשל userProxy, carProxy ולשניהם יש את המתודות: load,save.

הקוד של ה- controller יראה כך:

(function(angular) {

    'use strict';

    //////////////// AngularJS //////////////

    var mi = angular.module('myApp', []);

    mi.controller('UserCtrl', UserCtrl);

    mi.controller('CarCtrl', CarCtrl);

 

    //////////////// JavaScript //////////////

 

    function UserCtrl($scope, userProxy) {

        $scope.items = [];

        $scope.load = function() {

            $scope.items = userProxy.load();

        };

    }

    function CarCtrl($scope, carProxy) {

        $scope.items = [];

        $scope.load = function () {

            $scope.items = carProxy.load();

        };

    }

})(angular);

מהסתכלות על הקוד אנחנו רואים שיש המון מהמשותף בין ה- controllers, ולכן נבנה BaseCtrl.

(function(angular) {

    'use strict';

    //////////////// AngularJS //////////////

    var mi = angular.module('myApp', []);

    mi.controller('UserCtrl', UserCtrl);

    mi.controller('CarCtrl', CarCtrl);

    //////////////// JavaScript //////////////

    function BaseCtrl(proxy) {

        this._proxy = proxy;

        this.items = [];

    }

    BaseCtrl.prototype.load = function() {

        this.items = this._proxy.load();

    };

 

    function UserCtrl(userProxy) {

        BaseCtrl.call(this, userProxy);

    }

    UserCtrl.prototype = Object.create(BaseCtrl.prototype);

 

    function CarCtrl(carProxy) {

        BaseCtrl.call(this, carProxy);

    }

    CarCtrl.prototype = Object.create(BaseCtrl.prototype);

  

})(angular);

הסברים:

* כל ההסברים הם על UserCtrl, אך זה תופס גם לגבי CarCtrl.

1. השם מתחיל באות גדולה (UserCtrl) כדי להדגיש את הצורך לפעיל את הפונקציה ע"י new.

2. בתוך הפונקציה UserCtrl אני קורה קודם ל- BaseCtrl, עם ה- this של UserCtrl.

3. כל Controller מבקש את ה- proxy שהוא צריך מה- DI, למשל ה- UserCtrl מקבל את ה- userProxy. הפרוקסי מוכנס ל- BaseCtrl. אפשר להגיד שכל הפרוקסים מממשים את אותו "Interface" ולכן ה- BaseCtrl יכול לעבוד אם כל סוגי הפרוקסי שמממשים את המתודות: load ו- save.

4. שימו לב שחתימת המתודות היא רק בכמות הארגומטים ולא בסוג.

 

אם כל הפרוקסים צריכים למממש את המתודות של load ו- save, למה שלא יהיה להם גם BaseProxy?

לדוגמא:

(function (angular) {

    'use strict';

    //////////////// AngularJS //////////////

    var mi = angular.module('myApp', []);

    mi.controller('UserCtrl', UserCtrl);

    mi.controller('CarCtrl', CarCtrl);

 

    mi.service('carProxy', CarProxy);

    mi.service('userProxy', UserProxy);

 

    //////////////// JavaScript //////////////

   

   

 

    function BaseProxy(data) {

        this.items = data;

    }

    BaseProxy.prototype.load = function() {

        return this.items;

    };

 

    function CarProxy($log) {

       BaseProxy.call(this, [{ id: 1, name: 'BMV', model: 1971 }]);

    }

    CarProxy.prototype = Object.create(BaseProxy.prototype);

 

    function UserProxy($log) {

        BaseProxy.call(this, [{ id: 1, name: 'Eyal', age: 1971 }]);

    }

    UserProxy.prototype = Object.create(BaseProxy.prototype);

 

})(angular);

 

הסברים:

שני הפרוקסי יורשים מ- BaseProxy, במקום לכתוב את המתודות כל פעם מחדש. ב-JS אין לנו Types ואין לנו העמסת מתודות ולכן המצבים שאנחנו יכולים "לעלות קוד" ל- Base Class רבים יותר, וחבל לא לנצל את היכולות האלו.

 

אשמח לשמוע את דעתכם J

Advertisements
 
9 תגובות

פורסם ע"י ב- יולי 21, 2014 ב- AngularJS

 

9 תגובות ל-“OOP in Angular

  1. Lee Elenbaas (@LeeElenbaas)

    יולי 22, 2014 at 5:15 am

    זה אפילו נכון עוד יותר אם משתמשים בסביבה התומכת בירושה בצורה נוחה יותר: TypeScript, Dart & Traceur

     
  2. chenreuven

    יולי 23, 2014 at 2:02 am

    קודם כל תודה על הפוסט המעניין.
    מכיון שאני מתחיל באנגולר אז ייתכן ומה שאני יאמר כאן לא הכי מדויק אז אשמח לתגובה.

    1.לפי מיטב הבנתי ה- controllers אמורים להיות mini-brain ועל כן הם אמורים להיות קטנים מאוד(ביצוע ל- services ואולי מפסר פונקציות קטן), ועל כן מבחינת קריאות של קוד לדעתי לא חייב להשתמש ב- OOP.(הקוד הראשון קריא הרבה יותר מהקוד של oop, מה לעשות :)).
    2. בקשר ל- service אני מסכים לחלוטין וזה נראה אחלה דרך.
    3. בקשר להורשה שעשית לטעמי שכחת לחבר בחזרה את ה- constructor של UserCtrl ו- CarCtrl (גם ב- services). אתה רשמת בעצמך שהאות הגדולה זה להבין שניתן לבצע אתחול של אובייקט חדש באמצעות New.
    4. אתה יכול להסביר את המשפט: "שימו לב שחתימת המתודות היא רק בכמות הארגומטים ולא בסוג." – אין בב- JS התייחסות ל- type בכללי, ולפי מה שהבנתי כמות הארגומנטים, זה אומר ה- Dependency שאתה רוצה להיות תלוי בהם. – אולי לא הבנתי את זה נכון.

    שוב פעם, תודה על הפוסט ולחוקת הידע, הצורת חשיבה היא שונה(בקשר ל- this, בכלל לא ידעתי שזה אפשרי).

    חן

     
    • eyal.vardi

      יולי 23, 2014 at 5:53 am

      תודה על הפידבק.
      1. אתה צודק, רצוי לשמור על קונטרולים קטנים, אך זה לא תמיד יוצא. אם אתה מזה מכנה משותף בין קונטרלים הפתרון הנ"ל יכול לעזור.
      2. 🙂
      3. צודק.
      4. צודק, אך אצלי באבא אני לא עובד על ה-DI. כלומר חשוב הסדר. הסיבה כהילדים מופעלים ע"י ה-DI ואז הם בוחרים מה לעביר לאבא. בגלל שבJS
      אין טיפוסים אז אפשר לעביר מה שרוצים אך צריך לזכור שהאבא מצפה שיהיה להם מתודות מסוימות. אפשר להגיד שזה סוג של גנריק.

       
  3. chenreuven

    יולי 23, 2014 at 2:12 am

    עוד משהו נוסף:
    האם היה אפשר להשתמש ב-
    $injector.invoke(CarController, this, {$scope: $scope});
    ?

     
    • eyal.vardi

      יולי 23, 2014 at 5:54 am

      כן אפשר.

       
      • chenreuven

        יולי 23, 2014 at 3:56 pm

        מה ההבדדל ת'כלס בין injector לשיטת הורשה שאתה רשמת (שאותה אני יותר אוהב :)).
        perfomece,נוחות כתיבה, משהו אחר?

        תודה על פידבק בחזרה 🙂
        אחלה בלוג

         
  4. grekai

    דצמבר 1, 2014 at 2:27 am

    http://jsfiddle.net/2bhere4u/k26rrfm3/1/
    איך אני מציג את הitems ללא scope בצורה כזאת ?
    כמוכן new UserProxy on init
    סתכל בconsole
    הנתונים עוברים אבל לא מוצגים .. לדעתי ה SCOPE עדיין חסר אבל אם אני מכניס אותו נאבד את הקונספט שלך של PURE.

     

כתיבת תגובה

הזינו את פרטיכם בטופס, או לחצו על אחד מהאייקונים כדי להשתמש בחשבון קיים:

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת / לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת / לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת / לשנות )

תמונת גוגל פלוס

אתה מגיב באמצעות חשבון Google+ שלך. לצאת מהמערכת / לשנות )

מתחבר ל-%s

 
%d בלוגרים אהבו את זה: