68

Objective

Commencez par la page Installation d'Objective.js.

La classe Objective est à la racine de toutes les classes Objective.js.

Une instance de Objective peut déléguer l'exécution de méthodes à une autre instance, transmettre l'exécution d'une méthode à une autre instance qui peut automatiquement la redistribuer, transmettre l'exécution d'une méthode à une autre instance qui peut directement y répondre ou automatiquement la redistribuer, notifier de transmettre l'exécution d'une méthode à des écouteurs qui peuvent directement y répondre ou automatiquement la redistribuer.

Une instance de Objective peut se cloner.

NOTE : Les classes Undo et Validator sont indépendantes et peuvent être utilisées sans Objective.js dans tout projet en JavaScript.

  1. function Objective() {
  2. }

Crée une instance de la classe Objective.

  1. Objective.prototype.clone = function() {
  2.     return Object.assign(Object.create(this), this);
  3. };

clone retourne une nouvelle instance identique à this. IMPORTANT : clone copie les valeurs de toutes les propriétés de this. Si une valeur est une référence à un objet, le clone partagera la référence.

Voir l'article Object.assign() sur le site MDN Web Docs.

  1. Objective.prototype.delegate = function(f = null, ...args) {
  2.     let d = this._delegate;
  3.  
  4.     if (f === null)
  5.         return d;
  6.  
  7.     if (d === undefined)
  8.         return undefined;
  9.  
  10.     if (! (f in d && typeof d[f] === 'function'))
  11.         return undefined;
  12.  
  13.     return d[f](...args);
  14. };

delegate retourne le résultat de l'exécution de la fonction f avec les arguments args suivants f par le délégué de this. Si this n'a pas de délégué ou si le délégué de this n'a pas la fonction f, delegate retourne undefined. Si f vaut null, delegate retourne le délégué de this.

  1. Objective.prototype.setDelegate = function(d) {
  2.     if (! (d === null || (typeof d === 'object')))
  3.         throw new TypeError();
  4.  
  5.     this._delegate = d;
  6.  
  7.     return this;
  8. };

setDelegate initialise la propriété delegate de this avec d.

  1. Objective.prototype.hasListener = function(l) {
  2.     return this._listeners !== undefined && this._listeners.indexOf(l) != -1;
  3. };

hasListener retourne true si l'instance l est dans la liste des écouteurs de this, sinon false.

  1. Objective.prototype.addListener = function(l) {
  2.     if (this._listeners === undefined)
  3.         this._listeners = [l];
  4.     else if (this._listeners.indexOf(l) == -1)
  5.         this._listeners.push(l);
  6.  
  7.     return this;
  8. };

addListener ajoute l'instance l à la liste des écouteurs de this.

  1. Objective.prototype.removeListener = function(l) {
  2.     if (this._listeners !== undefined) {
  3.         let i = this._listeners.indexOf(l);
  4.  
  5.         if (i != -1)
  6.             this._listeners.splice(i, 1);
  7.     }
  8.  
  9.     return this;
  10. };

removeListener retire l'instance l de la liste des écouteurs de this.

  1. Objective.prototype.notify = function(f, ...args) {
  2.     if (this._listeners !== undefined) {
  3.         for (let l of this._listeners)
  4.             l.forwardTo(f, ...args);
  5.     }
  6.  
  7.     return this;
  8. };

notify transmet l'exécution de la méthode f avec les arguments args à toutes les écouteurs de this.

  1. Objective.prototype.forwardTo = function(f, ...args) {
  2.     if (typeof this[f] === 'function')
  3.         this[f](...args);
  4.     else
  5.         this.forward(f, ...args);
  6. };

forwardTo exécute la méthode f de this avec les arguments args. Si f n'est pas une méthode de this, forwardTo transmet automatiquement son exécution avec forward.

  1. Objective.prototype.forward = function(f, ...args) {
  2. };

forward de Objective ignore l'exécution de f et ne fait rien. Une sous-classe peut coder une implémentation de forward qui transmet l'exécution de la méthode f avec les arguments args à une autre instance, avec forwardTo pour préserver la transmission entre une série d'instances. Voir la section Test.

