PART I: Ch 2: A Tale of Two Values
Design and Architecture are the same.
The first value of software behaviour is urgent but not always particularly important. The second value of software architecture is important but not never particularly urgent.
Eisenhower’s Matrix
“I have two kinds of problems, the urgent and the important. The urgent are not important, and the important are never urgent.”
– Dwight D. Eisenhower
- Architecture is more important than Features/Behaviour.
- A Program that works but is impossible to change is less valuable than a Program that doesn’t work but is easy to change.
PART II: Ch 3: Paradigm Overview
Each Paradigm removes capabilities from the programmer. The rules of software have not changed. Each software is composed of sequence, selection, iteration, and indirection.
Structured Programming
- Discovered by Edsger Wybe Dijkstra in 1968
- He showed that unrestrained jumps with
goto
are harmful to program structure - He replaced those jumps with
if/then/else
anddo/while/until
- Structured Programming imposes discipline on direct transfer of control
Object-Oriented Programming
- Discovered by Ole Johan Dahl and Kristen Nygaard in 1966
- The function call stack frame in the language
ALGOL
could be moved to a heap, allowing local variables declared by a function to exist after function return - This led to the discovery of polymorphism trough usage of function pointers
- Object-oriented programming imposes discipline on indirect transfer of control
Functional Programming
- while Discovered by Alonzo Church in 1936 through inventing -calculus
- -calculus was the foundation of the
LISP
language (invented 1958 by John McCarthy) - foundational notion: immutability → no assignment
- Function programming imposes discipline upon assignment
Ch 4: Structured Programming
Edger Wybe Dijkstra
- 1930: born in Rotterdam
- 1948: graduated with highest possible marks in math, physics, chemistry and biology
- 1952: took a job with the Mathematical Center of Amsterdam as the Netherlands’ very first programmer
- 1955: he concluded that the intellectual challenge of programming was greater than that of theoretical physics
- 1957: he married Maria Debets and stated his profession to be a theoretical physicist because programmer was not an accepted profession
- He wanted to apply the mathematical discipline of proof / constructing a Euclidean hierarchy of postulates, theorems, corollaries, and lemmas.
- bad uses of
goto
were removed from languages - instead of proofs, a scientific approach won
- The nature of scientific theories and laws: They are falsifiable but not provable.
“Testing shows the presence, no the absence, of bugs”
Functional Decomposition
- Recursively decomposing Modules into provable units
- Or: Breaking a large-scale problem statement into high level functions, which are broken down into lower level function, ad infinitum
- Foundation of disciplines like structured analysis and structured design
Ch 5: Object-Oriented Programming
Encapsulation
- Drawing a line around a cohesive set of data and functions.
- Outside of that line, data is hidden and only some functions are known
- C had perfect encapsulation - Users of the header file have no knowledge of the implementation
- C++ perfect encapsulation was broken - for technical reasons, member variables have to be declared in the header file
- Java/C# have even weaker encapsulation - no separation between declaration and definition of a class
Inheritance
- The redeclaration of a group of variables and functions within an enclosing scope
- Was also possible with C - If the order of fields in two structs are the same, one can masquerade as the other (This is how C++ does single inheritance)
- OO languages made inheritance more convenient
Polymorphism
- This already existed in C, of course.
The UNIX Operating system requires that each IO Device provide five standard functions:
open, close, read, write and seek
. - In C++, every
virtual
function within a class has a pointer in a table called avtable
, and all calls to virtual functions go through that table. Constructors of derivatives simply load their version of those functions into the vtable of the object being created. - Hence, Object-Orientation didn’t provide anything new, but it eliminated the need for conventions and their dangers.
Dependency Inversion
- OO enables that any source code dependency, no matter where it is, can be inverted (using an interface).
- Modules that contain high-level policies are independent of modules containing low-level details.
Ch 6: Functional Programming
- Immutability: non-changing variables
- All race conditions, deadlock conditions, and concurrent update problems are due to mutable variables
- Segregation of Mutability: Segregating the Application into Mutable & Immutable Components. It is wise to push as much processing as possible into the immutable components and to drive as much code as possible out of those components that must allow mutation.
- Event Sourcing: Instead of storing state, transactions are stored. When state is required, all transactions from the beginning of time are applied. Instead of CRUD, we have CR → more storage is required, however no concurrency issues can occur. Git uses this concept.
PART III: Design Principles
The SOLID Principles tell us how to arrange our functions and data structures into classes (coupled grouping of functions and data), and how those classes should be interconnected. SOLID is for mid-level structure design - applied by programmers on the module level
- SRP: Single Responsibility Principle - Each software module has one and only one reason to change
- OCP: Open-Closed Principle - Open for Extension, Closed for modification
- LSP: Liskov Substitution Principle - Parts adhering to the same contract must be interchangeable
- ISP: Interface Segregation Principle - Avoid depending on things that you don’t use
- DIP: Dependency Inversion Principle - Code with high-level policies should not depend on code that implements low-level details
Ch 7: SRP - Single Responsibility
“A module should have one, and only one, reason to change.”
Software systems are changed to satisfy users and stakeholders; those stakeholders are the “reason to change”.
“A module should be responsible to one, and only one, user or stakeholder.”
Some stakeholders might have the same reason to change it. So we are referring to a group, let’s call it an actor.
“A module should be responsible to one, and only one, actor.”
We want to separate the code that different actors depend on. This way we can reduce risk and merge conflicts.
At the component level, SRP becomes the Common Closure Principle. At an architectural level, it becomes the Axis of Change responsible for the creation of Architectural Boundaries.
Ch 8: OCP - Open Closed
- created 1988 by Betrand Meyer in “Object Oriented Software Construction”
- a system that is easy to exend without incurring a high impact of change
- partitioning the system into components
- arranging component into a dependency hierarchy that protects high-level components from changes in lower-level components
Ch 9: LISP - Liskovs Substitutuon
- only depend on things you need
- indirect dependence is also possible
Ch 10: DIP - Dependency Inversion
- depend on Abstractions, not concretions
- exceptions: stable concretions like java.lang.string
- don’t depend on volatile classes
- don’t derive of volatile classes
- don’t override concrete functions
- don’t mentions anything concrete/volatile
- Abstract Factories can be used to create Implementations of Interfaces and provide them to the higher level component
PART 4 - Component Principles
Components are the units of deployment.