C#代码重构方法:提升代码质量的实用技巧


代码重构是软件开发过程中不可或缺的一环,它能够改善现有代码的设计而不改变其外部行为。对于C#开发者而言,掌握有效的重构方法可以显著提升代码的可读性、可维护性和可扩展性。本文将介绍一系列实用的C#代码重构方法。

1. 提取方法(Extract Method)

问题场景:当某个方法过长或包含太多责任时。

解决方案:将代码块提取为独立的方法。

// 重构前
public void ProcessOrder(Order order)
{
    // 验证订单
    if (order == null) throw new ArgumentNullException(nameof(order));
    if (order.Items.Count == 0) throw new InvalidOperationException("订单不能为空");

    // 计算总价
    decimal total = 0;
    foreach (var item in order.Items)
    {
        total += item.Price * item.Quantity;
    }

    // 应用折扣
    if (order.Customer.IsPremium)
    {
        total *= 0.9m;
    }

    // 保存订单
    order.TotalAmount = total;
    _orderRepository.Save(order);
}

// 重构后
public void ProcessOrder(Order order)
{
    ValidateOrder(order);
    decimal total = CalculateTotal(order);
    ApplyDiscount(order, ref total);
    SaveOrder(order, total);
}

private void ValidateOrder(Order order)
{
    if (order == null) throw new ArgumentNullException(nameof(order));
    if (order.Items.Count == 0) throw new InvalidOperationException("订单不能为空");
}

private decimal CalculateTotal(Order order)
{
    decimal total = 0;
    foreach (var item in order.Items)
    {
        total += item.Price * item.Quantity;
    }
    return total;
}

private void ApplyDiscount(Order order, ref decimal total)
{
    if (order.Customer.IsPremium)
    {
        total *= 0.9m;
    }
}

private void SaveOrder(Order order, decimal total)
{
    order.TotalAmount = total;
    _orderRepository.Save(order);
}

2. 引入解释性变量(Introduce Explaining Variable)

问题场景:复杂表达式难以理解时。

解决方案:将复杂表达式的结果赋给一个有意义的临时变量。

// 重构前
if (customer.Age > 18 && customer.PurchaseHistory.Count > 5 && !customer.HasUnpaidBills)
{
    // 给予优惠
}

// 重构后
bool isAdult = customer.Age > 18;
bool isFrequentBuyer = customer.PurchaseHistory.Count > 5;
bool hasGoodCredit = !customer.HasUnpaidBills;

if (isAdult && isFrequentBuyer && hasGoodCredit)
{
    // 给予优惠
}

3. 以多态替代条件语句(Replace Conditional with Polymorphism)

问题场景:复杂的switch-case或if-else语句。

解决方案:使用继承和多态来替代条件逻辑。

// 重构前
public decimal CalculateShippingCost(Order order)
{
    switch (order.ShippingMethod)
    {
        case ShippingMethod.Standard:
            return order.TotalWeight * 0.5m;
        case ShippingMethod.Express:
            return order.TotalWeight * 1.2m + 10;
        case ShippingMethod.Priority:
            return order.TotalWeight * 2m + 20;
        default:
            throw new ArgumentOutOfRangeException();
    }
}

// 重构后
public abstract class ShippingCalculator
{
    public abstract decimal CalculateCost(Order order);
}

public class StandardShippingCalculator : ShippingCalculator
{
    public override decimal CalculateCost(Order order) => order.TotalWeight * 0.5m;
}

public class ExpressShippingCalculator : ShippingCalculator
{
    public override decimal CalculateCost(Order order) => order.TotalWeight * 1.2m + 10;
}

public class PriorityShippingCalculator : ShippingCalculator
{
    public override decimal CalculateCost(Order order) => order.TotalWeight * 2m + 20;
}

// 使用工厂方法创建适当的计算器
public ShippingCalculator CreateShippingCalculator(ShippingMethod method)
{
    switch (method)
    {
        case ShippingMethod.Standard: return new StandardShippingCalculator();
        case ShippingMethod.Express: return new ExpressShippingCalculator();
        case ShippingMethod.Priority: return new PriorityShippingCalculator();
        default: throw new ArgumentOutOfRangeException();
    }
}

4. 使用LINQ替代循环

问题场景:使用传统循环处理集合时。

