KEMBAR78
OOP Best Practices in JavaScript | PDF
OOP Best Practices
Haim Michael
April 11th
, 2022
All logos, trade marks and brand names used in this presentation belong
to the respective owners.
JavaScript
www.lifemichael.com
in
life michael
© 2008 Haim Michael 20150805
About Me
 My name is Haim Michael. I was born in Israel, and I live in
Tel-Aviv. One of the best cities in the world.
 I am a father for two. Jonathan (6 years old), and Shira (4
years old).
 I love sport, and I am addicted to Snowboarding.
 I have passion for learning programming, and teaching.
© 2008 Haim Michael 20150805
About Me
 I have more than 25 years of experience in computer
programming.
CC++ (30+ Years), Java (25+ Years), JavaScript (25+
Years), PHP (17+ Years),C# (16+ Years), Python (14+
Years), Scala (14+ Years), Kotlin (8+ Years), TypeScript (8+
Years), and Swift (8+ Years).
© 2008 Haim Michael 20150805
Introduction
 OOP in JavaScript is based on prototypical inheritance. As of
ECMAScript 2016 (AKA ES6) we can define classes, and we
can define classes that extend others.
 With the emergence of C++ nearly 40 years ago, as well as
with the emergence of many other programming languages
that were based on C++, best practices have evolved.
© 2008 Haim Michael 20150805
Best Practice
 Apart of the SOLID design principles, during the last four
decades, we can identify best practices that continuously
evolve in various object oriented programming languages,
such as Java, Scala, C#, Swift, Kotlin, Python, and others.
 Some of these best practices are based on the SOLID
design principles.
© 2008 Haim Michael 20150805
Code Sample
class Person {
#id
#name
constructor(id,name) {
console.log("inside Person constructor");
this.id = id;
this.name = name;
}
set id(val){
console.log("setting id")
if (val>0) {
this.#id = val;
} else {
this.#id = 1000000000;
}
}
© 2008 Haim Michael 20150805
Code Sample
get id(){
console.log("getting id");
return this.#id;
}
set name(val){
console.log("setting name")
if (val!=="") {
this.#name = val;
} else {
this.#name = 'noname';
}
}
get name(){
console.log("getting name");
return this.#name;
}
toString() {
return "id="+this.id+" name="+this.name;
}
}
© 2008 Haim Michael 20150805
Code Sample
class Student extends Person {
#average
constructor(id,name,average) {
console.log("inside Student constructor");
super(id,name);
this.average = average;
}
set average(val){
console.log("setting average")
if (val>=0 && val<=100) {
this.#average = val;
} else {
this.#average = 0;
}
}
© 2008 Haim Michael 20150805
Code Sample
get average(){
console.log("getting average");
return this.#average;
}
toString() {
return super.toString() + " average=" + this.average;
}
}
let ob = new Student(123123123,"mosh",92);
console.log(ob.toString());
© 2008 Haim Michael 20150805
The Private Access Modifier
 In most cases, the variables we define in a class should be
private. Doing so, we will actually force the use of the
setter method. The setter method can include a validation
check for the assigned value.
There are very few cases where it might be useful to avoid
the use of the private accessibility.
© 2008 Haim Michael 20150805
Constructors Chain of Responsibility
 When defining a class that extends another class, the
constructor in our class should include a call to the
constructor in the parent class (using the super keyword).
 In addition to the invocation of the constructor that was
defined in our class, the other constructors (in the classes
that our class extends from) should do their part as well.
Doing so we will be in accordance with the single
responsibility principle.
© 2008 Haim Michael 20150805
The toString Method
 Whenever we define a new class, it would be best to
include the definition for the toString method.
This way when creating log messages it would be simpler
to include a textual representation for specific objects.
© 2008 Haim Michael 20150805
Exceptions Classes
 When using the try & catch exceptions handling
mechanism it would be useful to define a new exception
class specific for our project.
© 2008 Haim Michael 20150805
The SOLID Design Principles
 SOLID is an acronym that stands for five key design
