English | Chinese
Design patterns (GoF) implemented in ECMAScript.
Code sample principles:
- ES6
- Minimalism
- Abstract
Creational patterns that describe how objects are instantiated and focus on the instantiation of classes.
Ensure a class has only one instance.
// static
class Singleton {
constructor(name) {
this.name = name;
this.instance = null;
}
static create(name) {
if (!this.instance) this.instance = new Singleton(name);
return this.instance;
}
}
// constructor
class Singleton {
constructor(name) {
if (typeof Singleton.instance === 'object') {
return Singleton.instance;
}
Singleton.instance = this;
this.name = name;
}
}
// proxy
class Instance {
constructor(name) {
this.name = name;
}
}
class Singleton {
constructor(name) {
if (!Singleton.instance) {
Singleton.instance = new Instance(name);
}
return Singleton.instance;
}
}
Factory Method lets a class defer instantiation to subclasses.
class A {
constructor() {
this.name = 'A';
}
}
class B {
constructor() {
this.name = 'B';
}
}
class C {
constructor() {
this.name = 'C';
}
}
class Factory {
static create(product) {
switch (product.toUpperCase()) {
case 'A': return new A();
case 'B': return new B();
case 'C': return new C();
default:
throw new Error('no class');
}
}
}
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
class A1 {
constructor() {
this.name = 'A1';
}
}
class A2 {
constructor() {
this.name = 'A2';
}
}
class B1 {
constructor() {
this.name = 'B1';
}
}
class B2 {
constructor() {
this.name = 'B2';
}
}
class FactoryA {
static create(product) {
switch (product.toUpperCase()) {
case 'A1':
return new A1();
case 'A2':
return new A2();
default:
throw new Error('no product');
}
}
}
class FactoryB {
static create(product) {
switch (product.toUpperCase()) {
case 'B1':
return new B1();
case 'B2':
return new B2();
default:
throw new Error('no product');
}
}
}
class AbstractFactory {
static create(factory) {
switch (factory.toUpperCase()) {
case 'A':
return FactoryA;
case 'B':
return FactoryB;
default:
throw new Error('no factory');
}
}
}
Using multiple simple objects to build a complex object step by step.
class A {
constructor() {
this.name = 'A';
}
}
class B {
constructor() {
this.name = 'B';
}
}
class Builder {
constructor() {
this.a = new A();
this.b = new B();
}
}
Specify the kinds of objects to create using a prototypical instance, and create new objects from the 'skeleton' of an existing object.
class Prototype {
constructor(name) {
this.name = name;
}
clone() {
return new Prototype(this.name);
}
}
Structural patterns which describe how classes or objects can be combined to build larger, more complex structures, focus on combinations of classes and objects.
Allows classes with incompatible interfaces to work together by wrapping its own interface around that of an already existing class.
class Standard {
execute() {
return false;
}
}
class Instance {
action() {
return true;
}
}
class Adapter {
static adapter(instance) {
instance.execute = instance.action;
}
}
Decouples an abstraction from its implementation so that the two can vary independently.
class Bridge {
execute(value) {
return value;
}
}
class Instance {
constructor(value, bridge) {
this.value = value;
this.bridge = bridge;
}
execute() {
return this.bridge.execute(this.value);
}
}
Objects are grouped into a tree structure to represent a part-whole hierarchy.
class Instance {
constructor(value) {
this.value = value;
this.children = [];
}
add(instance) {
this.children.push(instance);
}
toString() {
return this.value.toString() + this.children.map((child) => child.toString()).join('');
}
}
Dynamically adds/overrides behaviour in an existing method of an object.
class Original {
constructor(value) {
this.value = value;
}
execute() {
return this.value;
}
}
class Decorator extends Original {
superExecute() {
return this.value * 2;
}
}
Provides a simplified interface to a large body of code.
class A {
execute() {
return 'A';
}
}
class B {
execute() {
return 'B';
}
}
class C {
execute() {
return 'C';
}
}
class Facade {
constructor() {
this.a = new A();
this.b = new B();
this.c = new C();
}
executeA() {
return this.a.execute();
}
executeB() {
return this.b.execute();
}
executeC() {
return this.c.execute();
}
}
Reduces the cost of creating and manipulating a large number of similar objects.
class Flyweight {
constructor(value) {
this.value = value;
}
}
class Instance {
constructor() {
this.items = [];
}
create(value) {
this.items.push(new Flyweight(value));
return this;
}
}
Provides a placeholder for another object to control access, reduce cost, and reduce complexity.
class Instance {
constructor() {
this.value = true;
}
}
class Proxy {
constructor() {
return new Instance();
}
}
Behavioral patterns that describe how to clearly divide the responsibilities of classes and objects and focus on communication between objects.
Delegates commands to a chain of processing objects.
class Start {
constructor(instance) {
this.instance = instance;
}
execute(value) {
this.instance.value += value * 1;
}
}
class Process {
constructor(instance) {
this.instance = instance;
}
execute(value) {
this.instance.value += value * 2;
}
}
class End {
constructor(instance) {
this.instance = instance;
}
execute(value) {
this.instance.value += value * 3;
}
}
class Instance {
constructor() {
this.value = 0;
this.chain = [
new Start(this),
new Process(this),
new End(this),
];
}
execute(value) {
this.chain.forEach((obj) => {
obj.execute(value);
});
}
}
Creates objects which encapsulate actions and parameters.
class Executor {
constructor() {
this.state = false;
}
execute() {
this.state = true;
}
}
class Command {
constructor(instance) {
this.instance = instance;
}
execute() {
this.instance.execute();
}
}
class Commander {
constructor(command) {
this.command = command;
}
execute() {
this.command.execute();
}
}
Implements a specialized language.
class Interpreter {
static execute(commend) {
return commend + 1;
}
}
class Command {
constructor(commend) {
this.commend = commend;
}
execute() {
return Interpreter.execute(this.commend);
}
}
Accesses the elements of an object sequentially without exposing its underlying representation.
class Iterator {
constructor(arr) {
this.i = 0;
this.arr = arr;
}
next() {
this.i += 1;
return this.arr[this.i];
}
hasNext() {
return this.index < this.arr.length;
}
}
Allows loose coupling between classes by being the only class that has detailed knowledge of their methods.
class Mediator {
static execute(instanceA, instanceB) {
instanceA.execute(instanceB.value);
}
}
class Instance {
constructor(value) {
this.value = value;
}
execute(value) {
this.value = value;
}
}
Provides the ability to restore an object to its previous state.
class Memento {
constructor() {
this.value = '';
}
set(value) {
this.value = value;
}
get() {
return this.value;
}
}
class Instance {
constructor(value) {
this.value = value;
}
save(memento) {
memento.set(this.value);
}
restore(memento) {
this.value = memento.get();
}
}
A publish/subscribe pattern which allows a number of observer objects to see an event.
class Instance {
constructor(value) {
this.value = value;
this.children = [];
}
add(observer) {
this.children.push(observer);
}
execute(value) {
this.value = value;
this.children.forEach((child) => child.execute(this));
}
}
class Observer {
constructor(rate) {
this.value = 0;
this.rate = rate;
}
execute(instance) {
this.value = this.rate * instance.value;
}
}
Allows an object to alter its behavior when its internal state changes.
class State {
constructor(name) {
this.name = name;
}
execute() {
return this.name;
}
}
class Instance {
constructor(state) {
this.state = state;
}
change(state) {
this.state = state;
}
execute() {
return this.state.execute();
}
}
Allows one of a family of algorithms to be selected on-the-fly at runtime.
const METHODS = {
addition: (a, b) => a + b,
subtraction: (a, b) => a - b,
multiplication: (a, b) => a * b,
division: (a, b) => a / b,
max: (a, b) => (a > b ? a : b),
min: (a, b) => ((a < b) ? a : b),
};
class Strategy {
constructor(a, b) {
this.a = a;
this.b = b;
}
execute(method) {
return METHODS[method](this.a, this.b);
}
}
Method defines the skeleton of an algorithm as an abstract class, allowing its subclasses to provide concrete behavior.
class Template {
start() {}
end() {}
execute() {
return this.start() + this.end();
}
}
class Instance extends Template {
constructor(a, b) {
super();
this.a = a;
this.b = b;
}
start() {
return this.a;
}
end() {
return this.b;
}
}
Separates an algorithm from an object structure by moving the hierarchy of methods into one object.
class Instance {
constructor(value) {
this.value = value;
}
execute(visitor) {
return visitor.execute(this);
}
}
class Visitor {
constructor(rate) {
this.rate = rate;
}
execute(instance) {
return instance.value * this.rate;
}
}
- Detailed explanation
- Complex application cases
Please star βοΈ the repository to show your support!