[TIPS] Refactoring - Clean Code - Tip 7 - Encapsulate Data and Behavior in Classes
By JoeVu, at: Aug. 15, 2024, 9:45 a.m.
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:
- Encapsulation: Group related data and behavior into classes, improving organization and maintainability.
- Modularity: Encapsulate functionality within classes, making the code easier to understand and extend.
- 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.