`n

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