Quote of the Day

more Quotes

Categories

Get notified of new posts

Buy me coffee

  • Home>
  • C#>

Notes on the three programming paradigms

Published January 30, 2022 in Architecture , C# , Java , Object Oriented Programming - 0 Comments

In this post, I summarize the different programming paradigms which I learned from reading the book “Clean Architecture A Craftsman’s guide to Software Structure and Design” by Robert Martin. The programming paradigms are structured programming, object oriented programming and functional programming.

Structured Programming

In the late 1900s, Böhm–Jacopini proved that using just the three control structures which are sequences, selection and iteration is enough to build any program. This idea becomes the basis of structured programming. If a module is complex, programmers can break it into subunits that are small enough to develop using just the three control structures.

When developing a program, one of the goals is to keep the software modules small enough such that they are testable, following the ideas of structured programming.

The use of certain goto statements are discouraged because they inhibit decomposing a complex module into smaller units; thereby making it difficult to show correctness of the program through tests.

Object-Oriented Programming (OOP)

When talking about OOP, people usually refer to the three concepts: encapsulation, polymorphisms, and inheritance.

In the book, the author shows that Object Oriented (OO) concepts actually exist before OOP languages. For instance, C language provides mechanisms to implement polymorphism, encapsulation and inheritance. The author argues that C++ and Java have weaker encapsulation than in C. In C++, encapsulation is weaker because you need to declare variables in a header file. As such, the header still has some details about the implementation. In Java, you don’t even have a separation between implementation and header files.

Encapsulation

Objected Oriented (OO) languages provide mechanisms to hide or encapsulate the internal data and methods of a class or module. For instance, through the use of private accessor in C#, one can make a method or variable of a class inaccessible to outside of the class. This reduce the couplings of the program because the internal parts of the class can change without affecting other classes, provided the public interfaces remain the same.

Inheritance

Inheritance provides a mechanism to reuse codes. If a class inherits from another class, the inherited class contains all the non-private functionalities and variables from which it inherits, without having to redeclare the methods or variables.

Polymorphism

Poly means many. In real life, different things can appear in different forms. A person can be a student in school, a husband at home, an employee at work etc … A sofa bed can work as a sofa or a bed. In programming, polymorphisms occur when an object appears in multiple forms.

One way OOP supports polymorphism is through the use of interfaces and abstract classes. For example, in C#, a class can implement multiple interfaces and can work as an instance of any of the interfaces it implements.

Using polymorphism effectively can make reduce couplings in a program and make the codes more generic. For example, in Java, you can use the InputStream and OutputStream classes to copy data from a source to a destination. You don’t care whether the source comes from a remote location, a database, your local hard drive etc… as long as the class which models the source extends from the InputStream class and provides a read operation. Similarly, as long as the class which models the destination extends from the OutputStream class and provides a write operation, your program can work with it. In this way, the various sources and destinations act as plugins to your program.

Dependency Inversion

Before polymorphisms, source code dependencies follow the flow of control. Polymorphisms provides a mechanism to control or invert the direction of the dependencies in a way that reduces the couplings in a program.

Dependency Inversion means that you have a module that depends on an interface at compile time. At run time, you module calls an implementation of the interface. The flow of control at runtime is that the module is going to call the function in the implementation class. However, the flow of dependency is inverse in the sense that both the module and the implementation depend on the interface. The inversion can be demonstrated by a diagram.

In the above diagram, the yellow line shows the direction of the flow of control. The green lines show the direction of the dependencies at runtime. At runtime, A.F() calls G() of class C. However, class A does not have an explicit dependency on Class C. In fact, both Class A and Class C depend on the interface G() at runtime. But Class A does not depend on Class C at compile time. As you see, the direction of dependency is opposite to the direction of the flow; hence the name dependency inversion.

In clean architecture, the business logic does not need to depend directly on the codes that interface with external systems and databases. This is possible through dependency inversion.

OO is the ability, through 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.

Martin, Robert C. “Chapter 5 Object Oriented Programming.” Clean Architecture: A Craftsman’s Guide to Software Structure and Design, Pearson Education, Boston, MA, 2018.

Functional Programming

One of the main benefits of functional programming is immutability. Issues with concurrency often involve mutating a variable. If storage space and processing speed are abundant, we should always strive for immutability in our applications. However, often it’s not possible or practical to have 100% immutability, especially if you work with a non functional programming language. A compromise is to separate the components that mutate from the components that do not mutate, put as much logic into the immutable components, and protect the mutated variables.

All race conditions, deadlock conditions, and concurrent update problems are due to mutable variables.

Martin, Robert C. “Chapter 5 Object Oriented Programming.” Clean Architecture: A Craftsman’s Guide to Software Structure and Design, Pearson Education, Boston, MA, 2018.

Event Sourcing is the technique in which we store the transactions, events that change the data. If we need to retrieve the state of the data, we just apply those events from the beginning to the end. We can also store snapshots of the data and only apply the events starting from a snapshot. Using event sourcing is one way we can achieve immutability in our application as we never allow updates. We only allow inserts and reads.

In conclusion, the author argues that the three programming paradigms show us what not to do. Structure programming discourages the use of goto statements. Object oriented programming discourages a programmer from using function pointers. Functional programming discourages a programmer from mutating variables unnecessarily.

References

Clean Architecture: A Craftsman’s Guide to Software Structure and Design

OOP Concepts for Beginners: What is Polymorphism

No comments yet