principles in OOP. Following these principles will contribute
to the quality of our code by improving its maintainability, its
clarity and its flexibility for changes.
© 2008 Haim Michael 20150805
Single Responsibility
"A class should have only one reason to change."
(Robert Martin)
https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html
© 2008 Haim Michael 20150805
Single Responsibility
 Every class should have one single responsibility, and it
shouldn't change unless it is in order to fulfill its responsibility.
When having a class with only one single responsibility the
testing will be simpler, the class will have less dependencies,
the code will be characterized with lower coupling, and the
clarity of the entire code will be improved.
© 2008 Haim Michael 20150805
Single Responsibility
class Car {
#engineVolume
#engineHP
#brand
#color
//...
}
© 2008 Haim Michael 20150805
Single Responsibility
class Engine {
#volume
#hp
}
class Car {
#engine
#brand
#color
//...
}
© 2008 Haim Michael 20150805
Open Closed
"software entities (classes, modules, functions, etc.) should
be open for extension, but closed for modification"
(Bertnard Meir)
© 2008 Haim Michael 20150805
Open Closed
 Classes should be open for extension but closed for
modification.
When following this principle we will avoid modifying
existing code, and by doing so we will avoid potential bugs
in the rest of the code. The only exception for this
principle would be fixing bugs.
© 2008 Haim Michael 20150805
Open Closed
class CheckingAccount {
#balance
//...
getBalance() {
return this.#balance;
}
}
class SavingAccount {
#balance
#interest
//...
getValue() {
return this.#balance * (1+this.#interest);
}
}
© 2008 Haim Michael 20150805
Open Closed
class Customer {
#accounts
//...
getCustomerValue() {
var total = 0;
for(let i=0; i<this.#accounts.length; i++) {
if (accounts[i] instanceof CheckingAccount) {
total += this.#accounts[i].getBalance();
} else if(accounts[i] instanceof SavingAccount) {
total += this.#accounts[i].getValue();
}
}
return total;
}
}
© 2008 Haim Michael 20150805
Open Closed
class BankExceptoion {
#message
constructor(text) {
this.#message = text;
}
}
class BankAccount {
getValue() {
throw new BankException("not implemented");
}
}
© 2008 Haim Michael 20150805
Open Closed
class CheckingAccount extends BankAccount {
#balance
getValue() {
//...
//return ...
}
}
class SavingAccount extends BankAccount {
#balance
#interest
getValue() {
//...
//return ...
}
}
© 2008 Haim Michael 20150805
Open Closed
class Customer {
#accounts
//...
getCustomerValue() {
var total = 0;
for(let i=0; i<this.#accounts.length; i++) {
total += this.#accounts[i].getValue();
}
return total;
}
}
© 2008 Haim Michael 20150805
Liskov Substitution
“Let Φ(x) be a property provable about objects x of type T.
Then Φ(y) should be true for objects y of type S where S is a
subtype of T.”
(Barbara Liskov)
© 2008 Haim Michael 20150805
Liskov Substitution
 When having superclass object it should be possible to
