Attualmente sto lavorando a un'applicazione web che dovrebbe assomigliare a un sistema operativo con GUI (gestisce processi e finestre). Ho diversi costruttori che trattano aspetti diversi del programma, come Process
e Window
. Esistono alcuni programmi integrati che utilizzano un'API speciale e una funzione di utilità per estendere Process
e aggiungere determinati metodi al prototipo.
Il ragionamento per questo è che ci possono essere più istanze in esecuzione dei programmi e vorrei che condividessero gli stessi metodi. Sarebbe inutile definire lo stesso metodo per ogni istanza di un programma (che è il motivo per cui non mi piace il tipo di metodo che viene comunemente chiamato "metodo privilegiato" - è certamente utile, ma lo considero un approccio sbagliato ).
La funzione sopra menzionata si trova in uno spazio dei nomi chiamato Programs
(tutti i programmi si trovano anche in quello spazio dei nomi).
Ecco come appare:
var Programs = {
register: function(name, props) {
function Program() {
var args = Array.prototype.slice.call(arguments); // optional arguments
args.unshift(name); // this will be used as the process name
Process.apply(this, args);
}
function Helper() {}
Helper.prototype = Process.prototype;
Program.prototype = new Helper();
for (var k in props)
Program.prototype[k] = props[k];
// for example: Program.prototype.onStart = props.onStart;
// the Process constructor is responsible for calling onStart()
// for more information, look below
return Program; // return the constructor
}
};
Creo un programma in questo modo:
Programs.Terminal = Programs.register('terminal', {
onStart: function() { // called when the process starts
this.mainWindow = this.createWindow(null, 'Terminal'); // the createWindow() method (inherited from Process) takes two arguments, the parent window and the caption, and then returns the newly created window
},
onCreate: function(window) { // called when a window is created with the reference to the window as the argument
// add something to the window
},
onActivate: function(window) { // called when a window gains focus
// ...
}
// and so on...
});
Ora posso semplicemente fare new Programs.Terminal()
per creare una nuova istanza del programma Terminal e, poiché tutti gli hook ( onStart()
, onCreate()
ecc.) vengono aggiunti al prototipo, ogni istanza di Terminal verrà condivisa loro.
Fino ad ora, un processo poteva avere esattamente una finestra e tutto ha funzionato bene, poiché non avevo bisogno di "handle" per tenere traccia delle finestre. Tuttavia, più tardi mi sono reso conto che sarebbe bello se un processo avesse più finestre e questo presentasse un problema.
Guarda la riga 3 nel secondo snippet di codice:
this.mainWindow = this.createWindow(null, 'Terminal');
Prendo il riferimento alla finestra e poi, in teoria, posso usarlo negli hook in questo modo:
onCreate: function(window) {
if (window == this.mainWindow) // do something
else if (window == this.someDialog) // do something else
}
A destra?
No. Il metodo Process.createWindow()
, proprio come suggerisce il nome, crea una nuova finestra, quindi chiama sia onCreate()
che onActivate()
ganci ... prima restituisce! Prima che ogni assedio avvenga! In questo modo, this.mainWindow
non è ancora disponibile quando vengono prima chiamati onCreate()
e onActivate()
.
WinAPI utilizza un sistema simile - le finestre vengono create usando la funzione CreateWindow()
(o CreateWindowEx()
), quella funzione restituisce un handle alla finestra e successivamente l'handle può essere utilizzato nella procedura della finestra per determinare quale finestra messaggi (ad esempio WM_CREATE
) provengono da. La mia ipotesi è che questo funzioni, perché quando viene creata una nuova finestra, essa non invia immediatamente il messaggio - invece, memorizza i messaggi in una coda e successivamente (dopo che la funzione è tornata e l'handle è già stato assegnato ad un variabile) un ciclo esegue il polling dei messaggi dalla coda e li invia alla procedura della finestra (non esitate a correggermi se ho torto). Questo certamente non sarebbe una soluzione ottimale per un'applicazione web.
Non so perché non ci avevo pensato prima. Ad ogni modo, mi stavo chiedendo come avrei potuto farlo funzionare e ho trovato il seguente workaround (non proprio una soluzione, almeno non lo trovo in quanto tale): Process.createWindow()
potrebbe prendere un argomento "handle" (ad esempio, un numero o una stringa), salvarlo internamente e quindi, ad es in onCreate()
, potrei controllare window.handle
nel modo seguente:
onCreate: function(window) {
if (window.handle == 'mainWindow') // do something
else if (window.handle == 'someDialog') // do something else
}
Tuttavia, ho trovato quello terribile e inelegante. Vorrei assegnare manualmente il riferimento di finestra a una proprietà, ad esempio this.mainWindow
, inserirlo in un array o archiviarlo in un oggetto (in modo che possa controllare l'ambito).
Quello che penso funzionerebbe è qualcosa di simile al seguente:
Programs.Terminal = Programs.register('terminal', {
onStart: function() {
this.createWindow(null, 'Terminal', {
onCreate: function() {
// do something
},
onActivate: function() {
// do something else
}
// etc.
});
}
});
Non sembra troppo male ... ma richiederebbe che i metodi di hook siano definiti separatamente su ciascun oggetto della finestra, il che, a mio avviso, potrebbe influire sulle prestazioni (a meno che il browser non abbia eseguito alcune ottimizzazioni molto radicali) se ci fosse un molte finestre presenti.
Che ne pensi? Devo attenermi alla soluzione "stringa di handle", dovrei andare con l'ultima soluzione proposta e ignorare il fatto che lo stesso metodo viene copiato più volte, o forse qualcos'altro? O forse il mio approccio con l'estensione di Process
è decisamente sbagliato? Grazie in anticipo.