@@ -1073,6 +1073,97 @@ let $ = new DOMTraverser({
1073
1073
```
1074
1074
** [ ⬆ back to top] ( #table-of-contents ) **
1075
1075
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
+
1076
1167
### Prefer ES6 classes over ES5 plain functions
1077
1168
It's very difficult to get readable class inheritance, construction, and method
1078
1169
definitions for classical ES5 classes. If you need inheritance (and be aware
0 commit comments