Szukaj…
Wprowadzenie
Wzorce projektowe są dobrym sposobem na zachowanie czytelności i DRY. DRY oznacza nie powtarzaj się . Poniżej można znaleźć więcej przykładów najważniejszych wzorów projektowych.
Uwagi
W inżynierii oprogramowania wzorzec projektowania oprogramowania jest ogólnym rozwiązaniem wielokrotnego użytku dla często występującego problemu w danym kontekście w projektowaniu oprogramowania.
Singleton Pattern
Wzorzec Singleton to wzorzec projektowy, który ogranicza tworzenie instancji klasy do jednego obiektu. Po utworzeniu pierwszego obiektu zwraca odwołanie do tego samego, ilekroć zostanie wywołany dla obiektu.
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;
}
};
})();
Stosowanie:
// 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
Moduły i ujawnianie wzorów modułów
Wzór modułu
Wzorzec modułu to kreacyjny i strukturalny wzorzec projektowy, który zapewnia sposób enkapsulacji prywatnych członków podczas tworzenia publicznego API. Odbywa się to poprzez utworzenie IIFE, który pozwala nam zdefiniować zmienne dostępne tylko w jego zakresie (poprzez zamknięcie ), zwracając obiekt zawierający publiczny interfejs API.
To daje nam czyste rozwiązanie do ukrywania głównej logiki i ujawniania interfejsu, którego chcielibyśmy używać z innych części naszej aplikacji.
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 */);
Odkrywający wzór modułu
Wzór modułu ujawniania jest odmianą wzoru modułu. Kluczowe różnice polegają na tym, że wszystkie elementy (prywatne i publiczne) są zdefiniowane w zamknięciu, zwracana wartość jest literałem obiektu nie zawierającym definicji funkcji, a wszystkie odwołania do danych elementu są dokonywane przez bezpośrednie odwołania, a nie przez zwracany obiekt.
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 */);
Odkrywając wzór prototypu
Ta odmiana ujawniającego się wzoru służy do oddzielenia konstruktora od metod. Ten wzorzec pozwala nam używać języka javascript jako języka zorientowanego obiektowo:
//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}
}();
Powyższy kod powinien znajdować się w oddzielnym pliku .js, do którego można się odwoływać na każdej potrzebnej stronie. Można go użyć w następujący sposób:
var menuActive = new NavigationNs.active('ul.sidebar-menu li', 5);
menuActive.setCurrent();
Wzór prototypu
Wzorzec prototypowy skupia się na stworzeniu obiektu, który może być użyty jako plan dla innych obiektów poprzez dziedziczenie prototypowe. Ten wzorzec jest z natury łatwy w obsłudze w JavaScript ze względu na natywną obsługę dziedziczenia prototypów w JS, co oznacza, że nie musimy tracić czasu ani wysiłku naśladując tę topologię.
Tworzenie metod na prototypie
function Welcome(name) {
this.name = name;
}
Welcome.prototype.sayHello = function() {
return 'Hello, ' + this.name + '!';
}
var welcome = new Welcome('John');
welcome.sayHello();
// => Hello, John!
Dziedziczenie prototypowe
Dziedziczenie po „obiekcie nadrzędnym” jest stosunkowo łatwe dzięki poniższemu wzorowi
ChildObject.prototype = Object.create(ParentObject.prototype);
ChildObject.prototype.constructor = ChildObject;
Gdzie ParentObject
to obiekt, od którego chcesz odziedziczyć prototypowane funkcje, a ChildObject
to nowy obiekt, na którym chcesz je umieścić.
Jeśli obiekt nadrzędny ma wartości, które inicjuje w swoim konstruktorze, musisz wywołać konstruktor nadrzędny podczas inicjowania dziecka.
Robisz to za pomocą następującego wzorca w konstruktorze ChildObject
.
function ChildObject(value) {
ParentObject.call(this, value);
}
Kompletny przykład realizacji powyższego
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!
Funkcje fabryczne
Funkcja fabryczna to po prostu funkcja zwracająca obiekt.
Funkcje fabryczne nie wymagają użycia new
słowa kluczowego, ale nadal mogą być używane do inicjowania obiektu, takiego jak konstruktor.
Często funkcje fabryczne są używane jako opakowania API, tak jak w przypadku jQuery i moment.js , więc użytkownicy nie muszą używać new
.
Oto najprostsza forma funkcji fabrycznej; przyjmowanie argumentów i używanie ich do tworzenia nowego obiektu za pomocą literału obiektu:
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"
Łatwo jest zdefiniować prywatne właściwości i metody w fabryce, umieszczając je poza zwracanym obiektem. Dzięki temu szczegóły implementacji są hermetyzowane, dzięki czemu można udostępnić publiczny interfejs tylko dla obiektu.
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
Ostatni wiersz spowoduje błąd, ponieważ funkcja formalName
jest zamknięta wewnątrz funkcji cowFactory
. To jest zamknięcie .
Fabryki są także świetnym sposobem na stosowanie funkcjonalnych praktyk programowania w JavaScript, ponieważ są funkcjami.
Fabryka z kompozycją
„Preferuj kompozycję zamiast dziedziczenia” jest ważną i popularną zasadą programowania, stosowaną do przypisywania zachowań obiektom, w przeciwieństwie do dziedziczenia wielu często niepotrzebnych zachowań.
Fabryki zachowań
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');
}
};
};
Fabryki obiektów
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)
);
};
Stosowanie
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
Abstrakcyjny wzór fabryki
Abstrakcyjny wzorzec fabryczny to kreacyjny wzorzec projektowy, którego można użyć do zdefiniowania określonych instancji lub klas bez konieczności określania dokładnie tworzonego obiektu.
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 )