Test
  1. function Calculator(helper = null) {
  2.     this._accu = 0.0;
  3.  
  4.     if (helper)
  5.         this.setDelegate(helper);
  6. }
  7.  
  8. Calculator.prototype = Object.create(Objective.prototype);
  9.  
  10. Object.defineProperty(Calculator.prototype, 'constructor', { value: Calculator, enumerable: false, writable: true });
  11.  
  12. Object.defineProperty(Calculator.prototype, 'value', {
  13.     get:    function() {
  14.         return this._accu;
  15.     },
  16.     set:    function(n) {
  17.         if (typeof n !== 'number')
  18.             throw new TypeError();
  19.  
  20.         this._accu = n;
  21.     }
  22. });
  23.  
  24. Calculator.prototype.clear = function() {
  25.     this._accu = 0.0;
  26.  
  27.     return this;
  28. };
  29.  
  30. Calculator.prototype.add = function(val) {
  31.     this._accu += val;
  32.  
  33.     return this;
  34. };
  35.  
  36. Calculator.prototype.sub = function(val) {
  37.     this._accu -= val;
  38.  
  39.     return this;
  40. };
  41.  
  42. Calculator.prototype.mul = function(val) {
  43.     this._accu *= val;
  44.  
  45.     return this;
  46. };
  47.  
  48. Calculator.prototype.div = function(val) {
  49.     this._accu /= val;
  50.  
  51.     return this;
  52. };
  53.  
  54. Calculator.prototype.sqrt = function() {
  55.     this._accu = Math.sqrt(this._accu);
  56.  
  57.     return this;
  58. };
  59.  
  60. Calculator.prototype.clr = function() {
  61.     this.delegate('clr');
  62.  
  63.     return this;
  64. };
  65.  
  66. Calculator.prototype.sto = function() {
  67.     this.delegate('sto', this._accu);
  68.  
  69.     return this;
  70. };
  71.  
  72. Calculator.prototype.rcl = function() {
  73.     this._accu = this.delegate('rcl');
  74.  
  75.     return this;
  76. };
  77.  
  78. function CalculatorMemory() {
  79.     this._mem = 0.0;
  80. }
  81.  
  82. CalculatorMemory.prototype = Object.create(Objective.prototype);
  83.  
  84. Object.defineProperty(CalculatorMemory.prototype, 'constructor', { value: CalculatorMemory, enumerable: false, writable: true });
  85.  
  86. CalculatorMemory.prototype.clr = function() {
  87.     this._mem = 0.0;
  88.  
  89.     return this;
  90. };
  91.  
  92. CalculatorMemory.prototype.sto = function(val) {
  93.     this._mem = val;
  94.  
  95.     return this;
  96. };
  97.  
  98. CalculatorMemory.prototype.rcl = function() {
  99.     return this._mem;
  100. };
  1. <?php head('javascript', '/objectivejs/Objective.js'); ?>
  2. <?php head('javascript', '/objectivejs/tests/Calculator.js'); ?>

Ajoute les balises <script src="/objectivejs/Objective.js"> et <script src="/objectivejs/tests/Calculator.js"></script> dans la section <head> du document HTML.

Le programme de test crée une calculette avec une mémoire, calcule le nombre d'or et l'affiche, mémorise le résultat, met la calculette à 0, rappelle le nombre en mémoire et l'affiche.

  1. const mem = new CalculatorMemory();
  2. const calc = new Calculator(mem);

Crée une instance de Calculator avec une instance de CalculatorMemory en paramètre.

  1. calc.value = 5;
  2. calc.sqrt();
  3. calc.add(1);
  4. calc.div(2);

Calcule (1 + sqrt(5)) / 2.

  1. console.log(calc.value);    // 1.618

Affiche le résultat.

  1. calc.sto();
  2. calc.clear();

Mémorise le résultat. Remet la calculette à 0.

  1. console.log(calc.value);    // 0

Affiche 0.

  1. calc.rcl();

Rappelle le nombre en mémoire.

  1. console.log(calc.value);    // 1.618

Affiche le résultat.

  1. calc2 = calc.clone();
  2. console.log(calc2.value);   // 1.618
  3. calc2.clear();
  4. console.log(calc.value);    // 1.618
  5. calc2.clr();
  6. calc.rcl();
  7. console.log(calc.value);    // 0

Crée un clone de la calculette et affiche sa valeur. Met la valeur du clone à 0 et vérifie que la valeur de la calculette n'a pas changé. Met la mémoire du clone à 0. Rappelle la valeur en mémoire de la calculette et affiche sa valeur. Comme la calculette et son clone partage la même instance de CalculatorHelper, elle vaut 0.

