Zoeken…
Invoering
Ontwerppatronen zijn een goede manier om uw code leesbaar en DROOG te houden. DRY staat voor herhaal jezelf niet . Hieronder vindt u meer voorbeelden over de belangrijkste ontwerppatronen.
Opmerkingen
In software engineering is een software-ontwerppatroon een algemene herbruikbare oplossing voor een veel voorkomend probleem binnen een gegeven context in software-ontwerp.
Singleton patroon
Het Singleton-patroon is een ontwerppatroon dat de instantiëring van een klasse tot één object beperkt. Nadat het eerste object is gemaakt, retourneert het de verwijzing naar hetzelfde telkens wanneer er om een object wordt gevraagd.
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;
}
};
})();
Gebruik:
// 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
Module en onthullende modulepatronen
Module patroon
Het modulepatroon is een creatief en structureel ontwerppatroon dat een manier biedt om privéleden in te kapselen en tegelijkertijd een openbare API te produceren. Dit wordt bereikt door een IIFE te maken waarmee we variabelen kunnen definiëren die alleen beschikbaar zijn binnen het bereik (door afsluiting ), terwijl een object wordt geretourneerd dat de openbare API bevat.
Dit biedt ons een schone oplossing om de hoofdlogica te verbergen en alleen een interface bloot te leggen die we in andere delen van onze applicatie willen gebruiken.
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 */);
Onthullend modulepatroon
Het Revealing Module-patroon is een variant in het Module-patroon. De belangrijkste verschillen zijn dat alle leden (privé en openbaar) binnen de afsluiting worden gedefinieerd, de retourwaarde een letterlijk object is dat geen functiedefinities bevat en alle verwijzingen naar lidgegevens worden gedaan via directe verwijzingen in plaats van via het geretourneerde object.
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 */);
Onthullend prototypepatroon
Deze variatie van het onthullende patroon wordt gebruikt om de constructor te scheiden van de methoden. Met dit patroon kunnen we de javascript-taal gebruiken als een georiënteerde taal:
//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}
}();
Deze bovenstaande code moet in een gescheiden bestand .js zijn waarnaar moet worden verwezen op elke gewenste pagina. Het kan als volgt worden gebruikt:
var menuActive = new NavigationNs.active('ul.sidebar-menu li', 5);
menuActive.setCurrent();
Prototype patroon
Het prototypepatroon richt zich op het creëren van een object dat kan worden gebruikt als een blauwdruk voor andere objecten door middel van prototypische overerving. Dit patroon is inherent eenvoudig om mee te werken in JavaScript vanwege de native ondersteuning voor prototypische overerving in JS, wat betekent dat we geen tijd of moeite hoeven te besteden om deze topologie te imiteren.
Methoden maken op het prototype
function Welcome(name) {
this.name = name;
}
Welcome.prototype.sayHello = function() {
return 'Hello, ' + this.name + '!';
}
var welcome = new Welcome('John');
welcome.sayHello();
// => Hello, John!
Prototypale erfenis
Het overnemen van een 'bovenliggend object' is relatief eenvoudig via het volgende patroon
ChildObject.prototype = Object.create(ParentObject.prototype);
ChildObject.prototype.constructor = ChildObject;
Waar ParentObject
het object is waarvan u de prototypefuncties wilt erven, en ChildObject
het nieuwe object is waarop u ze wilt plaatsen.
Als het bovenliggende object waarden heeft die het in de constructor initialiseert, moet u de ouderconstructor aanroepen wanneer het kind wordt geïnitialiseerd.
U doet dat met het volgende patroon in de constructor ChildObject
.
function ChildObject(value) {
ParentObject.call(this, value);
}
Een compleet voorbeeld waarbij het bovenstaande is geïmplementeerd
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!
Fabrieksfuncties
Een fabrieksfunctie is gewoon een functie die een object retourneert.
Voor fabrieksfuncties is het gebruik van het new
trefwoord niet nodig, maar het kan nog steeds worden gebruikt om een object te initialiseren, zoals een constructor.
Vaak worden fabrieksfuncties gebruikt als API-wrappers, zoals in het geval van jQuery en moment.js , zodat gebruikers geen new
hoeven te gebruiken.
Het volgende is de eenvoudigste vorm van fabrieksfunctie; argumenten nemen en gebruiken om een nieuw object met het letterlijke object te maken:
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"
Het is gemakkelijk om privéeigenschappen en -methoden in een fabriek te definiëren door ze buiten het geretourneerde object op te nemen. Hierdoor blijven uw implementatiegegevens ingekapseld, zodat u de openbare interface alleen aan uw object kunt blootstellen.
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
De laatste regel geeft een fout omdat de functie formalName
is gesloten in de cowFactory
functie. Dit is een sluiting .
Fabrieken zijn ook een geweldige manier om functionele programmeermethoden in JavaScript toe te passen, omdat het functies zijn.
Fabriek met compositie
'Samenstelling verkiezen boven overerving' is een belangrijk en populair programmeerprincipe, dat wordt gebruikt om gedrag aan objecten toe te wijzen, in tegenstelling tot het erven van veel vaak onnodig gedrag.
Gedrag fabrieken
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');
}
};
};
Object fabrieken
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)
);
};
Gebruik
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
Abstract fabriekspatroon
Het abstracte fabriekspatroon is een creatief ontwerppatroon dat kan worden gebruikt om specifieke instanties of klassen te definiëren zonder het exacte object te specificeren dat wordt gemaakt.
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 )