Ricerca…
introduzione
Gli schemi di progettazione sono un buon modo per mantenere il codice leggibile e ASCIUTTO. DRY sta per non ripetersi . Di seguito è possibile trovare ulteriori esempi sui modelli di progettazione più importanti.
Osservazioni
Nell'ingegneria del software, un modello di progettazione software è una soluzione generale riutilizzabile per un problema che si verifica comunemente in un dato contesto nella progettazione del software.
Singleton Pattern
Il pattern Singleton è un modello di progettazione che limita l'istanziazione di una classe a un oggetto. Dopo che il primo oggetto è stato creato, restituirà il riferimento allo stesso quando richiesto per un oggetto.
var Singleton = (function () {
// instance stores a reference to the Singleton
var instance;
function createInstance() {
// private variables and methods
var _privateVariable = 'I am a private variable';
function _privateMethod() {
console.log('I am a private method');
}
return {
// public methods and variables
publicMethod: function() {
console.log('I am a public method');
},
publicVariable: 'I am a public variable'
};
}
return {
// Get the Singleton instance if it exists
// or create one if doesn't
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
Uso:
// there is no existing instance of Singleton, so it will create one
var instance1 = Singleton.getInstance();
// there is an instance of Singleton, so it will return the reference to this one
var instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true
Modulo e modelli di moduli rivelatori
Modello del modulo
Il modello di modulo è un modello di progettazione creativa e strutturale che fornisce un modo per incapsulare membri privati durante la produzione di un'API pubblica. Questo si ottiene creando un IIFE che ci consente di definire variabili disponibili solo nel suo ambito (attraverso la chiusura ) mentre restituiamo un oggetto che contiene l'API pubblica.
Questo ci offre una soluzione pulita per nascondere la logica principale e solo per esporre un'interfaccia che desideriamo utilizzare da altre parti della nostra applicazione.
var Module = (function(/* pass initialization data if necessary */) {
// Private data is stored within the closure
var privateData = 1;
// Because the function is immediately invoked,
// the return value becomes the public API
var api = {
getPrivateData: function() {
return privateData;
},
getDoublePrivateData: function() {
return api.getPrivateData() * 2;
}
};
return api;
})(/* pass initialization data if necessary */);
Rivelare il modello del modulo
Il pattern Revealing Module è una variante del pattern Module. Le differenze chiave sono che tutti i membri (privati e pubblici) sono definiti all'interno della chiusura, il valore restituito è un oggetto letterale che non contiene definizioni di funzioni e tutti i riferimenti ai dati dei membri vengono eseguiti tramite riferimenti diretti anziché tramite l'oggetto restituito.
var Module = (function(/* pass initialization data if necessary */) {
// Private data is stored just like before
var privateData = 1;
// All functions must be declared outside of the returned object
var getPrivateData = function() {
return privateData;
};
var getDoublePrivateData = function() {
// Refer directly to enclosed members rather than through the returned object
return getPrivateData() * 2;
};
// Return an object literal with no function definitions
return {
getPrivateData: getPrivateData,
getDoublePrivateData: getDoublePrivateData
};
})(/* pass initialization data if necessary */);
Rivelando il modello di prototipo
Questa variazione del modello rivelatore viene utilizzata per separare il costruttore dai metodi. Questo modello ci consente di utilizzare il linguaggio javascript come un linguaggio orientato agli oggetti:
//Namespace setting
var NavigationNs = NavigationNs || {};
// This is used as a class constructor
NavigationNs.active = function(current, length) {
this.current = current;
this.length = length;
}
// The prototype is used to separate the construct and the methods
NavigationNs.active.prototype = function() {
// It is a example of a public method because is revealed in the return statement
var setCurrent = function() {
//Here the variables current and length are used as private class properties
for (var i = 0; i < this.length; i++) {
$(this.current).addClass('active');
}
}
return { setCurrent: setCurrent };
}();
// Example of parameterless constructor
NavigationNs.pagination = function() {}
NavigationNs.pagination.prototype = function() {
// It is a example of a private method because is not revealed in the return statement
var reload = function(data) {
// do something
},
// It the only public method, because it the only function referenced in the return statement
getPage = function(link) {
var a = $(link);
var options = {url: a.attr('href'), type: 'get'}
$.ajax(options).done(function(data) {
// after the the ajax call is done, it calls private method
reload(data);
});
return false;
}
return {getPage : getPage}
}();
Questo codice sopra deve essere in un file .js separato a cui fare riferimento in qualsiasi pagina necessaria. Può essere usato in questo modo:
var menuActive = new NavigationNs.active('ul.sidebar-menu li', 5);
menuActive.setCurrent();
Modello di prototipo
Il modello prototipo si concentra sulla creazione di un oggetto che può essere utilizzato come progetto per altri oggetti attraverso l'ereditarietà prototipale. Questo pattern è intrinsecamente facile da utilizzare in JavaScript a causa del supporto nativo per l'ereditarietà di prototipi in JS, il che significa che non è necessario dedicare tempo o fatica a imitare questa topologia.
Creare metodi sul prototipo
function Welcome(name) {
this.name = name;
}
Welcome.prototype.sayHello = function() {
return 'Hello, ' + this.name + '!';
}
var welcome = new Welcome('John');
welcome.sayHello();
// => Hello, John!
Eredità prototipale
Ereditare da un "oggetto genitore" è relativamente facile attraverso il seguente schema
ChildObject.prototype = Object.create(ParentObject.prototype);
ChildObject.prototype.constructor = ChildObject;
Dove ParentObject
è l'oggetto da cui si desidera ereditare le funzioni prototipate, e ChildObject
è il nuovo oggetto su cui si desidera inserirle.
Se l'oggetto genitore ha valori inizializzati nel suo costruttore è necessario chiamare il costruttore dei genitori durante l'inizializzazione del figlio.
Lo fai usando il seguente modello nel costruttore ChildObject
.
function ChildObject(value) {
ParentObject.call(this, value);
}
Un esempio completo in cui è implementato quanto sopra
function RoomService(name, order) {
// this.name will be set and made available on the scope of this function
Welcome.call(this, name);
this.order = order;
}
// Inherit 'sayHello()' methods from 'Welcome' prototype
RoomService.prototype = Object.create(Welcome.prototype);
// By default prototype object has 'constructor' property.
// But as we created new object without this property - we have to set it manually,
// otherwise 'constructor' property will point to 'Welcome' class
RoomService.prototype.constructor = RoomService;
RoomService.prototype.announceDelivery = function() {
return 'Your ' + this.order + ' has arrived!';
}
RoomService.prototype.deliverOrder = function() {
return this.sayHello() + ' ' + this.announceDelivery();
}
var delivery = new RoomService('John', 'pizza');
delivery.sayHello();
// => Hello, John!,
delivery.announceDelivery();
// Your pizza has arrived!
delivery.deliverOrder();
// => Hello, John! Your pizza has arrived!
Funzioni di fabbrica
Una funzione di fabbrica è semplicemente una funzione che restituisce un oggetto.
Le funzioni di fabbrica non richiedono l'uso della new
parola chiave, ma possono ancora essere utilizzate per inizializzare un oggetto, come un costruttore.
Spesso le funzioni di fabbrica vengono utilizzate come wrapper API, come nel caso di jQuery e moment.js , quindi gli utenti non hanno bisogno di usare new
.
Quanto segue è la forma più semplice di funzione di fabbrica; prendere argomenti e usarli per creare un nuovo oggetto con l'oggetto letterale:
function cowFactory(name) {
return {
name: name,
talk: function () {
console.log('Moo, my name is ' + this.name);
},
};
}
var daisy = cowFactory('Daisy'); // create a cow named Daisy
daisy.talk(); // "Moo, my name is Daisy"
È facile definire proprietà e metodi privati in una fabbrica, includendoli al di fuori dell'oggetto restituito. Ciò mantiene incapsulati i dettagli della tua implementazione, quindi puoi solo esporre l'interfaccia pubblica al tuo oggetto.
function cowFactory(name) {
function formalName() {
return name + ' the cow';
}
return {
talk: function () {
console.log('Moo, my name is ' + formalName());
},
};
}
var daisy = cowFactory('Daisy');
daisy.talk(); // "Moo, my name is Daisy the cow"
daisy.formalName(); // ERROR: daisy.formalName is not a function
L'ultima riga darà un errore perché la funzione formalName
è chiusa all'interno della funzione cowFactory
. Questa è una chiusura .
Le fabbriche sono anche un ottimo modo di applicare pratiche di programmazione funzionale in JavaScript, perché sono funzioni.
Fabbrica con composizione
"Preferisci la composizione sull'ereditarietà" è un principio di programmazione importante e popolare, utilizzato per assegnare comportamenti agli oggetti, al contrario di ereditare molti comportamenti spesso non necessari.
Fabbriche di comportamento
var speaker = function (state) {
var noise = state.noise || 'grunt';
return {
speak: function () {
console.log(state.name + ' says ' + noise);
}
};
};
var mover = function (state) {
return {
moveSlowly: function () {
console.log(state.name + ' is moving slowly');
},
moveQuickly: function () {
console.log(state.name + ' is moving quickly');
}
};
};
Fabbriche oggetto
var person = function (name, age) {
var state = {
name: name,
age: age,
noise: 'Hello'
};
return Object.assign( // Merge our 'behaviour' objects
{},
speaker(state),
mover(state)
);
};
var rabbit = function (name, colour) {
var state = {
name: name,
colour: colour
};
return Object.assign(
{},
mover(state)
);
};
uso
var fred = person('Fred', 42);
fred.speak(); // outputs: Fred says Hello
fred.moveSlowly(); // outputs: Fred is moving slowly
var snowy = rabbit('Snowy', 'white');
snowy.moveSlowly(); // outputs: Snowy is moving slowly
snowy.moveQuickly(); // outputs: Snowy is moving quickly
snowy.speak(); // ERROR: snowy.speak is not a function
Modello astratto di fabbrica
Abstract Factory Pattern è un pattern di progettazione creazionale che può essere utilizzato per definire istanze o classi specifiche senza dover specificare l'oggetto esatto che viene creato.
function Car() { this.name = "Car"; this.wheels = 4; }
function Truck() { this.name = "Truck"; this.wheels = 6; }
function Bike() { this.name = "Bike"; this.wheels = 2; }
const vehicleFactory = {
createVehicle: function (type) {
switch (type.toLowerCase()) {
case "car":
return new Car();
case "truck":
return new Truck();
case "bike":
return new Bike();
default:
return null;
}
}
};
const car = vehicleFactory.createVehicle("Car"); // Car { name: "Car", wheels: 4 }
const truck = vehicleFactory.createVehicle("Truck"); // Truck { name: "Truck", wheels: 6 }
const bike = vehicleFactory.createVehicle("Bike"); // Bike { name: "Bike", wheels: 2 }
const unknown = vehicleFactory.createVehicle("Boat"); // null ( Vehicle not known )