Affichez la page générée par le fichier testObjective.phtml et vérifiez la trace dans la console du navigateur :

1.618033988749895
0
1.618033988749895
1.618033988749895
1.618033988749895
0

Vérifiez que la calculette et son clone partagent le même délégué :

calc2._delegate === calc._delegate
true

Ajoutez une méthode clone à la classe Calculator qui clone le délégué d'une calculette :

Calculator.prototype.clone = function() {
    const clone = Objective.prototype.clone.call(this);

    const delegate = this.delegate();

    if (delegate)
        clone.setDelegate(delegate.clone());

    return clone;
}

Affichez de nouveau la page générée par le fichier testObjective.phtml et vérifiez dans la trace dans la console du navigateur que la valeur de la mémoire de la calculette n'est pas modifiée quand la mémoire du clone est mise à zéro :

1.618033988749895
0
1.618033988749895
1.618033988749895
1.618033988749895
1.618033988749895

La calculette et son clone ne partagent pas le même délégué :

calc2._delegate === calc._delegate
false
  1. <?php head('javascript', '/objectivejs/Objective.js'); ?>

Ajoute la balise <script src="/objectivejs/Objective.js"></script> dans la section <head> du document HTML.

  1. function Ping() {
  2. }
  3.  
  4. Ping.prototype = Object.create(Objective.prototype);
  5.  
  6. Object.defineProperty(Ping.prototype, 'constructor', { value: Ping, enumerable: false, writable: true });
  7.  
  8. Ping.prototype.echo = function(msg) {
  9.     console.log(`[PING] ${msg}`);
  10. }

La méthode echo de la classe Ping affiche [PING] suivi du message msg sur la console.

  1. function Pong() {
  2. }
  3.  
  4. Pong.prototype = Object.create(Objective.prototype);
  5.  
  6. Object.defineProperty(Pong.prototype, 'constructor', { value: Pong, enumerable: false, writable: true });
  7.  
  8. Pong.prototype.echo = function(msg) {
  9.     console.log(`[PONG] ${msg}`);
  10. }

La méthode echo de la classe Pong affiche [PONG] suivi du message msg sur la console.

  1. function PingPong(pong) {
  2.     this._pong = pong;
  3. }
  4.  
  5. PingPong.prototype = Object.create(Objective.prototype);
  6.  
  7. Object.defineProperty(PingPong.prototype, 'constructor', { value: PingPong, enumerable: false, writable: true });
  8.  
  9. PingPong.prototype.forward = function(f, ...args) {
  10.     this._pong.forwardTo(f, ...args);
  11. }

La méthode forward de la classe PingPong transmet l'exécution de toutes les méthodes à une instance de Pong.

  1. function Router(ping, pong) {
  2.     this._ping = ping;
  3.     this._pong = pong;
  4.  
  5.     this._channel = this._ping;
  6. }
  7.  
  8. Router.prototype = Object.create(Objective.prototype);
  9.  
  10. Object.defineProperty(Router.prototype, 'constructor', { value: Router, enumerable: false, writable: true });
  11.  
  12. Router.prototype.switch = function() {
  13.     this._channel = this._channel === this._ping ? this._pong : this._ping;
  14.  
  15.     this.notify('routerSwitched', this);
  16.  
  17.     return this;
  18. }
  19.  
  20. Router.prototype.forward = function(f, ...args) {
  21.     this._channel.forwardTo(f, ...args);
  22. }

La méthode forward de la classe Router transmet l'exécution de toutes les méthodes à une instance de Ping ou de Pong. La méthode switch bascule la transmission des méthodes entre les instances de Ping ou de Pong. switch notifie le message routerSwiched à tous les écouteurs.

  1. function Listener(logger) {
  2.     this._logger = logger;
  3. }
  4.  
  5. Listener.prototype = Object.create(Objective.prototype);
  6.  
  7. Object.defineProperty(Listener.prototype, 'constructor', { value: Listener, enumerable: false, writable: true });
  8.  
  9. Listener.prototype.forward = function(f, ...args) {
  10.     if (this._logger);
  11.         this._logger.forwardTo(f, ...args);
  12. }

La méthode forward de la classe Listener transmet l'exécution de toutes les méthodes à une éventuelle instance de Logger.

  1. function Logger() {
  2. }
  3.  
  4. Logger.prototype = Object.create(Objective.prototype);
  5.  
  6. Object.defineProperty(Logger.prototype, 'constructor', { value: Logger, enumerable: false, writable: true });
  7.  
  8. Logger.prototype.routerSwitched = function(sender) {
  9.     console.log('ROUTER SWITCHED');
  10. }

