Born in 1968.
Structured programming imposes discipline on direct transfer of control.
Born in 1966.
Object-oriented programming imposes discipline on indirect transfer of control.
Born in 1936.
Functional programming imposes discipline upon assignment.
Each imposes some kind of extra discipline that is negative in its intent. The paradigm tell us what not to do, more than they tell us what to do.
Here, I see sometimes that people discuss about what paradigm is the best - this type of discussion doesn't do anything. The good factors of paradigms can live together.
We use polymorphism as the mechanism to cross architectural boundaries; we use functional programming to impose discipline on the location of and access to data; and we use structured programming as the algorithmic foundation of our modules. Notice how well those three align with the three big concerns of architecture: function, separation of components, and data management.
About Dijkstra. Compared to his era - programming by punched cards, we can code way faster. We should embrace this and take advantage - keep making iteration faster.
Dijkstra's solution was to apply the mathematical discipline of proof. His vision was the construction of a Euclidian hierarchy of postulates, theorems, corollaries, and lemmas.
"Go To Statement Considered Harmful."
main() {
int i;
for (i = 1; i < 5; i++) {
printf("Index is %d", i);
if (i % 2) {
goto isEven;
}
}
isEven:
printf("The number %d is even", i);
return 0;
}
In that letter Dijkstra argued that unrestricted GOTO statements should be abolished from higher-level languages because they complicated the task of analyzing and verifying the correctness of programs (particularly those involving loops).
Structured programming allows modules to be recursively decomposed into provable units, which in turn means that modules can be functionally decomposed.
The "Go To Statement Considered Harmful." had never proved. Mathematical proofs are not the only strategy, another strategy is scientific method.
(T)he nature of scientific theories and laws: They are falsifiable but not provable.
"Testing shows the presence, not the absence, of bugs."
Such proofs of incorrectness can be applied only to provable programs. A program that is not provable - due to unrestrained use of
goto
, for example - cannot be correct no matter how many tests are applied to it.
If the module is not testable, we don't know if it's "provable" - that that is not even falsifiable.
It is this ability to create falsifiable units of programming that makes structured programming valuable today.
Software architects strive to define modules, components, and services that are easily falsifiable (testable). To do so, they employ restrictive disciplines similar to structured programming, albeit at a much higher level.
React.js is a good example here, The rise of functional components allow developers to code with a restrictive way by its design.
What's OO?
"The combination of data and function."
"A way to model the real world."
- Encapsulation
- Inheritance
- Polymorphism
(A) line can be drawn around a cohesive set of data and functions. Outside of that line, the data is hidden and only some of the functions are known.
With the C lang example, it tells that functions declared using Point
do not have knowledge of Point
.
Then, with the C++ example, encapsulation is done with public
, private
and protected
. The properties are private (encapsulated).
many OO languages have little or no enforced encapsulation.
Developers need to follow the team's "Coding Style Guide". e.g.,) class Foo { constructor (x) { this._x = x; } }
in JavaScript. The _x
is accessible.
We had a kind of inheritance before OO languages. OO languages made it easier to code inheritance without a trick.
polymorphism is an application of pointers and functions.
Then OO languages made polymorphism trivial - safe to use, easy to use.
The example with C is File handlers. Web APIs could also be, if we were to make more examples.
Thus, you don't need to know the details of IO devices, you don't need to recompile every time. IO devices are the plugins to the code.
OO allows the plugin architecture to be used anywhere, for anything.
- Fig 5.1 - Source code dependencies versus flow of control
every caller was forced to mention the name of the modules that contained the callee.
The flow of control was dictated by behavior of the system, and the source code dependencies were dictated by that flow of control.
- Fig 5.2 - Dependency inversion
The fact that OO languages provide safe and convenient polymorphism means that any source code dependency, no matter where it is, can be inverted.
Example - arrangement of code modules (UI, Business Rules, Database)A
- Fig 5.3 - The database and the user interface depend on the business rules
The UI and the DB can be plugins. The business logic doesn't need to know UI's / DB's details.
- Question - Any DI's benefit other than with tests?
- Question - Does anyone take advantages of independent deployability?
OO is the ability, though the use of polymorphism, to gain absolute control over every source code dependency in the system. It allows the architect to create a plugin architecture, in which modules that contain high-level policies are independent of modules that contain low-level details.
This leads us to a surprise statement: Variables in functional languages do not vary.
^ <3
Why would an architect be concerned with the mutability of variables? The answer is absurdly simple: All race conditions, deadlock conditions, and concurrent update problems are due to mutable variables.
The question you must be asking yourself, then, is whether immutability is practicable.
The answer to that question is affirmative, if you have infinite storage and infinite processor speed. Lacking those infinite resources, the answer is a bit more nuanced. Yes, immutability can be practicable, if certain compromises are made.
One of the most common compromises is to segregate the application, or the services within the application, into mutable and immutable components.
React hooks, for example. State management and side effects are defined in hooks.
Now imagine that instead of storing the account balances, we store only the transactions. Whenever anyone wants to know the balance of an account, we simply add up all the transactions for that account, from the beginning of time. This scheme requires no mutable variables.
Event sourcing is a strategy wherein we store the transactions, but not the state. When state is required, we simply apply all the transactions from the beginning of time.
Question: Does anyone take this in their app?
Software is not a rapidly advancing technology. The rules of software are the same today as they were in 1946
The rise of frameworks / new stack are quick, but the principles are steady.