Skip to content

Commit c85e78c

Browse files
committed
Liskov Substitution Principle (LSP)
1 parent b19273f commit c85e78c

File tree

1 file changed

+138
-0
lines changed

1 file changed

+138
-0
lines changed

README.md

+138
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,144 @@ class AjaxRequester {
861861
```
862862
**[⬆ back to top](#table-of-contents)**
863863

864+
865+
### Liskov Substitution Principle (LSP)
866+
This is a scary term for a very simple concept. It's formally defined as "If S
867+
is a subtype of T, then objects of type T may be replaced with objects of type S
868+
(i.e., objects of type S may substitute objects of type T) without altering any
869+
of the desirable properties of that program (correctness, task performed,
870+
etc.)." That's an even scarier definition.
871+
872+
The best explanation for this is if you have a parent class and a child class,
873+
then the base class and child class can be used interchangeably without getting
874+
incorrect results. This might still be confusing, so let's take a look at the
875+
classic Square-Rectangle example. Mathematically, a square is a rectangle, but
876+
if you model it using the "is-a" relationship via inheritance, you quickly
877+
get into trouble.
878+
879+
**Bad:**
880+
```javascript
881+
class Rectangle {
882+
constructor() {
883+
this.width = 0;
884+
this.height = 0;
885+
}
886+
887+
setColor(color) {
888+
// ...
889+
}
890+
891+
render(area) {
892+
// ...
893+
}
894+
895+
setWidth(width) {
896+
this.width = width;
897+
}
898+
899+
setHeight(height) {
900+
this.height = height;
901+
}
902+
903+
getArea() {
904+
return this.width * this.height;
905+
}
906+
}
907+
908+
class Square extends Rectangle {
909+
constructor() {
910+
super();
911+
}
912+
913+
setWidth(width) {
914+
this.width = width;
915+
this.height = width;
916+
}
917+
918+
setHeight(height) {
919+
this.width = height;
920+
this.height = height;
921+
}
922+
}
923+
924+
function renderLargeRectangles(rectangles) {
925+
rectangles.forEach((rectangle) => {
926+
rectangle.setWidth(4);
927+
rectangle.setHeight(5);
928+
let area = rectangle.getArea(); // BAD: Will return 25 for Square. Should be 20.
929+
rectangle.render(area);
930+
})
931+
}
932+
933+
let rectangles = [new Rectangle(), new Rectangle(), new Square()];
934+
renderLargeRectangles(rectangles);
935+
```
936+
937+
**Good**:
938+
```javascript
939+
class Shape {
940+
constructor() {}
941+
942+
setColor(color) {
943+
// ...
944+
}
945+
946+
render(area) {
947+
// ...
948+
}
949+
}
950+
951+
class Rectangle extends Shape {
952+
constructor() {
953+
super();
954+
this.width = 0;
955+
this.height = 0;
956+
}
957+
958+
setWidth(width) {
959+
this.width = width;
960+
}
961+
962+
setHeight(height) {
963+
this.height = height;
964+
}
965+
966+
getArea() {
967+
return this.width * this.height;
968+
}
969+
}
970+
971+
class Square extends Shape {
972+
constructor() {
973+
super();
974+
this.length = 0;
975+
}
976+
977+
setLength(length) {
978+
this.length = length;
979+
}
980+
}
981+
982+
function renderLargeShapes(shapes) {
983+
shapes.forEach((shape) => {
984+
switch (shape.constructor.name) {
985+
case 'Square':
986+
shape.setLength(5);
987+
case 'Rectangle':
988+
shape.setWidth(4);
989+
shape.setHeight(5);
990+
}
991+
992+
let area = shape.getArea();
993+
shape.render(area);
994+
})
995+
}
996+
997+
let shapes = [new Rectangle(), new Rectangle(), new Square()];
998+
renderLargeShapes();
999+
```
1000+
**[⬆ back to top](#table-of-contents)**
1001+
8641002
### Prefer ES6 classes over ES5 plain functions
8651003
It's very difficult to get readable class inheritance, construction, and method
8661004
definitions for classical ES5 classes. If you need inheritance (and be aware

0 commit comments

Comments
 (0)