Une instance de Logger répond au message routerSwiched en affichant ROUTER SWITCHED sur la console.

  1. const ping = new Ping();
  2. const pong = new Pong();
  3.  
  4. const router1 = new Router(ping, pong);

Crée une instance de Router avec une instance de Ping et une instance de Pong.

  1. router1.forward('echo', 'HELLO');   // [PING] HELLO
  2. router1.switch();
  3. router1.forward('echo', 'HELLO');   // [PONG] HELLO

Transmet la méthode echo à router1 qui la transmet à ping qui affiche [PING] HELLO. Bascule router1 sur pong. Transmet la méthode echo à router1 qui la transmet à pong qui affiche [PONG] HELLO.

  1. const pingpong = new PingPong(pong);
  2.  
  3. const router2 = new Router(ping, pingpong);

Crée une instance de PingPong avec une instance de Pong. Crée une instance de Router avec une instance de Ping et une instance de PingPong.

  1. router2.forward('echo', 'HELLO');   // [PING] HELLO
  2. router2.switch();
  3. router2.forward('echo', 'HELLO');   // [PONG] HELLO

Transmet la méthode echo à router2 qui la transmet à ping qui affiche [PING] HELLO. Bascule router2 sur pingpong. Transmet la méthode echo à router2 qui la transmet à pingpong qui la transmet à pong qui affiche [PONG] HELLO.

  1. const logger = new Logger();
  2. const listener = new Listener(logger);

Crée une instance de Listener avec une instance de Logger.

  1. router2.addListener(listener);
  2. router2.switch();                   // ROUTER SWITCHED
  3. router2.forward('echo', 'HELLO');   // [PING] HELLO

Ajoute listener à la liste des écouteurs de router2. Bascule router2 sur ping ce qui notifie le message routerSwitched à listener qui le transmet automatiquement à logger qui affiche ROUTER SWITCHED. Transmet la méthode echo à router2 qui la transmet à ping qui affiche [PING] HELLO.

  1. const router3 = new Router(ping, router2);

Crée une instance de Router avec une instance de Ping et une instance de Router.

  1. router3.addListener(listener);
  2. router3.switch();                   // ROUTER SWITCHED
  3. router3.forward('echo', 'HELLO');   // [PING] HELLO
  4. router2.switch();                   // ROUTER SWITCHED
  5. router3.forward('echo', 'HELLO');   // [PONG] HELLO

Ajoute listener à la liste des écouteurs de router3. Bascule router3 sur router2 ce qui notifie le message routerSwitched à listener qui le transmet automatiquement à logger qui affiche ROUTER SWITCHED. Transmet la méthode echo à router3 qui la transmet à router2 qui la transmet à ping qui affiche [PING] HELLO. Bascule router2 sur pingpong ce qui notifie le message routerSwitched à listener qui le transmet automatiquement à logger qui affiche ROUTER SWITCHED. Transmet la méthode echo à router3 qui la transmet à router2 qui la transmet à pingpong qui la transmet à pong qui affiche [PONG] HELLO.

Affichez la page générée par le fichier testObjectiveForward.phtml et vérifiez la trace dans la console du navigateur :

[PING] HELLO
[PONG] HELLO
[PING] HELLO
[PONG] HELLO
ROUTER SWITCHED
[PING] HELLO
ROUTER SWITCHED
[PING] HELLO
ROUTER SWITCHED
[PONG] HELLO
VOIR AUSSI

Installation, Once, Responder

Commentaires

Votre commentaire :
[p] [b] [i] [u] [s] [quote] [pre] [br] [code] [url] [email] strip aide 2000

Entrez un maximum de 2000 caractères.
Améliorez la présentation de votre texte avec les balises de formatage suivantes :
[p]paragraphe[/p], [b]gras[/b], [i]italique[/i], [u]souligné[/u], [s]barré[/s], [quote]citation[/quote], [pre]tel quel[/pre], [br]à la ligne,
[url]http://www.izend.org[/url], [url=http://www.izend.org]site[/url], [email]izend@izend.org[/email], [email=izend@izend.org]izend[/email],
[code]commande[/code], [code=langage]code source en c, java, php, html, javascript, xml, css, sql, bash, dos, make, etc.[/code].