[TIPS] Refactoring - Clean Code - Tip 7 - Encapsulate Data and Behavior in Classes

By JoeVu, at: Aug. 15, 2024, 9:45 a.m.

Estimated Reading Time: 5 min read

[TIPS] Refactoring - Clean Code - Tip 7 - Encapsulate Data and Behavior in Classes
[TIPS] Refactoring - Clean Code - Tip 7 - Encapsulate Data and Behavior in Classes

Refactoring Tip 7: Encapsulate Data and Behavior in Classes

  • Junior: Might use procedural programming without leveraging classes.

  • Senior: Uses classes to encapsulate related data and behavior, following principles of Object-Oriented Programming (OOP).


Encapsulating data and behavior in classes is crucial for writing organized and maintainable code. Here's an example to illustrate the difference between how a junior and a senior developer might approach this principle.

 

Example 1: Handling User Data


Junior Developer's Approach

A junior developer might use procedural programming to handle user data:

users = []

def add_user(name, age):
    users.append({'name': name, 'age': age})

def get_user(name):
    for user in users:
        if user['name'] == name:
            return user
    return None

def update_user(name, age):
    for user in users:
        if user['name'] == name:
            user['age'] = age

# Usage
add_user('Alice', 30)
add_user('Bob', 25)
print(get_user('Alice'))
update_user('Alice', 31)
print(get_user('Alice'))

 

Senior Developer's Approach

A senior developer would use classes to encapsulate user data and behavior:

class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def update_age(self, age):
        self.age = age


class UserManager:
    def __init__(self):
        self.users = []

    def add_user(self, name, age):
        self.users.append(User(name, age))

    def get_user(self, name):
        for user in self.users:
            if user.name == name:
                return user
        return None

    def update_user(self, name, age):
        user = self.get_user(name)
        if user:
            user.update_age(age)

# Usage
user_manager = UserManager()
user_manager.add_user('Alice', 30)
user_manager.add_user('Bob', 25)
print(vars(user_manager.get_user('Alice')))
user_manager.update_user('Alice', 31)
print(vars(user_manager.get_user('Alice')))

 

Example 2: Managing a Library System


Junior Developer's Approach

A junior developer might use procedural programming to manage a library system:

books = []

def add_book(title, author):
    books.append({'title': title, 'author': author})

def get_book(title):
    for book in books:
        if book['title'] == title:
            return book
    return None

def remove_book(title):
    global books
    books = [book for book in books if book['title'] != title]

# Usage
add_book('1984', 'George Orwell')
add_book('To Kill a Mockingbird', 'Harper Lee')
print(get_book('1984'))
remove_book('1984')
print(get_book('1984'))


Senior Developer's Approach

A senior developer would use classes to encapsulate book data and library management behavior:

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

class Library:
    def __init__(self):
        self.books = []

    def add_book(self, title, author):
        self.books.append(Book(title, author))

    def get_book(self, title):
        for book in self.books:
            if book.title == title:
                return book
        return None

    def remove_book(self, title):
        self.books = [book for book in self.books if book.title != title]

# Usage
library = Library()
library.add_book('1984', 'George Orwell')
library.add_book('To Kill a Mockingbird', 'Harper Lee')
print(vars(library.get_book('1984')))
library.remove_book('1984')
print(library.get_book('1984'))

 

Example 3: Processing Orders


Junior Developer's Approach

A junior developer might use procedural programming to process orders:

orders = []

def add_order(order_id, amount):
    orders.append({'order_id': order_id, 'amount': amount})

def get_order(order_id):
    for order in orders:
        if order['order_id'] == order_id:
            return order
    return None

def update_order(order_id, amount):
    for order in orders:
        if order['order_id'] == order_id:
            order['amount'] = amount

# Usage
add_order(1, 100)
add_order(2, 150)
print(get_order(1))
update_order(1, 120)
print(get_order(1))

 

Senior Developer's Approach

A senior developer would use classes to encapsulate order data and processing behavior:

class Order:
    def __init__(self, order_id, amount):
        self.order_id = order_id
        self.amount = amount

    def update_amount(self, amount):
        self.amount = amount

class OrderManager:
    def __init__(self):
        self.orders = []

    def add_order(self, order_id, amount):
        self.orders.append(Order(order_id, amount))

    def get_order(self, order_id):
        for order in self.orders:
            if order.order_id == order_id:
                return order
        return None

    def update_order(self, order_id, amount):
        order = self.get_order(order_id)
        if order:
            order.update_amount(amount)

# Usage
order_manager = OrderManager()
order_manager.add_order(1, 100)
order_manager.add_order(2, 150)
print(vars(order_manager.get_order(1)))
order_manager.update_order(1, 120)
print(vars(order_manager.get_order(1)))

 

Key Improvements:

  1. Encapsulation: Group related data and behavior into classes, improving organization and maintainability.
     
  2. Modularity: Encapsulate functionality within classes, making the code easier to understand and extend.
     
  3. Object-Oriented Principles: Follow OOP principles to create reusable and scalable code.
     

Encapsulating data and behavior in classes leads to better organization, easier maintenance, and scalability of the codebase. These examples demonstrate how a senior developer's approach to refactoring can lead to more structured and maintainable code.


Subscribe

Subscribe to our newsletter and never miss out lastest news.