Programming paradigms - Procedural vs Object Oriented Programming
Table of Content
Types of Programming paradigms
Imperative:
- an imperative program consists of commands for the computer to perform to change state e.g. C, Java, Python, etc.
Declarative:
- focuses on what the program should accomplish without specifying all the details of how the program should achieve the result e.g. SQL, Lisp, etc.
Imperative
Procedural programming:
- programming paradigm that uses a sequence of steps to solve a problem.
- based on the concept of the procedure call.
- Procedures (a type of routine or subroutine) simply contain a series of computational steps to be carried out.
- Any given procedure might be called at any point during a program’s execution, including by other procedures or itself.
- Think of all programming as managing the relationship between two fundamental concepts: state and behavior. State is the data of your program. Behavior is the logic.
- State is held in data structures. Behavior is held in functions (also known as procedures or subroutines). A procedural application therefore passes data structures into functions to produce some output.
- Eg: Imagine you want to transfer some money from one account to another. These are the following steps that you would take:
- Open the source account
- Withdraw the money
- Open the destination account
- Deposit the money in destination account
- A procedural version of this program would be:
Python Procedural Code
1
2
3
4
5
6
7
8
9
10
11
def transfer(source: int, destination: int, amount: int) -> None:
source_account = get_account(source)
update_account(source_account, -amount)
destination_account = get_account(destination)
update_account(destination_account, amount)
def get_account(number: int) -> dict:
return list(filter(lambda account: account['number'] == number,accounts))[0]
def update_account(account: int, delta: int) -> None:
account['balance'] += delta
Object Oriented Programming
- programming paradigm that uses objects to model real-world things and aims to implement state and behavior using objects.
- State and behaviour are combined into one new concept: an Object. An OO application can therefore produce some output by calling an Object, without needing to pass data structures.
- Advantages of OO include the potential for information hiding: if a caller needn’t pass any data structure, then the caller needn’t be aware of any data structure, and can therefore be completely decoupled from the data format.
Abstraction:
- process of hiding the implementation details of a program from the user.
Advantages of Abstraction
- used to create a boundary between the application and the client code.
- separate responsibilities into software entities (classes, method, etc.) that only know the required functionality of each other but not how that functionality is implemented.
- It allows the programmer to change the internal implementation of methods or concrete classes without hampering the interface.
- increase the code security as only relevant details will be provided to users.
Encapsulation:
- used to hide the values or state of a structured data object inside a class,
- preventing direct access to them by clients in a way that could expose hidden implementation details or violate state invariance maintained by the methods.
Advantages of Encapsulation:
- Hiding Data - Users will have no idea how classes are being implemented or stored. All that users will know is that values are being passed and initialized.
- More Flexibility - Enables you to set variables as read or write-only.
- Easy to Reuse - With encapsulation it’s easy to change and adapt to new requirements.
Class:
- is a blueprint which you use to create objects.
Object:
- is an instance of a class.
Java OOP Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class OopBankAccount {
private Integer number;
private Integer balance;
public OopBankAccount(Integer number, Integer balance) {
this.number = number;
this.balance = balance;
}
void deposit(Integer amount) {
this.balance += amount;
}
void withdraw(Integer amount) {
this.balance += amount;
}
void transfer(OopBankAccount destination, Integer amount) {
this.withdraw(amount);
destination.deposit(amount);
}
}
Python OOP Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class OopBankAccount:
def __init__(self, balance, number):
self.__number = number
self.__balance = balance
def getNumber(self):
return self.__number
def setNumber(self, number):
self.__number = number
def getBalance(self):
return self.__balance
def setBalance(self, balance):
self.__balance = balance
def deposit(self, amount):
self.__balance += amount
def withdraw(self, amount):
self.__balance -= amount
def transfer(self, destination, amount):
self.withdraw(amount)
destination.deposit(amount)
Golang Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
type BankAccount struct {
AccountNumber int64
Name string
Balance float64
}
type BankAccountOps interface {
GetAccountNumber() (int64, error)
GetName() (string, error)
GetBalance() (float64, error)
Deposit(amount float64) error
Withdraw(amount float64) error
Transfer(destiantion *BankAccount, amount float64) error
PrintBalance()
}
func NewBankAccount(number int64, name string, balance float64) *BankAccount {
return &BankAccount{AccountNumber: number, Name: name, Balance: balance}
}
func (b *BankAccount) GetAccountNumber() (int64, error) {
return b.AccountNumber, nil
}
func (b *BankAccount) GetName() (string, error) {
return b.Name, nil
}
func (b *BankAccount) GetBalance() (float64, error) {
return b.Balance, nil
}
func (b *BankAccount) Deposit(amount float64) error {
b.Balance += amount
return nil
}
func (b *BankAccount) Withdraw(amount float64) error {
b.Balance -= amount
return nil
}
func (b *BankAccount) Transfer(destiantion *BankAccount, amount float64) error {
b.Withdraw(amount)
destiantion.Deposit(amount)
return nil
}
func (b *BankAccount) PrintBalance() {
fmt.Println(b.AccountNumber, b.Name, b.Balance)
}
Advantages:
- Reusability: Through classes and objects, and inheritance of common attributes and functions.
- Security: Hiding and protecting information through encapsulation.
- Maintenance: Easy to make changes without affecting existing objects much.
- Inheritance: Easy to import required functionality from libraries and customize them, thanks to inheritance.
Disadvantages:
- Beforehand planning of entities that should be modeled as classes.
- OOPS programs are usually larger than those of other paradigms.
- Banana-gorilla problem - You wanted a banana but what you got was a gorilla holding the banana and the entire jungle
These are my notes from the Low-Level Design (LLD) course I took at Scaler.
Check Python, Java and Go code on Github Repo