Refactoring Techniques - Complete Guide
Published: September 25, 2024 | Reading time: 28 minutes
Refactoring Overview
Refactoring techniques improve code quality without changing functionality:
Refactoring Benefits
# Refactoring Benefits
- Improved code readability
- Better maintainability
- Reduced complexity
- Enhanced performance
- Easier testing
- Better code organization
- Reduced technical debt
Extract Method Refactoring
Extract Method Implementation
Extract Method Refactoring
# Extract Method Refactoring
# Before: Long method with multiple responsibilities
class OrderProcessor {
processOrder(order) {
// Validate order
if (!order.customerId) {
throw new Error('Customer ID is required');
}
if (!order.items || order.items.length === 0) {
throw new Error('Order must have at least one item');
}
if (!order.shippingAddress) {
throw new Error('Shipping address is required');
}
// Calculate total
let total = 0;
for (const item of order.items) {
total += item.price * item.quantity;
}
// Apply discounts
if (order.customerId && order.customerId.startsWith('VIP')) {
total *= 0.9; // 10% discount for VIP customers
}
if (order.items.length >= 5) {
total *= 0.95; // 5% discount for bulk orders
}
// Calculate tax
const taxRate = 0.08;
const tax = total * taxRate;
const finalTotal = total + tax;
// Create order record
const orderRecord = {
id: this.generateOrderId(),
customerId: order.customerId,
items: order.items,
subtotal: total,
tax: tax,
total: finalTotal,
shippingAddress: order.shippingAddress,
status: 'pending',
createdAt: new Date()
};
// Save to database
this.database.save('orders', orderRecord);
// Send confirmation email
const emailContent = `Order ${orderRecord.id} has been placed successfully. Total: $${finalTotal.toFixed(2)}`;
this.emailService.send(order.customerEmail, 'Order Confirmation', emailContent);
// Update inventory
for (const item of order.items) {
this.inventoryService.updateStock(item.productId, -item.quantity);
}
return orderRecord;
}
generateOrderId() {
return 'ORD-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
}
}
# After: Extracted methods with single responsibilities
class OrderProcessor {
processOrder(order) {
this.validateOrder(order);
const subtotal = this.calculateSubtotal(order.items);
const discountedTotal = this.applyDiscounts(subtotal, order);
const finalTotal = this.calculateFinalTotal(discountedTotal);
const orderRecord = this.createOrderRecord(order, subtotal, finalTotal);
this.saveOrder(orderRecord);
this.sendConfirmationEmail(order, orderRecord);
this.updateInventory(order.items);
return orderRecord;
}
validateOrder(order) {
if (!order.customerId) {
throw new Error('Customer ID is required');
}
if (!order.items || order.items.length === 0) {
throw new Error('Order must have at least one item');
}
if (!order.shippingAddress) {
throw new Error('Shipping address is required');
}
}
calculateSubtotal(items) {
let total = 0;
for (const item of items) {
total += item.price * item.quantity;
}
return total;
}
applyDiscounts(subtotal, order) {
let discountedTotal = subtotal;
if (order.customerId && order.customerId.startsWith('VIP')) {
discountedTotal *= 0.9; // 10% discount for VIP customers
}
if (order.items.length >= 5) {
discountedTotal *= 0.95; // 5% discount for bulk orders
}
return discountedTotal;
}
calculateFinalTotal(discountedTotal) {
const taxRate = 0.08;
const tax = discountedTotal * taxRate;
return discountedTotal + tax;
}
createOrderRecord(order, subtotal, finalTotal) {
const tax = finalTotal - subtotal;
return {
id: this.generateOrderId(),
customerId: order.customerId,
items: order.items,
subtotal: subtotal,
tax: tax,
total: finalTotal,
shippingAddress: order.shippingAddress,
status: 'pending',
createdAt: new Date()
};
}
saveOrder(orderRecord) {
this.database.save('orders', orderRecord);
}
sendConfirmationEmail(order, orderRecord) {
const emailContent = `Order ${orderRecord.id} has been placed successfully. Total: $${orderRecord.total.toFixed(2)}`;
this.emailService.send(order.customerEmail, 'Order Confirmation', emailContent);
}
updateInventory(items) {
for (const item of items) {
this.inventoryService.updateStock(item.productId, -item.quantity);
}
}
generateOrderId() {
return 'ORD-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
}
}
# Extract Method with Parameters
# Before: Hardcoded values and repeated logic
class UserService {
createUser(userData) {
// Validate email
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(userData.email)) {
throw new Error('Invalid email format');
}
// Validate password
if (userData.password.length < 8) {
throw new Error('Password must be at least 8 characters');
}
// Hash password
const hashedPassword = this.hashPassword(userData.password);
// Create user
const user = {
id: this.generateUserId(),
email: userData.email,
password: hashedPassword,
name: userData.name,
createdAt: new Date(),
isActive: true
};
// Save user
this.database.save('users', user);
// Send welcome email
const emailContent = `Welcome ${userData.name}! Your account has been created successfully.`;
this.emailService.send(userData.email, 'Welcome', emailContent);
return user;
}
updateUser(userId, userData) {
// Validate email if provided
if (userData.email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(userData.email)) {
throw new Error('Invalid email format');
}
}
// Validate password if provided
if (userData.password) {
if (userData.password.length < 8) {
throw new Error('Password must be at least 8 characters');
}
}
// Hash password if provided
if (userData.password) {
userData.password = this.hashPassword(userData.password);
}
// Update user
const user = this.database.findById('users', userId);
if (!user) {
throw new Error('User not found');
}
Object.assign(user, userData);
user.updatedAt = new Date();
this.database.save('users', user);
return user;
}
}
# After: Extracted validation methods
class UserService {
createUser(userData) {
this.validateEmail(userData.email);
this.validatePassword(userData.password);
const hashedPassword = this.hashPassword(userData.password);
const user = {
id: this.generateUserId(),
email: userData.email,
password: hashedPassword,
name: userData.name,
createdAt: new Date(),
isActive: true
};
this.database.save('users', user);
this.sendWelcomeEmail(userData.email, userData.name);
return user;
}
updateUser(userId, userData) {
if (userData.email) {
this.validateEmail(userData.email);
}
if (userData.password) {
this.validatePassword(userData.password);
userData.password = this.hashPassword(userData.password);
}
const user = this.database.findById('users', userId);
if (!user) {
throw new Error('User not found');
}
Object.assign(user, userData);
user.updatedAt = new Date();
this.database.save('users', user);
return user;
}
validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw new Error('Invalid email format');
}
}
validatePassword(password) {
if (password.length < 8) {
throw new Error('Password must be at least 8 characters');
}
}
sendWelcomeEmail(email, name) {
const emailContent = `Welcome ${name}! Your account has been created successfully.`;
this.emailService.send(email, 'Welcome', emailContent);
}
}
Extract Class Refactoring
Extract Class Implementation
Extract Class Refactoring
# Extract Class Refactoring
# Before: Large class with multiple responsibilities
class UserManager {
constructor() {
this.database = new Database();
this.emailService = new EmailService();
this.logger = new Logger();
this.cache = new Cache();
this.validator = new Validator();
}
// User management
createUser(userData) {
this.validator.validateUser(userData);
const user = this.database.save('users', userData);
this.cache.set(`user:${user.id}`, user);
this.logger.info(`User created: ${user.id}`);
return user;
}
getUserById(id) {
let user = this.cache.get(`user:${id}`);
if (!user) {
user = this.database.findById('users', id);
if (user) {
this.cache.set(`user:${id}`, user);
}
}
return user;
}
updateUser(id, userData) {
this.validator.validateUser(userData);
const user = this.database.update('users', id, userData);
this.cache.set(`user:${id}`, user);
this.logger.info(`User updated: ${id}`);
return user;
}
deleteUser(id) {
this.database.delete('users', id);
this.cache.delete(`user:${id}`);
this.logger.info(`User deleted: ${id}`);
}
// Email management
sendWelcomeEmail(user) {
const template = this.getEmailTemplate('welcome');
const content = this.renderTemplate(template, { name: user.name });
this.emailService.send(user.email, 'Welcome', content);
this.logger.info(`Welcome email sent to: ${user.email}`);
}
sendPasswordResetEmail(user) {
const template = this.getEmailTemplate('password-reset');
const resetToken = this.generateResetToken();
const content = this.renderTemplate(template, {
name: user.name,
resetToken: resetToken
});
this.emailService.send(user.email, 'Password Reset', content);
this.logger.info(`Password reset email sent to: ${user.email}`);
}
sendNotificationEmail(user, message) {
const template = this.getEmailTemplate('notification');
const content = this.renderTemplate(template, {
name: user.name,
message: message
});
this.emailService.send(user.email, 'Notification', content);
this.logger.info(`Notification email sent to: ${user.email}`);
}
// Template management
getEmailTemplate(templateName) {
const templates = {
'welcome': 'Welcome {{name}}! Your account has been created.',
'password-reset': 'Hello {{name}}, click here to reset your password: {{resetToken}}',
'notification': 'Hello {{name}}, {{message}}'
};
return templates[templateName];
}
renderTemplate(template, data) {
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
return data[key] || match;
});
}
generateResetToken() {
return Math.random().toString(36).substr(2, 9);
}
// Cache management
clearUserCache(id) {
this.cache.delete(`user:${id}`);
}
clearAllUserCache() {
this.cache.clear();
}
// Logging
logUserAction(userId, action) {
this.logger.info(`User ${userId} performed action: ${action}`);
}
}
# After: Extracted classes with single responsibilities
class UserManager {
constructor() {
this.userRepository = new UserRepository();
this.emailService = new UserEmailService();
this.userCache = new UserCache();
this.userLogger = new UserLogger();
}
createUser(userData) {
const user = this.userRepository.create(userData);
this.userCache.set(user.id, user);
this.userLogger.logUserAction(user.id, 'created');
return user;
}
getUserById(id) {
let user = this.userCache.get(id);
if (!user) {
user = this.userRepository.findById(id);
if (user) {
this.userCache.set(id, user);
}
}
return user;
}
updateUser(id, userData) {
const user = this.userRepository.update(id, userData);
this.userCache.set(id, user);
this.userLogger.logUserAction(id, 'updated');
return user;
}
deleteUser(id) {
this.userRepository.delete(id);
this.userCache.delete(id);
this.userLogger.logUserAction(id, 'deleted');
}
sendWelcomeEmail(user) {
this.emailService.sendWelcomeEmail(user);
}
sendPasswordResetEmail(user) {
this.emailService.sendPasswordResetEmail(user);
}
sendNotificationEmail(user, message) {
this.emailService.sendNotificationEmail(user, message);
}
}
# Extracted UserRepository class
class UserRepository {
constructor() {
this.database = new Database();
this.validator = new Validator();
}
create(userData) {
this.validator.validateUser(userData);
return this.database.save('users', userData);
}
findById(id) {
return this.database.findById('users', id);
}
update(id, userData) {
this.validator.validateUser(userData);
return this.database.update('users', id, userData);
}
delete(id) {
this.database.delete('users', id);
}
}
# Extracted UserEmailService class
class UserEmailService {
constructor() {
this.emailService = new EmailService();
this.templateEngine = new TemplateEngine();
this.logger = new Logger();
}
sendWelcomeEmail(user) {
const template = this.templateEngine.getTemplate('welcome');
const content = this.templateEngine.render(template, { name: user.name });
this.emailService.send(user.email, 'Welcome', content);
this.logger.info(`Welcome email sent to: ${user.email}`);
}
sendPasswordResetEmail(user) {
const template = this.templateEngine.getTemplate('password-reset');
const resetToken = this.generateResetToken();
const content = this.templateEngine.render(template, {
name: user.name,
resetToken: resetToken
});
this.emailService.send(user.email, 'Password Reset', content);
this.logger.info(`Password reset email sent to: ${user.email}`);
}
sendNotificationEmail(user, message) {
const template = this.templateEngine.getTemplate('notification');
const content = this.templateEngine.render(template, {
name: user.name,
message: message
});
this.emailService.send(user.email, 'Notification', content);
this.logger.info(`Notification email sent to: ${user.email}`);
}
generateResetToken() {
return Math.random().toString(36).substr(2, 9);
}
}
# Extracted TemplateEngine class
class TemplateEngine {
constructor() {
this.templates = {
'welcome': 'Welcome {{name}}! Your account has been created.',
'password-reset': 'Hello {{name}}, click here to reset your password: {{resetToken}}',
'notification': 'Hello {{name}}, {{message}}'
};
}
getTemplate(templateName) {
return this.templates[templateName];
}
render(template, data) {
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
return data[key] || match;
});
}
}
# Extracted UserCache class
class UserCache {
constructor() {
this.cache = new Cache();
}
set(id, user) {
this.cache.set(`user:${id}`, user);
}
get(id) {
return this.cache.get(`user:${id}`);
}
delete(id) {
this.cache.delete(`user:${id}`);
}
clear() {
this.cache.clear();
}
}
# Extracted UserLogger class
class UserLogger {
constructor() {
this.logger = new Logger();
}
logUserAction(userId, action) {
this.logger.info(`User ${userId} performed action: ${action}`);
}
}
Replace Conditional with Polymorphism
Polymorphism Refactoring
Replace Conditional with Polymorphism
# Replace Conditional with Polymorphism
# Before: Large conditional statements
class PaymentProcessor {
processPayment(payment, paymentType) {
if (paymentType === 'credit_card') {
// Credit card processing
if (!payment.cardNumber || !payment.expiryDate || !payment.cvv) {
throw new Error('Credit card details are required');
}
const cardValidator = new CreditCardValidator();
if (!cardValidator.validate(payment.cardNumber)) {
throw new Error('Invalid credit card number');
}
const processor = new CreditCardProcessor();
const result = processor.charge(payment.amount, payment.cardNumber, payment.expiryDate, payment.cvv);
if (result.success) {
this.logTransaction(payment, 'credit_card', result.transactionId);
return { success: true, transactionId: result.transactionId };
} else {
throw new Error('Credit card payment failed: ' + result.error);
}
} else if (paymentType === 'paypal') {
// PayPal processing
if (!payment.paypalEmail) {
throw new Error('PayPal email is required');
}
const processor = new PayPalProcessor();
const result = processor.charge(payment.amount, payment.paypalEmail);
if (result.success) {
this.logTransaction(payment, 'paypal', result.transactionId);
return { success: true, transactionId: result.transactionId };
} else {
throw new Error('PayPal payment failed: ' + result.error);
}
} else if (paymentType === 'bank_transfer') {
// Bank transfer processing
if (!payment.accountNumber || !payment.routingNumber) {
throw new Error('Bank account details are required');
}
const processor = new BankTransferProcessor();
const result = processor.charge(payment.amount, payment.accountNumber, payment.routingNumber);
if (result.success) {
this.logTransaction(payment, 'bank_transfer', result.transactionId);
return { success: true, transactionId: result.transactionId };
} else {
throw new Error('Bank transfer failed: ' + result.error);
}
} else if (paymentType === 'cryptocurrency') {
// Cryptocurrency processing
if (!payment.walletAddress) {
throw new Error('Wallet address is required');
}
const processor = new CryptoProcessor();
const result = processor.charge(payment.amount, payment.walletAddress);
if (result.success) {
this.logTransaction(payment, 'cryptocurrency', result.transactionId);
return { success: true, transactionId: result.transactionId };
} else {
throw new Error('Cryptocurrency payment failed: ' + result.error);
}
} else {
throw new Error('Unsupported payment type: ' + paymentType);
}
}
logTransaction(payment, type, transactionId) {
const log = {
type: type,
amount: payment.amount,
transactionId: transactionId,
timestamp: new Date()
};
this.logger.log(log);
}
}
# After: Polymorphic payment processors
class PaymentProcessor {
constructor() {
this.paymentHandlers = {
'credit_card': new CreditCardPaymentHandler(),
'paypal': new PayPalPaymentHandler(),
'bank_transfer': new BankTransferPaymentHandler(),
'cryptocurrency': new CryptoPaymentHandler()
};
this.logger = new Logger();
}
processPayment(payment, paymentType) {
const handler = this.paymentHandlers[paymentType];
if (!handler) {
throw new Error('Unsupported payment type: ' + paymentType);
}
const result = handler.process(payment);
this.logTransaction(payment, paymentType, result.transactionId);
return result;
}
logTransaction(payment, type, transactionId) {
const log = {
type: type,
amount: payment.amount,
transactionId: transactionId,
timestamp: new Date()
};
this.logger.log(log);
}
}
# Abstract PaymentHandler
class PaymentHandler {
process(payment) {
throw new Error('process method must be implemented');
}
validate(payment) {
throw new Error('validate method must be implemented');
}
}
# Credit Card Payment Handler
class CreditCardPaymentHandler extends PaymentHandler {
process(payment) {
this.validate(payment);
const cardValidator = new CreditCardValidator();
if (!cardValidator.validate(payment.cardNumber)) {
throw new Error('Invalid credit card number');
}
const processor = new CreditCardProcessor();
const result = processor.charge(payment.amount, payment.cardNumber, payment.expiryDate, payment.cvv);
if (result.success) {
return { success: true, transactionId: result.transactionId };
} else {
throw new Error('Credit card payment failed: ' + result.error);
}
}
validate(payment) {
if (!payment.cardNumber || !payment.expiryDate || !payment.cvv) {
throw new Error('Credit card details are required');
}
}
}
# PayPal Payment Handler
class PayPalPaymentHandler extends PaymentHandler {
process(payment) {
this.validate(payment);
const processor = new PayPalProcessor();
const result = processor.charge(payment.amount, payment.paypalEmail);
if (result.success) {
return { success: true, transactionId: result.transactionId };
} else {
throw new Error('PayPal payment failed: ' + result.error);
}
}
validate(payment) {
if (!payment.paypalEmail) {
throw new Error('PayPal email is required');
}
}
}
# Bank Transfer Payment Handler
class BankTransferPaymentHandler extends PaymentHandler {
process(payment) {
this.validate(payment);
const processor = new BankTransferProcessor();
const result = processor.charge(payment.amount, payment.accountNumber, payment.routingNumber);
if (result.success) {
return { success: true, transactionId: result.transactionId };
} else {
throw new Error('Bank transfer failed: ' + result.error);
}
}
validate(payment) {
if (!payment.accountNumber || !payment.routingNumber) {
throw new Error('Bank account details are required');
}
}
}
# Cryptocurrency Payment Handler
class CryptoPaymentHandler extends PaymentHandler {
process(payment) {
this.validate(payment);
const processor = new CryptoProcessor();
const result = processor.charge(payment.amount, payment.walletAddress);
if (result.success) {
return { success: true, transactionId: result.transactionId };
} else {
throw new Error('Cryptocurrency payment failed: ' + result.error);
}
}
validate(payment) {
if (!payment.walletAddress) {
throw new Error('Wallet address is required');
}
}
}
# Another example: Employee salary calculation
# Before: Conditional salary calculation
class SalaryCalculator {
calculateSalary(employee) {
if (employee.type === 'manager') {
return employee.baseSalary + (employee.baseSalary * 0.2) + employee.bonus;
} else if (employee.type === 'developer') {
return employee.baseSalary + (employee.baseSalary * 0.1) + employee.overtimePay;
} else if (employee.type === 'sales') {
return employee.baseSalary + (employee.baseSalary * 0.15) + employee.commission;
} else if (employee.type === 'intern') {
return employee.baseSalary * 0.5;
} else {
return employee.baseSalary;
}
}
}
# After: Polymorphic salary calculation
class SalaryCalculator {
calculateSalary(employee) {
const calculator = this.getSalaryCalculator(employee.type);
return calculator.calculate(employee);
}
getSalaryCalculator(type) {
const calculators = {
'manager': new ManagerSalaryCalculator(),
'developer': new DeveloperSalaryCalculator(),
'sales': new SalesSalaryCalculator(),
'intern': new InternSalaryCalculator(),
'default': new DefaultSalaryCalculator()
};
return calculators[type] || calculators['default'];
}
}
# Abstract SalaryCalculator
class EmployeeSalaryCalculator {
calculate(employee) {
throw new Error('calculate method must be implemented');
}
}
# Manager Salary Calculator
class ManagerSalaryCalculator extends EmployeeSalaryCalculator {
calculate(employee) {
return employee.baseSalary + (employee.baseSalary * 0.2) + employee.bonus;
}
}
# Developer Salary Calculator
class DeveloperSalaryCalculator extends EmployeeSalaryCalculator {
calculate(employee) {
return employee.baseSalary + (employee.baseSalary * 0.1) + employee.overtimePay;
}
}
# Sales Salary Calculator
class SalesSalaryCalculator extends EmployeeSalaryCalculator {
calculate(employee) {
return employee.baseSalary + (employee.baseSalary * 0.15) + employee.commission;
}
}
# Intern Salary Calculator
class InternSalaryCalculator extends EmployeeSalaryCalculator {
calculate(employee) {
return employee.baseSalary * 0.5;
}
}
# Default Salary Calculator
class DefaultSalaryCalculator extends EmployeeSalaryCalculator {
calculate(employee) {
return employee.baseSalary;
}
}
Introduce Parameter Object
Parameter Object Refactoring
Introduce Parameter Object
# Introduce Parameter Object
# Before: Method with many parameters
class UserService {
createUser(firstName, lastName, email, password, phoneNumber, address, city, state, zipCode, country, dateOfBirth, gender, isActive, role, department, managerId, startDate, salary, benefits, emergencyContactName, emergencyContactPhone, emergencyContactRelationship) {
// Validation
if (!firstName || !lastName || !email || !password) {
throw new Error('Required fields are missing');
}
if (!this.isValidEmail(email)) {
throw new Error('Invalid email format');
}
if (password.length < 8) {
throw new Error('Password must be at least 8 characters');
}
// Create user object
const user = {
firstName: firstName,
lastName: lastName,
email: email,
password: this.hashPassword(password),
phoneNumber: phoneNumber,
address: address,
city: city,
state: state,
zipCode: zipCode,
country: country,
dateOfBirth: dateOfBirth,
gender: gender,
isActive: isActive || true,
role: role || 'user',
department: department,
managerId: managerId,
startDate: startDate || new Date(),
salary: salary,
benefits: benefits,
emergencyContact: {
name: emergencyContactName,
phone: emergencyContactPhone,
relationship: emergencyContactRelationship
},
createdAt: new Date()
};
// Save to database
return this.database.save('users', user);
}
updateUser(userId, firstName, lastName, email, phoneNumber, address, city, state, zipCode, country, dateOfBirth, gender, isActive, role, department, managerId, salary, benefits, emergencyContactName, emergencyContactPhone, emergencyContactRelationship) {
const user = this.database.findById('users', userId);
if (!user) {
throw new Error('User not found');
}
// Update user object
user.firstName = firstName || user.firstName;
user.lastName = lastName || user.lastName;
user.email = email || user.email;
user.phoneNumber = phoneNumber || user.phoneNumber;
user.address = address || user.address;
user.city = city || user.city;
user.state = state || user.state;
user.zipCode = zipCode || user.zipCode;
user.country = country || user.country;
user.dateOfBirth = dateOfBirth || user.dateOfBirth;
user.gender = gender || user.gender;
user.isActive = isActive !== undefined ? isActive : user.isActive;
user.role = role || user.role;
user.department = department || user.department;
user.managerId = managerId || user.managerId;
user.salary = salary || user.salary;
user.benefits = benefits || user.benefits;
user.emergencyContact = {
name: emergencyContactName || user.emergencyContact.name,
phone: emergencyContactPhone || user.emergencyContact.phone,
relationship: emergencyContactRelationship || user.emergencyContact.relationship
};
user.updatedAt = new Date();
// Save to database
return this.database.save('users', user);
}
isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
hashPassword(password) {
// Password hashing implementation
return password; // Simplified for example
}
}
# After: Using parameter objects
class UserService {
createUser(userData) {
this.validateUserData(userData);
const user = {
...userData,
password: this.hashPassword(userData.password),
isActive: userData.isActive !== undefined ? userData.isActive : true,
role: userData.role || 'user',
startDate: userData.startDate || new Date(),
emergencyContact: userData.emergencyContact || {},
createdAt: new Date()
};
return this.database.save('users', user);
}
updateUser(userId, userData) {
const user = this.database.findById('users', userId);
if (!user) {
throw new Error('User not found');
}
const updatedUser = {
...user,
...userData,
updatedAt: new Date()
};
return this.database.save('users', updatedUser);
}
validateUserData(userData) {
if (!userData.firstName || !userData.lastName || !userData.email || !userData.password) {
throw new Error('Required fields are missing');
}
if (!this.isValidEmail(userData.email)) {
throw new Error('Invalid email format');
}
if (userData.password.length < 8) {
throw new Error('Password must be at least 8 characters');
}
}
isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
hashPassword(password) {
// Password hashing implementation
return password; // Simplified for example
}
}
# Parameter Object Classes
class UserData {
constructor(data) {
this.firstName = data.firstName;
this.lastName = data.lastName;
this.email = data.email;
this.password = data.password;
this.phoneNumber = data.phoneNumber;
this.address = data.address;
this.city = data.city;
this.state = data.state;
this.zipCode = data.zipCode;
this.country = data.country;
this.dateOfBirth = data.dateOfBirth;
this.gender = data.gender;
this.isActive = data.isActive;
this.role = data.role;
this.department = data.department;
this.managerId = data.managerId;
this.startDate = data.startDate;
this.salary = data.salary;
this.benefits = data.benefits;
this.emergencyContact = data.emergencyContact;
}
validate() {
const errors = [];
if (!this.firstName) errors.push('First name is required');
if (!this.lastName) errors.push('Last name is required');
if (!this.email) errors.push('Email is required');
if (!this.password) errors.push('Password is required');
if (this.email && !this.isValidEmail(this.email)) {
errors.push('Invalid email format');
}
if (this.password && this.password.length < 8) {
errors.push('Password must be at least 8 characters');
}
return errors;
}
isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
toObject() {
return {
firstName: this.firstName,
lastName: this.lastName,
email: this.email,
password: this.password,
phoneNumber: this.phoneNumber,
address: this.address,
city: this.city,
state: this.state,
zipCode: this.zipCode,
country: this.country,
dateOfBirth: this.dateOfBirth,
gender: this.gender,
isActive: this.isActive,
role: this.role,
department: this.department,
managerId: this.managerId,
startDate: this.startDate,
salary: this.salary,
benefits: this.benefits,
emergencyContact: this.emergencyContact
};
}
}
class EmergencyContact {
constructor(data) {
this.name = data.name;
this.phone = data.phone;
this.relationship = data.relationship;
}
validate() {
const errors = [];
if (!this.name) errors.push('Emergency contact name is required');
if (!this.phone) errors.push('Emergency contact phone is required');
if (!this.relationship) errors.push('Emergency contact relationship is required');
return errors;
}
toObject() {
return {
name: this.name,
phone: this.phone,
relationship: this.relationship
};
}
}
# Usage with parameter objects
class UserService {
createUser(userData) {
const userDataObj = new UserData(userData);
const errors = userDataObj.validate();
if (errors.length > 0) {
throw new Error('Validation failed: ' + errors.join(', '));
}
const user = {
...userDataObj.toObject(),
password: this.hashPassword(userDataObj.password),
isActive: userDataObj.isActive !== undefined ? userDataObj.isActive : true,
role: userDataObj.role || 'user',
startDate: userDataObj.startDate || new Date(),
createdAt: new Date()
};
return this.database.save('users', user);
}
updateUser(userId, userData) {
const user = this.database.findById('users', userId);
if (!user) {
throw new Error('User not found');
}
const userDataObj = new UserData(userData);
const updatedUser = {
...user,
...userDataObj.toObject(),
updatedAt: new Date()
};
return this.database.save('users', updatedUser);
}
hashPassword(password) {
// Password hashing implementation
return password; // Simplified for example
}
}
Refactoring Best Practices
Refactoring Guidelines
Refactoring Principles
- Make small, incremental changes
- Run tests after each change
- Refactor in small steps
- Keep functionality unchanged
- Use version control
- Document complex refactoring
- Review changes with team
Common Refactoring Patterns
- Extract Method
- Extract Class
- Replace Conditional with Polymorphism
- Introduce Parameter Object
- Move Method
- Rename Variable
- Remove Dead Code
Summary
Refactoring techniques implementation involves several key areas:
- Extract Method: Breaking down large methods into smaller, focused functions
- Extract Class: Separating responsibilities into dedicated classes
- Polymorphism: Replacing conditionals with object-oriented design
- Parameter Objects: Grouping related parameters into structured objects
Need More Help?
Struggling with refactoring implementation or need help improving code quality? Our refactoring experts can help you implement effective code improvement strategies.
Get Refactoring Help