Object default values and use cases of builder pattern
Object Default values
In this article I will continue covering design patterns. Today's tutorial is about builder strategy pattern. It has can be very beneficial to maintain clean code. There are few tips and tricks which can help you to identify and use builder pattern.
let user = {
name: "Tim",
lastName: "Benoi",
address: {
street: "Picadilly",
post: "212 14"
},
telefon: [
{
homeNumber: "761222122"
},
{
workNumber: "761222122"
}
]
}
We can access object parameters through dot, object destructuring as discussed in the previous post. If you would like to read more about it follow the link. here => https://webdevs.se/admin/content/posts/edit/18
let { name, lastName, address: { street, post }, telefon} = user
However, let's imagine the case scenario where we access variables which are not defined on user object
let { name, lastName, address: { street, post }, telefon, companyName, position} = user
as you can see we assigned companyName and position variables with user object. However user object does not have those variables. As a result javascript will set default values of undefined.
// companyName => undefined, position => undefined
This where default values comes in.
let { name, lastName, address: { street, post }, telefon, companyName = "Invajo", position = "developer"} = user
//console.log(company) companyName => Invajo, console.log(position) position => developer
// we encapsulate work related information into an object
let { name, lastName, address: { street, post }, telefon, work = { name:"Invajo", position:"developer" }} = user
// => console.log(work) {name: "Invajo", position: "developer"}
Default values can be used in functions and there are lots of other beneficial use case scenarios, however it is not in the scope of this article. Later on we can see how this technique can be useful in construction of builder pattern.
Business case: Car manufacturer
Let 's imagine that we are car manufacturer that sells different kind of cars and allows its customers to modify certain parts of the car. Like engine, air conditioning, automatic switcher, autopilot and etc. In order to that we first create a car with basic configuration and then write the logic to enable addition of extra features.
class CarBuilder {
constructor(engine, fuelType, autopilot, navigation) {
this.engine = engine
this.fuelType = fuelType
this.autopilot = autopilot
this.navigation = navigation
}
}
// let define our customer engine
class Engine {
constructor(model, horsePower) {
this.model = model
this.horsePower = horsePower
}
}
// our default engine
class defautlEngine {
constructor() {
this.model = "F100"
this.horsePower = 120
}
}
// let's assume our customer made choice of only
let engine = new Engine("F130", 390)
let customerCar = new CarBuilder(engine, "petrol", true, true)
// console.log(customerCar)
// => Car; {engine: {horsePower: 390, model: "F130"}, fuelType: "petrol", autopilot: true, navigation: true}
In this case our customer chooses all four configuration options which we had to offer, now what will happen if our customer would like to buy car with only two configurations. As you can see our first placeholder is engine, that's why we have to pass undefined for engine, autopilot and navigation.
let customerCar1 = new Car(new Engine("F100", 120), "petrol", false, true)
// engine with model F130 and horse power 120 is our default engine
// console.log(customerCar1) => Car;{engine:{horsePower:120, model: "F100"}, fuelType: "petrol", autopilot: false, navigation: true}
As you can see it would be hard to continue with this current logic, imagine we have 100 configurations and we have to assign correct values based on how variables are placed in our class constructor. This is where builder pattern comes in. We can dynamically build car based in inputs. Let's first define our default car class
class defaultEngine {
constructor() {
this.model = "F100"
this.horsePower = 120
}
}
// engine builder
class engineBuilder {
constructor() {
this.engine = new defaultEngine()
}
setModel(model) {
this.engine.model = model
return this
}
sethorsePower(hp) {
this.engine.horsePower = hp
return this
}
buildEngine() {
return this.engine
}
}
class defaultCar {
constructor() {
// we are building simple engine
this.engine = new engineBuilder()
this.fuelType = "diesel"
this.autopilot = false
this.navigation = false
}
}
class carBuilder {
constructor() {
this.car = new defaultCar()
}
setEngine(engine) {
this.car.engine = engine
return this
}
setPetrol(fuelType) {
this.car.fuelType = fuelType
return this
}
setAutoPilot(autopilot) {
this.car.autopilot = autopilot
return this
}
setNavigation(navigation) {
this.car.navigation = navigation
return this
}
setAirconditoner(airConditioner) {
this.car.airConditioner = airConditioner
return this
}
setAutomaticBreak(autobreak) {
this.car.autobreak = autobreak
return this
}
buildCar() {
return this.car
}
}
Our setup is ready, let's actually initialize simple car, with simple engine
let defaultEngine = new engineBuilder().buildEngine()
// => defaultEngine {model: "F100", horsePower: 120}
let simpleCa1 = new carBuilder().setEngine(defaultEngine).buildCar()
// => defaultCar {engine: defaultEngine, fuelType: "diesel", autopilot: false, navigation: false}
// let configure our engine and car
let f130Engine = new engineBuilder().setModel("F130").sethorsePower(450).buildEngine()
let f140car = new carBuilder()
.setEngine(f130engine)
.setAirconditoner(true).
.setFuelType("petrol")
.setNavigation(true).
.setAutoPilot(true).
.setAutomaticBreak(true)
.buildCar()
// =>
/*
defaultCar {engine: defaultEngine, fuelType: "diesel", autopilot: true, navigation: true, airConditioner: true, …}
airConditioner: true
autobreak: true
autopilot: true
engine: defaultEngine
horsePower: 120
model: "F130"
fuelType: "petrol"
navigation: true
*/
Let's redo the same pattern utilizing default values.
// same things with default values
class Engine {
constructor({ model="F120", horsePower=120 } = {}) {
this.model = model
this.horsePower = horsePower
}
}
class Car {
constructor(engine, { fuelType="diesel", autopilot=false, navigation=false } = {}) {
// we are building simple engine
this.engine = engine
this.fuelType = fuelType
this.autopilot = autopilot
this.navigation = navigation
}
}
// let build f13o engine car with diesel and navigation
// accesssing parameters with object destructuring
let f130Engine = new Engine({ model: "F130"})
// console.log(f130Engine) => { model: "F130", horsePower: 120}
let dieselCar = new Car(f130Engine, { navigation: true })
// console.log(dieselCar) =>
/*
Car {engine: Engine, fuelType: "diesel", autopilot: false, navigation: true}
autopilot: false
engine: Engine
horsePower: 120
model: "F130"
fuelType: "diesel"
navigation: true
*/
It looks straightforward with object destructuring and default values technique. I hope it brings benefit for you.