解决方案:使用LINQ查询使代码更简洁、更具表达力。

// 重构前
List<Customer> premiumCustomers = new List<Customer>();
foreach (var customer in customers)
{
    if (customer.IsPremium && customer.TotalPurchases > 1000)
    {
        premiumCustomers.Add(customer);
    }
}

// 重构后
var premiumCustomers = customers
    .Where(c => c.IsPremium && c.TotalPurchases > 1000)
    .ToList();

5. 使用空对象模式(Null Object Pattern)

问题场景:频繁检查null值时。

解决方案:引入表示”无”行为的特殊对象。

// 重构前
public interface ILogger
{
    void Log(string message);
}

public class FileLogger : ILogger
{
    public void Log(string message) => File.AppendAllText("log.txt", message);
}

// 使用时需要检查null
ILogger logger = GetLogger();
if (logger != null)
{
    logger.Log("操作开始");
}

// 重构后
public class NullLogger : ILogger
{
    public void Log(string message) { /* 什么都不做 */ }
}

// 使用时不需要检查null
ILogger logger = GetLogger() ?? new NullLogger();
logger.Log("操作开始");

6. 使用C#新特性简化代码

问题场景:冗长的代码结构。

解决方案:利用C#的新特性如模式匹配、记录类型等。

// 重构前
public class Product
{
    public int Id { get; }
    public string Name { get; }
    public decimal Price { get; }

    public Product(int id, string name, decimal price)
    {
        Id = id;
        Name = name;
        Price = price;
    }

    public override bool Equals(object obj)
    {
        return obj is Product product &&
               Id == product.Id &&
               Name == product.Name &&
               Price == product.Price;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(Id, Name, Price);
    }
}

// 重构后
public record Product(int Id, string Name, decimal Price);

7. 分解复杂条件(Decompose Conditional)

问题场景:复杂的条件判断难以理解。

解决方案:将条件表达式提取为方法或有意义的变量。

// 重构前
if ((customer.Age > 65 || customer.IsDisabled) && 
    customer.AccountBalance > 10000 && 
    !customer.HasRecentFraudActivity)
{
    // 提供特殊服务
}

// 重构后
bool isEligibleForSpecialService = IsSeniorOrDisabled(customer) && 
                                  HasSufficientBalance(customer) && 
                                  HasCleanRecord(customer);

if (isEligibleForSpecialService)
{
    // 提供特殊服务
}

private bool IsSeniorOrDisabled(Customer customer) => 
    customer.Age > 65 || customer.IsDisabled;

private bool HasSufficientBalance(Customer customer) => 
    customer.AccountBalance > 10000;

private bool HasCleanRecord(Customer customer) => 
    !customer.HasRecentFraudActivity;

8. 使用依赖注入

问题场景:紧耦合的类关系。

解决方案:通过依赖注入解耦类之间的依赖。

// 重构前
public class OrderProcessor
{
    private readonly OrderRepository _repository = new OrderRepository();
    private readonly EmailService _emailService = new EmailService();

    public void Process(Order order)
    {
        _repository.Save(order);
        _emailService.SendConfirmation(order);
    }
}

// 重构后
public class OrderProcessor
{
    private readonly IOrderRepository _repository;
    private readonly IEmailService _emailService;

    public OrderProcessor(IOrderRepository repository, IEmailService emailService)
    {
        _repository = repository;
        _emailService = emailService;
    }

    public void Process(Order order)
    {
        _repository.Save(order);
        _emailService.SendConfirmation(order);
    }
}

重构的最佳实践

  1. 小步前进:每次重构只做小的改动,确保每次改动后都能通过测试。
  2. 充分测试:确保有良好的测试覆盖率后再开始重构。
  3. 版本控制:频繁提交,每次提交代表一个小的、完整的变化。
  4. 代码审查:让同事审查你的重构,获得反馈。
  5. 性能考量:某些重构可能影响性能,需要权衡。

结论

C#代码重构是一项需要持续实践的技能。通过应用这些重构方法,你可以显著提升代码质量,使代码更易于理解、维护和扩展。记住,重构不是一次性活动,而是开发过程中持续进行的实践。随着C#语言的不断发展,新的特性和模式也会不断出现,持续学习和应用这些新特性将使你的代码保持现代化和高效。


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注