replace it with a subclass object without breaking the
functionality.
When defining a new class that extends a class that already
exists we better avoid overriding a method with a new version
that throws exception when calling it (as a way for showing
that it still wasn't implemented).
© 2008 Haim Michael 20150805
Liskov Substitution
class BankAccount {
#balance
constructor(sum) {
this.#balance = sum;
}
//...
}
class CheckingAccount extends BankAccount {
//...
}
var ob = new CheckingAccount();
© 2008 Haim Michael 20150805
Liskov Substitution
class BankAccount {
#balance
constructor(sum) {
this.#balance = sum;
}
//...
}
class CheckingAccount extends BankAccount {
constructor(sum) {
super(sum);
}
}
var ob = new CheckingAccount(100);
© 2008 Haim Michael 20150805
Interface Segregation
 We better split large interfaces into smaller ones. This
way, the classes we define will implement just the relevant
interfaces.
Splitting an interface into multiple ones will improve the
clarity of our code.
© 2008 Haim Michael 20150805
Interface Segragation
“Many client specific interfaces are better than one general
purpose interface”
(Robert C.Martin)
© 2008 Haim Michael 20150805
Interface Segragation
class WebBrowser {
browse(url) {
throw new OperationException("browse is not supported");
}
back() {
throw new OperationException("back is not supported");
}
forward() {
throw new OperationException("forward is not supported");
}
executeJS(code) {
throw new OperationException("executeJS is not supported");
}
}
© 2008 Haim Michael 20150805
Interface Segragation
class WebBrowser {
browse(url) {
throw new OperationException("browse is not supported");
}
back() {
throw new OperationException("back is not supported");
}
forward() {
throw new OperationException("forward is not supported");
}
}
class JavaScriptEngine {
executeJS(code) {
throw new OperationException("executeJS is not supported");
}
}
© 2008 Haim Michael 20150805
Dependency Inversion
“ High-level modules should not import anything from low-
level modules. Both should depend on abstractions (e.g.,
interfaces). Abstractions should not depend on details.
Details (concrete implementations) should depend on
abstractions.”
(Robert C.Martin)
© 2008 Haim Michael 20150805
Dependency Inversion
 Instead of having high level modules depending on low
level modules, both should depend on abstraction.
Following this principle, we will decouple the
dependencies in our code and improve its flexibility for
changes.
© 2008 Haim Michael 20150805
Dependency Inversion
class Engine {
#volume
#hp
}
class Car {
#engine
#brand
#color
constructor() {
this.#engine = new Engine();
}
}
© 2008 Haim Michael 20150805
Dependency Inversion
class Engine {
#volume
#hp
}
class Car {
#engine
#brand
#color
constructor(engine) {
this.#engine = engine;
}
}
© 2008 Haim Michael 20150805
Conclusions
 Instead of sticking with JavaScript let's shift to TypeScript.
The use of types and the additional capabilities, such as
the possibility to define interfaces, and the possibility to
use generics will provide us with a better foundation and
the quality of our code will improve.
© 2008 Haim Michael 20150805
Conclusions
 When the need for simplicity governs, let's keep it simple
by using the simple JavaScript object notation.
{name:”Mosh”, id:3452344, average:98}
© 2009 Haim Michael All Rights Reserved 40
Questions & Answers
Thanks for Your Time!
Haim Michael
haim.michael@lifemichael.com
+972+3+3726013 ext:700
+972+54+6655837 (whatsapp)

OOP Best Practices in JavaScript

  • 1.
    OOP Best Practices HaimMichael April 11th , 2022 All logos, trade marks and brand names used in this presentation belong to the respective owners. JavaScript www.lifemichael.com in life michael
  • 2.
    © 2008 HaimMichael 20150805 About Me  My name is Haim Michael. I was born in Israel, and I live in Tel-Aviv. One of the best cities in the world.  I am a father for two. Jonathan (6 years old), and Shira (4 years old).  I love sport, and I am addicted to Snowboarding.  I have passion for learning programming, and teaching.
  • 3.
    © 2008 HaimMichael 20150805 About Me  I have more than 25 years of experience in computer programming. CC++ (30+ Years), Java (25+ Years), JavaScript (25+ Years), PHP (17+ Years),C# (16+ Years), Python (14+ Years), Scala (14+ Years), Kotlin (8+ Years), TypeScript (8+ Years), and Swift (8+ Years).
  • 4.
    © 2008 HaimMichael 20150805 Introduction  OOP in JavaScript is based on prototypical inheritance. As of ECMAScript 2016 (AKA ES6) we can define classes, and we can define classes that extend others.  With the emergence of C++ nearly 40 years ago, as well as with the emergence of many other programming languages that were based on C++, best practices have evolved.
  • 5.
    © 2008 HaimMichael 20150805 Best Practice  Apart of the SOLID design principles, during the last four decades, we can identify best practices that continuously evolve in various object oriented programming languages, such as Java, Scala, C#, Swift, Kotlin, Python, and others.  Some of these best practices are based on the SOLID design principles.
  • 6.
    © 2008 HaimMichael 20150805 Code Sample class Person { #id #name constructor(id,name) { console.log("inside Person constructor"); this.id = id; this.name = name; } set id(val){ console.log("setting id") if (val>0) { this.#id = val; } else { this.#id = 1000000000; } }
  • 7.
    © 2008 HaimMichael 20150805 Code Sample get id(){ console.log("getting id"); return this.#id; } set name(val){ console.log("setting name") if (val!=="") { this.#name = val; } else { this.#name = 'noname'; } } get name(){ console.log("getting name"); return this.#name; } toString() { return "id="+this.id+" name="+this.name; } }
  • 8.
    © 2008 HaimMichael 20150805 Code Sample class Student extends Person { #average constructor(id,name,average) { console.log("inside Student constructor"); super(id,name); this.average = average; } set average(val){ console.log("setting average") if (val>=0 && val<=100) { this.#average = val; } else { this.#average = 0; } }
  • 9.
    © 2008 HaimMichael 20150805 Code Sample get average(){ console.log("getting average"); return this.#average; } toString() { return super.toString() + " average=" + this.average; } } let ob = new Student(123123123,"mosh",92); console.log(ob.toString());
  • 10.
    © 2008 HaimMichael 20150805 The Private Access Modifier  In most cases, the variables we define in a class should be private. Doing so, we will actually force the use of the setter method. The setter method can include a validation check for the assigned value. There are very few cases where it might be useful to avoid the use of the private accessibility.
  • 11.
    © 2008 HaimMichael 20150805 Constructors Chain of Responsibility  When defining a class that extends another class, the constructor in our class should include a call to the constructor in the parent class (using the super keyword).  In addition to the invocation of the constructor that was defined in our class, the other constructors (in the classes that our class extends from) should do their part as well. Doing so we will be in accordance with the single responsibility principle.
  • 12.
    © 2008 HaimMichael 20150805 The toString Method  Whenever we define a new class, it would be best to include the definition for the toString method. This way when creating log messages it would be simpler to include a textual representation for specific objects.
  • 13.
    © 2008 HaimMichael 20150805 Exceptions Classes  When using the try & catch exceptions handling mechanism it would be useful to define a new exception class specific for our project.
  • 14.
    © 2008 HaimMichael 20150805 The SOLID Design Principles  SOLID is an acronym that stands for five key design principles in OOP. Following these principles will contribute to the quality of our code by improving its maintainability, its clarity and its flexibility for changes.
  • 15.
    © 2008 HaimMichael 20150805 Single Responsibility "A class should have only one reason to change." (Robert Martin) https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html
  • 16.
    © 2008 HaimMichael 20150805 Single Responsibility  Every class should have one single responsibility, and it shouldn't change unless it is in order to fulfill its responsibility. When having a class with only one single responsibility the testing will be simpler, the class will have less dependencies, the code will be characterized with lower coupling, and the clarity of the entire code will be improved.
  • 17.
    © 2008 HaimMichael 20150805 Single Responsibility class Car { #engineVolume #engineHP #brand #color //... }
  • 18.
    © 2008 HaimMichael 20150805 Single Responsibility class Engine { #volume #hp } class Car { #engine #brand #color //... }
  • 19.
    © 2008 HaimMichael 20150805 Open Closed "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification" (Bertnard Meir)
  • 20.
    © 2008 HaimMichael 20150805 Open Closed  Classes should be open for extension but closed for modification. When following this principle we will avoid modifying existing code, and by doing so we will avoid potential bugs in the rest of the code. The only exception for this principle would be fixing bugs.
  • 21.
    © 2008 HaimMichael 20150805 Open Closed class CheckingAccount { #balance //... getBalance() { return this.#balance; } } class SavingAccount { #balance #interest //... getValue() { return this.#balance * (1+this.#interest); } }
  • 22.
    © 2008 HaimMichael 20150805 Open Closed class Customer { #accounts //... getCustomerValue() { var total = 0; for(let i=0; i<this.#accounts.length; i++) { if (accounts[i] instanceof CheckingAccount) { total += this.#accounts[i].getBalance(); } else if(accounts[i] instanceof SavingAccount) { total += this.#accounts[i].getValue(); } } return total; } }
  • 23.
    © 2008 HaimMichael 20150805 Open Closed class BankExceptoion { #message constructor(text) { this.#message = text; } } class BankAccount { getValue() { throw new BankException("not implemented"); } }
  • 24.
    © 2008 HaimMichael 20150805 Open Closed class CheckingAccount extends BankAccount { #balance getValue() { //... //return ... } } class SavingAccount extends BankAccount { #balance #interest getValue() { //... //return ... } }
  • 25.
    © 2008 HaimMichael 20150805 Open Closed class Customer { #accounts //... getCustomerValue() { var total = 0; for(let i=0; i<this.#accounts.length; i++) { total += this.#accounts[i].getValue(); } return total; } }
  • 26.
    © 2008 HaimMichael 20150805 Liskov Substitution “Let Φ(x) be a property provable about objects x of type T. Then Φ(y) should be true for objects y of type S where S is a subtype of T.” (Barbara Liskov)
  • 27.
    © 2008 HaimMichael 20150805 Liskov Substitution  When having superclass object it should be possible to replace it with a subclass object without breaking the functionality. When defining a new class that extends a class that already exists we better avoid overriding a method with a new version that throws exception when calling it (as a way for showing that it still wasn't implemented).
  • 28.
    © 2008 HaimMichael 20150805 Liskov Substitution class BankAccount { #balance constructor(sum) { this.#balance = sum; } //... } class CheckingAccount extends BankAccount { //... } var ob = new CheckingAccount();
  • 29.
    © 2008 HaimMichael 20150805 Liskov Substitution class BankAccount { #balance constructor(sum) { this.#balance = sum; } //... } class CheckingAccount extends BankAccount { constructor(sum) { super(sum); } } var ob = new CheckingAccount(100);
  • 30.
    © 2008 HaimMichael 20150805 Interface Segregation  We better split large interfaces into smaller ones. This way, the classes we define will implement just the relevant interfaces. Splitting an interface into multiple ones will improve the clarity of our code.
  • 31.
    © 2008 HaimMichael 20150805 Interface Segragation “Many client specific interfaces are better than one general purpose interface” (Robert C.Martin)
  • 32.
    © 2008 HaimMichael 20150805 Interface Segragation class WebBrowser { browse(url) { throw new OperationException("browse is not supported"); } back() { throw new OperationException("back is not supported"); } forward() { throw new OperationException("forward is not supported"); } executeJS(code) { throw new OperationException("executeJS is not supported"); } }
  • 33.
    © 2008 HaimMichael 20150805 Interface Segragation class WebBrowser { browse(url) { throw new OperationException("browse is not supported"); } back() { throw new OperationException("back is not supported"); } forward() { throw new OperationException("forward is not supported"); } } class JavaScriptEngine { executeJS(code) { throw new OperationException("executeJS is not supported"); } }
  • 34.
    © 2008 HaimMichael 20150805 Dependency Inversion “ High-level modules should not import anything from low- level modules. Both should depend on abstractions (e.g., interfaces). Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.” (Robert C.Martin)
  • 35.
    © 2008 HaimMichael 20150805 Dependency Inversion  Instead of having high level modules depending on low level modules, both should depend on abstraction. Following this principle, we will decouple the dependencies in our code and improve its flexibility for changes.
  • 36.
    © 2008 HaimMichael 20150805 Dependency Inversion class Engine { #volume #hp } class Car { #engine #brand #color constructor() { this.#engine = new Engine(); } }
  • 37.
    © 2008 HaimMichael 20150805 Dependency Inversion class Engine { #volume #hp } class Car { #engine #brand #color constructor(engine) { this.#engine = engine; } }
  • 38.
    © 2008 HaimMichael 20150805 Conclusions  Instead of sticking with JavaScript let's shift to TypeScript. The use of types and the additional capabilities, such as the possibility to define interfaces, and the possibility to use generics will provide us with a better foundation and the quality of our code will improve.
  • 39.
    © 2008 HaimMichael 20150805 Conclusions  When the need for simplicity governs, let's keep it simple by using the simple JavaScript object notation. {name:”Mosh”, id:3452344, average:98}
  • 40.
    © 2009 HaimMichael All Rights Reserved 40 Questions & Answers Thanks for Your Time! Haim Michael haim.michael@lifemichael.com +972+3+3726013 ext:700 +972+54+6655837 (whatsapp)