Software design patters are a ways to think about and categorize different types of solutions to common problems in software development.
At the core of design patterns is the inbuilt idea that they are reusable, that is, they are designed to encapsulate a certain set of use cases and have universal applicability within that set.
A design pattern will follow a certain structure, use certain types of code objects and relationships, but do not necessarily specify a particular syntactical format. More broadly, they represent ways to approach specific implementations to specific problems.
There does not seem to be overall consensus as to what the threshold for a type of problem-solving approach to be considered a design pattern, but generally it can be agreed that there are three primary types of design pattern: creational, structural, and behavioral
Creational design patterns categorize procedures for instantiating objects. Oftentimes code will need to create its own objects for use, and the way that it creates these objects will have significant bearing on its overall functionality.
The way an end-user interacts with an object may change they way an object needs to be created, or the way a certain part of the program interacts with that object may change what should and should not be contained in that object.
A few examples of creational design patterns in javascript:
Constructors make a function with initial input properties that is then instantiated the keyword new
, passing in arguments to be set as the key values.
function Cat(name, color, isHungry){
this.name = name;
this.color = color;
this.isHungry = isHungry;
}
const myCat = new Cat("Fiddlesticks", "brown", true);
console.log(myCat)
//Object { name: "Fiddlesticks", color: "brown", isHungry: true }
The builder design pattern creates objects in discrete phases. There is a main base object, on which methods or functions external to it are called upon to modify the object to have the desired properties and values. A very simple example:
const obj1 = {
username: 'jimmy',
password: 1234
}
const addStorage = (obj) =>{
obj.storage = []
}
addStorage(obj1)
console.log(obj1);
//Object { username: "jimmy", password: 1234, storage: [] }
This could also include something like a maker class containing methods for construction another object and then a main class constructor that has the maker class's methods called on itself.
Other creational design patterns include prototypal (as in using object prototypes for inheritance), modules (for limiting access of certain properties to avoid polluting the global namespace, and factories to name a few.
Structural design patterns are concerned with how multiple objects and/or classes are composed and organized into larger structures. They help to define how objects relate to one another
Facade structural patterns create a uniform interface for several other unrelated sub-processes. They are designed to abstract away complex functionality of multiple other systems and provide the client with a standardized way to interact with the sub-systems.
class Donkey{
sound(){
console.log("hee-haw")
}
}
class Ogre{
sound(){
console.log("shut up, donkey")
}
}
class DreamworksShrekMovieFascade{
constructor(){
this.donkey = new Donkey();
this.ogre = new Ogre();
}
bigRedButton(){
this.donkey.sound();
this.ogre.sound();
}
}
const shrek = new DreamworksShrekMovieFascade()
shrek.bigRedButton()
Other structural patterns include the bridge structural pattern, which connects two systems but maintains independence such that changes to one system will not affect the other, and the decorator pattern, which makes it possible to modify the behavior of a given object by "decorating" with with a function.
Behavioral design patterns involve communication and interaction between objects and classes. It focuses on ways that functionality is delegated and the hierarchical network of object relationships.
The simplest example of a behavioral pattern is the concept of iteration through a collection of data. So structures such as for loops, and higher order functions like forEach, filter, map and reduce are all ways to traverse data uniformly and are not coupled with the particular data being iterated through.
A more involved behavioral pattern in the strategy pattern, which creates a set of algorithms that can be switched out with one-another to easily facilitate adaptational functionality.
For example:
class SpaceTime{
constructor(timeline){
this.timeline = timeline
}
enterTimeline(){
this.timeline.enter()
}
}
class FutureTimeline{
enter() {
console.log("AAAAHHHH A ROBOT IS EATING ME!")
}
}
class PastTimeline{
enter(){
console.log("AAAAHHHH A DINOSAUR IS EATING ME!")
}
}
const future = new SpaceTime(new FutureTimeline());
future.enterTimeline();
const past = new SpaceTime(new PastTimeline())
past.enterTimeline();
We create a class SpaceTime that constructs a timeline, and has a method to enterTimeline. Next we create a class pointing to the future, and onther looking to the past. Each has its own enter function. Now when we instantiate a future timeline, we do so passing it into a new SpaceTime object. This way we can re-use our same enterTimeline function on both past and future timelines.
These examples are by no means a comprehensive representation of all possible design patterns, but hopefully helped to clarify some of the different ways programs can be written to facilitate a variety of different specific functionalities.
Sources
geeksforgeeks
dofactory
Design Patterns in JavaScript: A Comprehensive Guide
JavaScript Design Patterns
The Comprehensive Guide to JavaScript Design Patterns
Top comments (0)