Paradigms

Programming Paradigms

Since we will start be diving into architectural concepts, principles and patterns through the lens of Object-Oriented Programming (OOP), it's important to first understand what OOP is, how it compares to other paradigms, and why or if these differences matter.

What is a paradigm?

A programming paradigm is a fundamental style or approach to writing software, a "way of programming" if you will. Each paradigm provides a unique set of tools, constraints, guiding principles, methodologies and, of course, trade-offs that influence the way we write, design, develop and think about our software.

Different paradigms

Although we will mostly be focusing on Object Orientation Programming and a bit on Functional Programming from here on out, there exist many different paradigms:

Object Oriented

Organizes software design around objects, which are instances of classes. These objects encapsulate data and behaviors, promoting modularity, reusability, and scalability. OOP is particularly effective for modeling complex systems with interacting entities.

Supported Languages Example: Java, Javascript, C#, Python.

Functional

Functional Programming emphasizes immutability and pure functions, where functions produce the same output given the same input and have no side effects. It’s ideal for tasks require predictable outcomes, mathematical precision and parallelism.

Supported Languages Example: Haskell, Clojure, F#, Scala, Javascript.

Procedural

Is based on the concept of procedure calls, where the program is a sequence of instructions that tell the computer what to do step by step. It’s straightforward and well-suited for tasks that follow a linear process.

Supported Languages Example: C, Java, C#, BASIC

Constraint

Involves defining a set of constraints or conditions that a solution must satisfy. The programming system then searches for solutions that meet all these constraints, often used in optimization problems or systems where multiple conditions must be met.

Supported Languages Example: Prolog, MiniZinc

Logic

Is based on formal logic like (𝑝→𝑞)∧(¬𝑟→𝑞), where you define a set of rules and facts, and the system uses these to infer conclusions or find solutions. It’s particularly useful for problems involving knowledge representation, automated reasoning, and symbolic computation.

Supported Languages Example: Prolog

Why OOP?

You will often here OOP being criticized by functional programming purists online for it's boilerplate-y, shared state-y, hierarchical and complex code. So you might be wondering why are we focusing on OOP to begin with and not this superior functional programming.

These criticisms and concerns are valid so lets talk about them.

They hate us cuz they ain't us

Bjarne Stroustrup

"There are only two kinds of languages: the ones people complain about and the ones nobody uses."

Bjarne Stroustrup

Everything popular will generate hate and criticism in one way or another and OOP is no exception. There is a reason it is the most popular paradigm by far, Yes it has some criticizable components but those same components are also what give it it's strengths.

Trade-offs

As all potential architectural decisions, choosing a paradigm has it's trade-offs. Some paradigms are better suited for some teams as well as being more suited for some problems than others.

For example, OOP is great for building large, complex systems where we need to model real-world entities, like an online store or a social media platform. Functional programming, on the other hand, shines in situations where immutability, concurrency, and predictable behavior are paramount, such as handling large data streams or parallel processing.

OOP might have shareable state which isn't idle for concurrency but functional programming can have worse performance as well as a more complicated mental model.

So you see, the "OOP bad" mentality is wrong. We must choose the right tool for the job and as always when making an architectural decision, think about what is important as well as their trade-offs .

Patterns transcend paradigms

So yes we are choosing OOP for it's simplicity and wide adoption as our foundation for learning principles and patterns but that doesn't mean that these patterns don't also apply to other paradigms.

Many of the patterns and principles transcend paradigms and are applicable to a much wider scope and context. For example the Single Responsibility Principle applies to OOP, Functional and microservices. The Observer Pattern can be in the form of OO classes or in the form of asynchronous events like Kafka or RabbitMQ.

Therefore the foundation in which we learn these patterns and principles doesn't matter all that much, what matters is learning their abstract concepts and how to apply them in different scenarios.

Mixing and matching

While each paradigm has its own strengths and weaknesses, there's an increasing overlap between them in modern software development. Many programming languages (like Python, JavaScript, and even Java) now support multiple paradigms, allowing you to mix and match as needed. For example, in JavaScript, you can write object-oriented code, but also leverage functional techniques like higher-order functions or immutability. Allowing for a more flexible approach.