Es una solución reutilizable y genérica a un problema común dentro de un contexto. Además esta solución no debe ser obvia y constituirse de un concepto corroborado para dicho problema. Se los considera como buenas prácticas oficiales que un programador puede utilizar para resolver problemas comunes cuando diseña una aplicación o un sistema, pero se debe tener en cuenta que la solución planteada por el patrón de diseño tiene que ser independiente a los lenguajes de programación, esto significa que un patrón de diseño representa una idea, no una implementación particular. Al ser buenas practicas de programación, estos nos permiten crear código más flexible, reutilizable y mantenible.
Existen tres tipos principales de patrones de diseño:
Proveen mecanismos para crear objetos basándose en cierto criterio y de una forma controlada.
Es utilizado cuando el tipo de los objetos a ser creados es determinado por una instancia prototipal, la cual es clonada para producir nuevos objetos Este patrón es usado para:
new
) cuando este es demasiado grande para una aplicación.Prototype - Es una encapsulación de propiedades o métodos con la cual un objeto se vincula o relaciona.
const Developer = function(name) {
this.name = name;
this.skills = [];
};
Developer.prototype.addSkill = function(skill) {
console.log(`adding skill: ${skill}`);
this.skills.push(skill);
};
Developer.prototype.save = function() {
console.log(`Saving developer info: ${this.name}`);
};
const developerOne = new Developer('Jon');
const developerTwo = new Developer('Doe');
developerOne.addSkill('design patterns');
developerOne.save();
developerTwo.save();
// console output
// adding skill: design patterns
// Saving developer info: Jon
// Saving developer info: Doe
Cada nueva copia/instancia del objeto Developer
se vincula al prototipo, el cual es solamente un objeto y por lo tanto este no se estará duplicando
Es utilizado para simplificar la creación de objetos ya que en ocasiones esto puede ser complicado, debido a configuraciones u otros factores, por lo tanto este patrón es utilizado para simplificar y esconder mucho al frontend. En otras palabras, es usado para crear objetos sin exponer la lógica de dicha creación al cliente y este usará una interfaz común para crear un nuevo tipo de objeto.
// developerRepository.js
const developerRepository = function() {
function get(id) {
console.log(`getting developer from DB, ${id}`);
return {
name: `developer from DB ${id}`,
};
}
function save(developer) {
console.log(`saving developer to DB ${developer.name}`);
}
return {
get: get,
save: save,
};
};
module.exports = developerRepository();
// repoFactory.js
const repoFactory = function() {
let repos = this;
const repoList = [
{ name: 'developer', source: './developerRepository' },
{ name: 'project', source: './projectRepository' },
];
repoList.forEach(({ name, source }) => {
repos[name] = require(source);
});
};
module.exports = new repoFactory();
//main.js
const repoFactory = require('./repoFactory');
const developerOne = repoFactory.developer.get(1));
const project = repoFactory.project.get(1);
developerOne.project = project;
console.log(developerOne);
El projectRepository
es muy similar adeveloperRepository
. Este ejemplo está en node.js y en él creamos diferentes objetos repositories
utilizando la función facotry
.
Restringe la instanciación de una clase a una única instancia. Es utilizado para restringir a un objeto a una sola instancia de el mismo. Este patrón de diseño se basa en recordar la instancia y retornarla cuando sea solicitada.
En node.js, los modules son almacenados o "cached" la primera vez que se los carga (modules-caching)[https://nodejs.org/api/modules.html#modules_caching]. Entonces si miras developerRepository
en el patrón de diseño de factory, veras que estamos implementando un singleton
ya que llamamos a la función y exportamos el objeto retornado de la misma.
// developerRepository.js
const developerRepository = function() {
...
return {
get: get,
save: save,
};
};
module.exports = developerRepository();
// or
//module.exports = new developerRepository;
De esta manera, cada vez que requerimos el módulo de esta forma const developerRepo = require('./developerRepository')
, estamos obteniendo la misma instancia/copia del objeto
Therefore every time we require this module const developerRepo = require('./developerRepository')
we are going to get the same object instance.
Para que un module ejecute su código multiples veces, se debe exportar una función y luego llamar dicha función.
Entonces si no queremos definir un singleton
deberíamos hacer lo siguiente.
//developerRepository.js
module.exports = developerRepository;
//main.js
const developerRepo = require('./developerRepository');
const myRepo = developerRepo();
//other.js
const developerRepo = require('./developerRepository');
const otherRepo = developerRepo();
Se ocupan de cómo los objetos son construidos y simplifican las relaciones entre ellos. Se encargan de estas relaciones entre los objetos de dos formas, extendiendo o simplificando funcionalidades de un objeto determinado.
Agregan funcionalidades a un objeto dinámicamente sin afectar el comportamiento de otros objetos de la misma clase. Este patrón de diseño es a menudo utilizado para complementar el -Pincipio de responsabilidad única- , ya que este permite dividir la funcionalidad entre clases, cada una responsable de una funcionalidad particular.
Es utilizado para agregar nuevas funcionalidades sobre objeto existente pero sin cambiar la estructura del mismo.
const Developer = function(name) {
this.name = name;
this.skills = [];
};
Developer.prototype.addSkill = function(skill) {
console.log(`adding skill: ${skill}`);
this.skills.push(skill);
};
Developer.prototype.save = function() {
console.log(`Saving developer info: ${this.name}`);
};
// create new object extending developer object
const FrontendDeveloper = function(name) {
Developer.call(this, name);
};
FrontendDeveloper.prototype = Object.create(Developer.prototype);
FrontendDeveloper.prototype.createUserInterface = function() {
console.log(`${this.name} is creating a new user interface`);
};
FrontendDeveloper.prototype.save = function() {
console.log(`Saving Frontend developer info: ${this.name}`);
};
const developer = new Developer('Jon');
const frontendDeveloper = new FrontendDeveloper('Doe');
developer.save();
frontendDeveloper.createUserInterface();
frontendDeveloper.save();
// console output
// Saving developer info: Jon
// Doe is creating a new user interface
// Saving Frontend developer info: Doe
FrontendDeveloper
extiende o agrega nuevas funcionalidades a partir del objeto Developer
, la ventaja es que el objeto Developer
no se modificara
Utilizado para proveer una interfaz simplificada de un sistema complejo. Facade define una interfaz de nivel superior que hace al subsistema facil de usar y además esconde el caos del subsistema a los usuarios.
Visualiza el frente o fachada de un edificio
JQuery es probablemente la implementación más conocida del patrón de diseño Facade
. En su núcleo, JQuery complementa al DOM creando una interfaz para interactuar con este de manera simple y entendible.
Define una dependencia de uno a muchos objetos así cuando el estado de un objeto cambia, todos los objetos relacionados son notificados y actualizados automáticamente.
Permite a una colección de objetos(observadores) estar pendientes de un objeto(sujeto) y ser notificados ante un cambio.
Ventajas: Causa un bajo acoplamiento entre las interacciones de los objetos, generando objetos más flexibles ante los cambios de requerimientos.En si este bajo acoplamiento significa que los objetos deben tener menos información sobre el resto.
Observer pattern provides this loose coupling as:
Considera usar este patrón en tu aplicación cuando multiples objetos dependen del estado de un objeto
design-patterns ,
Javascript