Clean Code на C#: как писать код, который хочется читать
Каждый разработчик .NET сталкивался с ситуацией, когда через месяц или два собственный код становится нечитаемым. Чтобы этого избежать, существует философия Clean Code (Чистый код), популяризированная Робертом Мартином. В этой статье мы разберём основные принципы чистого кода на C# с конкретными примерами, которые помогут вам писать поддерживаемые и масштабируемые приложения.
Почему Clean Code важен для C# разработчика?
Чистый код — это не просто красивое форматирование. Это набор практик, которые делают код:
- Читаемым — любой разработчик быстро поймёт логику;
- Поддерживаемым — легко вносить изменения без риска сломать систему;
- Тестируемым — код легко покрывается юнит-тестами;
- Эффективным — отсутствие лишних абстракций и дублирования.
В C# с его строгой типизацией и мощными возможностями .NET (LINQ, async/await, Records) следование принципам Clean Code особенно важно. Плохой код на C# может привести к неочевидным багам и проблемам с производительностью.
Принцип 1: Именование переменных и методов
Имя переменной или метода должно отвечать на вопрос "что это?" или "что делает?". Избегайте однобуквенных сокращений (кроме индексов циклов).
Плохой пример
public class Data
{
public int a; // что это?
public string b; // непонятно
public void Do(int x, int y) // что делает метод?
{
var r = x + y;
Console.WriteLine(r);
}
}
Хороший пример (Clean Code)
public class Customer
{
public int Id { get; set; }
public string FullName { get; set; }
public void PrintCustomerSummary(int customerId, int orderCount)
{
var totalOrders = customerId + orderCount;
Console.WriteLine($"Customer {customerId} has {totalOrders} orders");
}
}
Правило: Имена должны быть выразительными. Если переменная называется data, это почти всегда плохо. Используйте customerData, orderList, invoiceTotal.
Принцип 2: Маленькие методы с одной ответственностью
Каждый метод должен делать ровно одну вещь. Если метод содержит более 10-15 строк, скорее всего, его нужно разбить. Это основа принципа Single Responsibility (из SOLID).
Плохой пример (метод делает всё)
public void ProcessOrder(int orderId)
{
// 1. Валидация
if (orderId <= 0) throw new ArgumentException("Invalid ID");
// 2. Получение данных из БД
var order = _db.Orders.Find(orderId);
// 3. Рассчёт скидки
decimal discount = 0;
if (order.Total > 1000) discount = order.Total * 0.1m;
// 4. Отправка email
var email = new MailMessage();
email.To.Add(order.CustomerEmail);
email.Subject = "Order processed";
// 5. Логирование
_logger.LogInformation("Order {Id} processed", orderId);
}
Хороший пример (рефакторинг)
public void ProcessOrder(int orderId)
{
ValidateOrderId(orderId);
var order = GetOrderFromDatabase(orderId);
var discount = CalculateDiscount(order);
SendConfirmationEmail(order, discount);
LogOrderProcessing(orderId);
}
private void ValidateOrderId(int orderId)
{
if (orderId <= 0)
throw new ArgumentException("Order ID must be positive", nameof(orderId));
}
private Order GetOrderFromDatabase(int orderId)
{
return _db.Orders.Find(orderId)
?? throw new InvalidOperationException("Order not found");
}
private decimal CalculateDiscount(Order order)
{
return order.Total > 1000 ? order.Total * 0.1m : 0;
}
private void SendConfirmationEmail(Order order, decimal discount)
{
var email = new MailMessage
{
To = { order.CustomerEmail },
Subject = "Order processed",
Body = $"Your total with discount: {order.Total - discount:C}"
};
_emailService.Send(email);
}
private void LogOrderProcessing(int orderId)
{
_logger.LogInformation("Order {Id} processed successfully", orderId);
}
Теперь каждый метод легко читается, тестируется и может быть изменён независимо.
Принцип 3: Избегайте дублирования (DRY — Don't Repeat Yourself)
Дублирование кода — главный враг поддерживаемости. Если вы копируете блок кода более одного раза, выносите его в метод или класс.
Плохой пример
// В методе A
var totalA = itemsA.Sum(i => i.Price);
var taxA = totalA * 0.2m;
var finalA = totalA + taxA;
// В методе B (дублирование)
var totalB = itemsB.Sum(i => i.Price);
var taxB = totalB * 0.2m;
var finalB = totalB + taxB;
Хороший пример
public decimal CalculateFinalTotal(IEnumerable<Item> items, decimal taxRate = 0.2m)
{
var subtotal = items.Sum(i => i.Price);
var tax = subtotal * taxRate;
return subtotal + tax;
}
// Использование
var finalA = CalculateFinalTotal(itemsA);
var finalB = CalculateFinalTotal(itemsB);
Совет: В C# активно используйте LINQ, extension methods и generic-методы для устранения дублирования.
Принцип 4: Управление сложностью и форматирование
Чистый код подразумевает не только логику, но и визуальную структуру. Правильное форматирование и использование современных возможностей C# делают код понятнее.
Используйте выражения switch (Pattern Matching)
// Вместо длинных if-else
public string GetDiscountCategory(decimal total) => total switch
{
< 100 => "No discount",
< 500 => "Silver",
< 1000 => "Gold",
_ => "Platinum"
};
Используйте Records для DTO
// Вместо классов с кучей кода
public record CustomerDto(int Id, string Name, string Email);
// Использование
var customer = new CustomerDto(1, "John Doe", "john@example.com");
Правильное форматирование
// Плохо: всё в одну строку
if (user.IsActive) {