Clean Code на C#: принципы и примеры на практике
Чистый код (Clean Code) — это не просто модный тренд в разработке, а фундаментальный подход, который определяет успех любого программного проекта на C#. Писать код, который легко читать, поддерживать и расширять, — навык, отличающий профессионала от новичка. В этой статье мы разберем ключевые принципы Clean Code на практических примерах для .NET-разработчиков.
Почему Clean Code важен для C# проектов
В экосистеме .NET, где проекты часто живут годами и переходят от одной команды к другой, читаемость кода становится критическим фактором. Чистый код позволяет:
- Сократить время на отладку и рефакторинг
- Уменьшить количество багов за счет ясной логики
- Улучшить командную работу и код-ревью
- Повысить производительность разработки в долгосрочной перспективе
Принцип 1: Говорящие имена и единый стиль
Первый шаг к чистому коду — правильное именование. Имена переменных, методов и классов должны отражать их назначение, а не способ реализации.
Плохой пример
public class Data
{
public int a { get; set; }
public string b { get; set; }
public void Do()
{
// какая-то логика
}
}
Хороший пример
public class Customer
{
public int Id { get; set; }
public string FullName { get; set; }
public void SendWelcomeEmail()
{
// логика отправки приветственного письма
}
}
Используйте camelCase для локальных переменных, PascalCase для классов и методов. Избегайте сокращений, если они не общеприняты (например, Id вместо Identifier).
Принцип 2: Маленькие методы с одной ответственностью
Каждый метод должен выполнять ровно одну задачу. Это делает код тестируемым и переиспользуемым. Если метод занимает больше 15-20 строк — пора его разбивать.
Плохой пример
public void ProcessOrder(Order order)
{
// Валидация
if (order.Total <= 0) throw new Exception("Invalid order");
// Сохранение в БД
_context.Orders.Add(order);
_context.SaveChanges();
// Отправка email
var emailService = new EmailService();
emailService.Send(order.CustomerEmail, "Order confirmed");
}
Хороший пример
public void ProcessOrder(Order order)
{
ValidateOrder(order);
SaveOrder(order);
SendConfirmationEmail(order);
}
private void ValidateOrder(Order order)
{
if (order.Total <= 0)
throw new ArgumentException("Order total must be positive");
}
private void SaveOrder(Order order)
{
_context.Orders.Add(order);
_context.SaveChanges();
}
private void SendConfirmationEmail(Order order)
{
var emailService = new EmailService();
emailService.Send(order.CustomerEmail, "Order confirmed");
}
Такой подход позволяет легко переопределять логику, писать юнит-тесты для каждого шага и быстро находить ошибки.
Принцип 3: Избегайте магических чисел и строк
Магические числа и строки — одна из главных причин трудночитаемого кода. Выносите их в константы или конфигурационные файлы.
Плохой пример
if (user.Role == 3)
{
// логика для администратора
}
if (DateTime.Now.Hour > 18)
{
// вечерняя скидка
}
Хороший пример
private const int AdminRoleId = 3;
private const int EveningDiscountStartHour = 18;
if (user.RoleId == AdminRoleId)
{
// логика для администратора
}
if (DateTime.Now.Hour > EveningDiscountStartHour)
{
// вечерняя скидка
}
Для более сложных сценариев используйте перечисления (enum) и конфигурационные классы.
Принцип 4: Принцип DRY (Don't Repeat Yourself)
Дублирование кода — злейший враг чистого кода. Если вы копируете один и тот же блок кода в нескольких местах, пора выносить его в отдельный метод или класс.
Плохой пример
public decimal CalculateDiscount(Order order)
{
if (order.Total > 1000) return order.Total * 0.1m;
if (order.Total > 500) return order.Total * 0.05m;
return 0;
}
public decimal CalculateBonus(Order order)
{
if (order.Total > 1000) return order.Total * 0.15m;
if (order.Total > 500) return order.Total * 0.07m;
return 0;
}
Хороший пример
private const decimal HighThreshold = 1000;
private const decimal MediumThreshold = 500;
public decimal CalculateDiscount(Order order) =>
CalculatePercentage(order.Total, 0.1m, 0.05m, 0);
public decimal CalculateBonus(Order order) =>
CalculatePercentage(order.Total, 0.15m, 0.07m, 0);
private decimal CalculatePercentage(decimal amount, decimal highRate,
decimal mediumRate, decimal lowRate)
{
if (amount > HighThreshold) return amount * highRate;
if (amount > MediumThreshold) return amount * mediumRate;
return amount * lowRate;
}
Обратите внимание: мы не только убрали дублирование, но и сделали логику гибкой для изменений.
Принцип 5: Работа с исключениями
Обработка ошибок должна быть предсказуемой и понятной. Избегайте голого catch (Exception ex) — обрабатывайте только те исключения, которые можете обработать.
Плохой пример
try
{
var data = _repository.GetData();
ProcessData(data);
}
catch (Exception ex)
{
// Пустой блок — ошибка проглатывается
}
Хороший пример
try
{
var data = _repository.GetData();
ProcessData(data);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("not found"))
{
_logger.LogWarning("Data not found, returning default");
return GetDefaultData();
}
catch (SqlException ex)
{
_logger.LogError(ex, "Database error occurred");
throw; // Пробрасываем дальше
}
Используйте when для фильтрации исключений и всегда логируйте ошибки на соответствующем уровне.
Заключение
Clean Code в C# — это не