@@ -1073,6 +1073,97 @@ let $ = new DOMTraverser({
10731073```
10741074** [ ⬆ back to top] ( #table-of-contents ) **
10751075
1076+ ### Dependency Inversion Principle (DIP)
1077+ This principle states two essential things:
1078+ 1 . High-level modules should not depend on low-level modules. Both should
1079+ depend on abstractions.
1080+ 2 . Abstractions should not depend upon details. Details should depend on
1081+ abstractions.
1082+
1083+ This can be hard to understand at first, but if you've worked with Angular.js,
1084+ you've seen an implementation of this principle in the form of Dependency
1085+ Injection (DI). While they are not identical concepts, DIP keeps high-level
1086+ modules from knowing the details of its low-level modules and setting them up.
1087+ It can accomplish this through DI.
1088+
1089+ As stated previously, JavaScript doesn't have interfaces so the abstractions
1090+ that are depended upon are implicit contracts. That is to say, the methods
1091+ and properties that an object/class exposes to another object/class.
1092+
1093+ ** Bad:**
1094+ ``` javascript
1095+ class InventoryTracker {
1096+ constructor (items ) {
1097+ this .items = items;
1098+
1099+ // BAD: We have created a dependency on a specific request implementation.
1100+ // We should just have requestItems depend on a request method: `request`
1101+ this .requester = new InventoryRequester ();
1102+ }
1103+
1104+ requestItems () {
1105+ this .items .forEach ((item ) => {
1106+ this .requester .requestItem (item);
1107+ });
1108+ }
1109+ }
1110+
1111+ class InventoryRequester {
1112+ constructor () {
1113+ this .REQ_METHODS = [' HTTP' ];
1114+ }
1115+
1116+ requestItem (item ) {
1117+ // ...
1118+ }
1119+ }
1120+
1121+ let inventoryTracker = new InventoryTracker ([' apples' , ' bananas' ]);
1122+ inventoryTracker .requestItems ();
1123+ ```
1124+
1125+ ** Good** :
1126+ ``` javascript
1127+ class InventoryTracker {
1128+ constructor (items , requester ) {
1129+ this .items = items;
1130+ this .requester = requester;
1131+ }
1132+
1133+ requestItems () {
1134+ this .items .forEach ((item ) => {
1135+ this .requester .requestItem (item);
1136+ });
1137+ }
1138+ }
1139+
1140+ class InventoryRequesterV1 {
1141+ constructor () {
1142+ this .REQ_METHODS = [' HTTP' ];
1143+ }
1144+
1145+ requestItem (item ) {
1146+ // ...
1147+ }
1148+ }
1149+
1150+ class InventoryRequesterV2 {
1151+ constructor () {
1152+ this .REQ_METHODS = [' WS' ];
1153+ }
1154+
1155+ requestItem (item ) {
1156+ // ...
1157+ }
1158+ }
1159+
1160+ // By constructing our dependencies externally and injecting them, we can easily
1161+ // substitute our request module for a fancy new one that uses WebSockets.
1162+ let inventoryTracker = new InventoryTracker ([' apples' , ' bananas' ], new InventoryRequesterV2 ());
1163+ inventoryTracker .requestItems ();
1164+ ```
1165+ ** [ ⬆ back to top] ( #table-of-contents ) **
1166+
10761167### Prefer ES6 classes over ES5 plain functions
10771168It's very difficult to get readable class inheritance, construction, and method
10781169definitions for classical ES5 classes. If you need inheritance (and be aware
0 commit comments