-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
209 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,118 @@ | ||
> The biggest problem in the development and maintenance of large-scale software is complexity - large systems are hard to understand | ||
- "Out of the Tarpit - Ben Mosely Peter Marks" | ||
- "Out of the Tarpit - Ben Mosely Peter Marks" | ||
|
||
> We believe that the major contributor to this complexity in many systems is the handling of state and the burden that this adds when trying to analyse and reason about the system. Other closely related contributors are code volume, and explicit concern with the flow of control through the system. | ||
- "Out of the Tarpit - Ben Mosely Peter Marks" | ||
**State Management** | ||
Lesson 1: Don't use shared mutable state. | ||
- "Out of the Tarpit - Ben Mosely Peter Marks" | ||
|
||
- Lesson 1: Don't use shared mutable state. | ||
- Lesson 2: Code Volume | ||
- Lesson 3: Flow | ||
|
||
### **State Management** | ||
|
||
#### Problem with HIDDEN STATE | ||
|
||
![image](https://github.com/gglee89/giwoolee.com/assets/16644017/0c0004b1-8a1e-4103-91b6-8c5a0fd73d1d) | ||
|
||
Lesson 2: Code Volume | ||
```js | ||
class Inventory { | ||
ledger = { total: 1200 } | ||
} | ||
|
||
class ItemsComponent { | ||
ledger: any | ||
constructor(private inventory: Inventory) { | ||
this.ledger = inventory.ledger; | ||
} | ||
add(x) { this.ledger.total += x; } | ||
} | ||
|
||
class WidgetsComponent { | ||
ledger: any | ||
constructor(private inventory: Inventory) { | ||
this.ledger = inventory.ledger; | ||
} | ||
add(x) { this.ledger.total += x; } | ||
} | ||
``` | ||
|
||
#### Violating the SRP (Single Responsibility Principle) | ||
|
||
Essentially we need to test two things within a single test based on how the `reCalculateTotal` function is written. This represents an increase in complexity. | ||
|
||
Example of a bad code (hard to test) | ||
|
||
``` | ||
price; | ||
mode; | ||
widgets: Widget[]; | ||
reCalculateTotal(widget: Widget) { | ||
switch (this.mode) { | ||
case 'create': | ||
const newWidget = Object.assign({, widget, { id: uuidv4() }}); | ||
this.widgets = [...this.widgets, newWidget] | ||
break; | ||
case 'update': | ||
this.widgets = this.widgets.map(wdgt => | ||
(widget.id === wdgt.id) ? Object.assign({}, widget) : wdgt); | ||
break; | ||
case 'delete': | ||
this.widgets = this.widgets.filter(wdgt => widget.id !== wdgt.id); | ||
break; | ||
default: | ||
break; | ||
} | ||
this.price = this.widgets.reduce((acc, curr) => acc + curr.price, 0) | ||
} | ||
``` | ||
|
||
![image](https://github.com/lifebit-ai/web-application/assets/16644017/246a07a2-0436-44c0-a342-42853ad92ad2) | ||
|
||
On the surface, you could be tricked into thinking like this is okayish code. | ||
|
||
- I've used all the cool, immutable operators, | ||
- using ternary functions | ||
- using Fat arrow functions | ||
|
||
Now when we run the code: | ||
|
||
``` | ||
const testService = new RefactorService(); | ||
const testWidget = { id: 100, name: '', price: 100, description: '' }; | ||
const testWidgets = [{ id: 100, name: '', price: 200, description: '' }]; | ||
testService.widgets = testWidgets; | ||
testService.mode = 'create'; | ||
testService.reCalculateTotal(testWidget); | ||
testService.mode = 'update'; | ||
testService.reCalculateTotal(testWidget); | ||
testService.mode = 'delete'; | ||
testService.reCalculateTotal(testWidget); | ||
``` | ||
|
||
What are we doing to get as an output of this flow? A: It's really hard to come up with an answer. | ||
The issue is that, using the same parameter, you get different results, potentially every single time. | ||
|
||
#### Nested Logic | ||
|
||
![image](https://github.com/lifebit-ai/web-application/assets/16644017/5d514df2-95f9-4587-aa60-39e43076e0a0) | ||
|
||
Ironically, this is the EASIEST problem to solve. Via, DEPENDENCY INJECTION & the EXTRACT METHOD | ||
|
||
- First step (DEPENDENCY INJECTION): | ||
- Instead of using `switch (this.mode)...` we now should use `switch (mode)` | ||
- and the `mode` should be now passed as a parameter of the function as `updateCoursesAndRecalculateCosts(course: Course, mode: string)...` | ||
- Second step (EXTRACT): | ||
- Without breaking the enclosing code | ||
- We get three methods to test (eg.: createCourse, updateCourse, deleteCourse) | ||
- We get three methods we can reuse | ||
|
||
![image](https://github.com/lifebit-ai/web-application/assets/16644017/6c3333d3-2866-46a5-9148-6a25c904d496) | ||
|
||
Lesson 3: Flow | ||
- With this we're `managing state`, `flow control`, and `code volume` |
19 changes: 19 additions & 0 deletions
19
expert-learning-path/enterprice-architecture-patterns-0-class.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
class Inventory { | ||
ledger = { total: 1200 } | ||
} | ||
|
||
class ItemsComponent { | ||
ledger: any | ||
constructor(private inventory: Inventory) { | ||
this.ledger = inventory.ledger; | ||
} | ||
add(x) { this.ledger.total += x; } | ||
} | ||
|
||
class WidgetsComponent { | ||
ledger: any | ||
constructor(private inventory: Inventory) { | ||
this.ledger = inventory.ledger; | ||
} | ||
add(x) { this.ledger.total += x; } | ||
} |
23 changes: 23 additions & 0 deletions
23
expert-learning-path/enterprice-architecture-patterns-0.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
price; | ||
mode; | ||
widgets: Widget[]; | ||
|
||
reCalculateTotal(widget: Widget) { | ||
switch (this.mode) { | ||
case 'create': | ||
const newWidget = Object.assign({, widget, { id: uuidv4() }}); | ||
this.widgets = [...this.widgets, newWidget] | ||
break; | ||
case 'update': | ||
this.widgets = this.widgets.map(wdgt => | ||
(widget.id === wdgt.id) ? Object.assign({}, widget) : wdgt); | ||
break; | ||
case 'delete': | ||
this.widgets = this.widgets.filter(wdgt => widget.id !== wdgt.id); | ||
break; | ||
default: | ||
break; | ||
} | ||
|
||
this.price = this.widgets.reduce((acc, curr) => acc + curr.price, 0) | ||
} |
27 changes: 27 additions & 0 deletions
27
expert-learning-path/enterprice-architecture-patterns-1.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
updateCoursesAndRecalculateCost(course: Course, mode: string) { | ||
switch (mode) { | ||
case 'create': | ||
this.courses = this.createCourse(this.courses, course) | ||
break; | ||
case 'update': | ||
this.courses = this.updateCourse(this.courses, course) | ||
break; | ||
case 'delete': | ||
this.courses = this.deleteCourse(this.courses, course); | ||
break; | ||
default: | ||
break; | ||
} | ||
|
||
this.price = this.widgets.reduce((acc, curr) => acc + curr.price, 0) | ||
} | ||
|
||
createCourse(courses, course) { | ||
return [...courses, Object.assign({}, course, { id: uuidv4() })]; | ||
} | ||
updateCourse(courses, course) { | ||
return courses.map(_course => course.id === _course.id ? Object.assign({}, course) : _course) | ||
} | ||
deleteCourse(courses, course) { | ||
return courses.filter(_course => course.id !== _course.id) | ||
} |
29 changes: 29 additions & 0 deletions
29
expert-learning-path/enterprice-architecture-patterns-2.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
onCoursesUpdated(course, mode) { | ||
this.courses = this.updateCourses(this.courses, course, mode); | ||
this.totalCost = this.updateTotalCost(this.courses); | ||
} | ||
|
||
updateCourses(courses: Course[], course: Course, mode: string) { | ||
switch (mode) { | ||
case 'create': | ||
return this.createCourse(this.courses, course) | ||
case 'update': | ||
return this.updateCourse(this.courses, course) | ||
case 'delete': | ||
return this.deleteCourse(this.courses, course); | ||
default: | ||
return courses; | ||
} | ||
} | ||
|
||
updateTotalCost(courses) { return courses.reduce((acc, curr) => acc + curr.price, 0); } | ||
|
||
createCourse(courses, course) { | ||
return [...courses, Object.assign({}, course, { id: uuidv4() })]; | ||
} | ||
updateCourse(courses, course) { | ||
return courses.map(_course => course.id === _course.id ? Object.assign({}, course) : _course) | ||
} | ||
deleteCourse(courses, course) { | ||
return courses.filter(_course => course.id !== _course.id) | ||
} |