代码重构是软件开发过程中不可或缺的一环,它能够改善现有代码的设计而不改变其外部行为。对于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);
}
}
重构的最佳实践
- 小步前进:每次重构只做小的改动,确保每次改动后都能通过测试。
- 充分测试:确保有良好的测试覆盖率后再开始重构。
- 版本控制:频繁提交,每次提交代表一个小的、完整的变化。
- 代码审查:让同事审查你的重构,获得反馈。
- 性能考量:某些重构可能影响性能,需要权衡。
结论
C#代码重构是一项需要持续实践的技能。通过应用这些重构方法,你可以显著提升代码质量,使代码更易于理解、维护和扩展。记住,重构不是一次性活动,而是开发过程中持续进行的实践。随着C#语言的不断发展,新的特性和模式也会不断出现,持续学习和应用这些新特性将使你的代码保持现代化和高效。