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.
- function Objective() {
- }
Crée une instance de la classe Objective.
- Objective.prototype.clone = function() {
- return Object.assign(Object.create(this), this);
- };
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.
- Objective.prototype.delegate = function(f = null, ...args) {
- let d = this._delegate;
- if (f === null)
- return d;
- if (d === undefined)
- return undefined;
- if (! (f in d && typeof d[f] === 'function'))
- return undefined;
- return d[f](...args);
- };
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
.
- Objective.prototype.setDelegate = function(d) {
- if (! (d === null || (typeof d === 'object')))
- throw new TypeError();
- this._delegate = d;
- return this;
- };
setDelegate
initialise la propriété delegate de this
avec d
.
- Objective.prototype.hasListener = function(l) {
- return this._listeners !== undefined && this._listeners.indexOf(l) != -1;
- };
hasListener
retourne true
si l'instance l
est dans la liste des écouteurs de this
, sinon false
.
- Objective.prototype.addListener = function(l) {
- if (this._listeners === undefined)
- this._listeners = [l];
- else if (this._listeners.indexOf(l) == -1)
- this._listeners.push(l);
- return this;
- };
addListener
ajoute l'instance l
à la liste des écouteurs de this
.
- Objective.prototype.removeListener = function(l) {
- if (this._listeners !== undefined) {
- let i = this._listeners.indexOf(l);
- if (i != -1)
- this._listeners.splice(i, 1);
- }
- return this;
- };
removeListener
retire l'instance l
de la liste des écouteurs de this
.
- Objective.prototype.notify = function(f, ...args) {
- if (this._listeners !== undefined) {
- for (let l of this._listeners)
- l.forwardTo(f, ...args);
- }
- return this;
- };
notify
transmet l'exécution de la méthode f
avec les arguments args
à toutes les écouteurs de this
.
- Objective.prototype.forwardTo = function(f, ...args) {
- if (typeof this[f] === 'function')
- this[f](...args);
- else
- this.forward(f, ...args);
- };
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
.
- Objective.prototype.forward = function(f, ...args) {
- };
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
- function Calculator(helper = null) {
- this._accu = 0.0;
- if (helper)
- this.setDelegate(helper);
- }
- Calculator.prototype = Object.create(Objective.prototype);
- Object.defineProperty(Calculator.prototype, 'constructor', { value: Calculator, enumerable: false, writable: true });
- Object.defineProperty(Calculator.prototype, 'value', {
- get: function() {
- return this._accu;
- },
- set: function(n) {
- if (typeof n !== 'number')
- throw new TypeError();
- this._accu = n;
- }
- });
- Calculator.prototype.clear = function() {
- this._accu = 0.0;
- return this;
- };
- Calculator.prototype.add = function(val) {
- this._accu += val;
- return this;
- };
- Calculator.prototype.sub = function(val) {
- this._accu -= val;
- return this;
- };
- Calculator.prototype.mul = function(val) {
- this._accu *= val;
- return this;
- };
- Calculator.prototype.div = function(val) {
- this._accu /= val;
- return this;
- };
- Calculator.prototype.sqrt = function() {
- this._accu = Math.sqrt(this._accu);
- return this;
- };
- Calculator.prototype.clr = function() {
- this.delegate('clr');
- return this;
- };
- Calculator.prototype.sto = function() {
- this.delegate('sto', this._accu);
- return this;
- };
- Calculator.prototype.rcl = function() {
- this._accu = this.delegate('rcl');
- return this;
- };
- function CalculatorMemory() {
- this._mem = 0.0;
- }
- CalculatorMemory.prototype = Object.create(Objective.prototype);
- Object.defineProperty(CalculatorMemory.prototype, 'constructor', { value: CalculatorMemory, enumerable: false, writable: true });
- CalculatorMemory.prototype.clr = function() {
- this._mem = 0.0;
- return this;
- };
- CalculatorMemory.prototype.sto = function(val) {
- this._mem = val;
- return this;
- };
- CalculatorMemory.prototype.rcl = function() {
- return this._mem;
- };
- <?php head('javascript', '/objectivejs/Objective.js'); ?>
- <?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.
- const mem = new CalculatorMemory();
- const calc = new Calculator(mem);
Crée une instance de Calculator avec une instance de CalculatorMemory en paramètre.
- calc.value = 5;
- calc.sqrt();
- calc.add(1);
- calc.div(2);
Calcule (1 + sqrt(5)) / 2.
- console.log(calc.value); // 1.618
Affiche le résultat.
- calc.sto();
- calc.clear();
Mémorise le résultat. Remet la calculette à 0.
- console.log(calc.value); // 0
Affiche 0.
- calc.rcl();
Rappelle le nombre en mémoire.
- console.log(calc.value); // 1.618
Affiche le résultat.
- calc2 = calc.clone();
- console.log(calc2.value); // 1.618
- calc2.clear();
- console.log(calc.value); // 1.618
- calc2.clr();
- calc.rcl();
- 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 :
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
- <?php head('javascript', '/objectivejs/Objective.js'); ?>
Ajoute la balise <script src="/objectivejs/Objective.js"></script>
dans la section <head>
du document HTML.
- function Ping() {
- }
- Ping.prototype = Object.create(Objective.prototype);
- Object.defineProperty(Ping.prototype, 'constructor', { value: Ping, enumerable: false, writable: true });
- Ping.prototype.echo = function(msg) {
- console.log(`[PING] ${msg}`);
- }
La méthode echo
de la classe Ping affiche [PING]
suivi du message msg
sur la console.
- function Pong() {
- }
- Pong.prototype = Object.create(Objective.prototype);
- Object.defineProperty(Pong.prototype, 'constructor', { value: Pong, enumerable: false, writable: true });
- Pong.prototype.echo = function(msg) {
- console.log(`[PONG] ${msg}`);
- }
La méthode echo
de la classe Pong affiche [PONG]
suivi du message msg
sur la console.
- function PingPong(pong) {
- this._pong = pong;
- }
- PingPong.prototype = Object.create(Objective.prototype);
- Object.defineProperty(PingPong.prototype, 'constructor', { value: PingPong, enumerable: false, writable: true });
- PingPong.prototype.forward = function(f, ...args) {
- this._pong.forwardTo(f, ...args);
- }
La méthode forward
de la classe PingPong transmet l'exécution de toutes les méthodes à une instance de Pong.
- function Router(ping, pong) {
- this._ping = ping;
- this._pong = pong;
- this._channel = this._ping;
- }
- Router.prototype = Object.create(Objective.prototype);
- Object.defineProperty(Router.prototype, 'constructor', { value: Router, enumerable: false, writable: true });
- Router.prototype.switch = function() {
- this._channel = this._channel === this._ping ? this._pong : this._ping;
- this.notify('routerSwitched', this);
- return this;
- }
- Router.prototype.forward = function(f, ...args) {
- this._channel.forwardTo(f, ...args);
- }
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.
- function Listener(logger) {
- this._logger = logger;
- }
- Listener.prototype = Object.create(Objective.prototype);
- Object.defineProperty(Listener.prototype, 'constructor', { value: Listener, enumerable: false, writable: true });
- Listener.prototype.forward = function(f, ...args) {
- if (this._logger);
- this._logger.forwardTo(f, ...args);
- }
La méthode forward
de la classe Listener transmet l'exécution de toutes les méthodes à une éventuelle instance de Logger.
- function Logger() {
- }
- Logger.prototype = Object.create(Objective.prototype);
- Object.defineProperty(Logger.prototype, 'constructor', { value: Logger, enumerable: false, writable: true });
- Logger.prototype.routerSwitched = function(sender) {
- console.log('ROUTER SWITCHED');
- }
Une instance de Logger répond au message routerSwiched en affichant ROUTER SWITCHED
sur la console.
- const ping = new Ping();
- const pong = new Pong();
- const router1 = new Router(ping, pong);
Crée une instance de Router avec une instance de Ping et une instance de Pong.
- router1.forward('echo', 'HELLO'); // [PING] HELLO
- router1.switch();
- 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
.
- const pingpong = new PingPong(pong);
- 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.
- router2.forward('echo', 'HELLO'); // [PING] HELLO
- router2.switch();
- 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
.
- const logger = new Logger();
- const listener = new Listener(logger);
Crée une instance de Listener avec une instance de Logger.
- router2.addListener(listener);
- router2.switch(); // ROUTER SWITCHED
- 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
.
- const router3 = new Router(ping, router2);
Crée une instance de Router avec une instance de Ping et une instance de Router.
- router3.addListener(listener);
- router3.switch(); // ROUTER SWITCHED
- router3.forward('echo', 'HELLO'); // [PING] HELLO
- router2.switch(); // ROUTER SWITCHED
- 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
Commentaires