Каким должен быть хорошо написанный код?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Характеристики хорошо написанного кода
Хорошо написанный код — это не просто код, который работает. Это код, который легко понять, поддерживать, расширять и тестировать. За 10+ лет я сформулировал ясные критерии качества.
1. Читаемость (Readability)
Читаемость — это самое важное. Код читается в 10 раз чаще, чем пишется.
// Плохо - трудно понять
public int c(int[] a) {
int r = 0;
for (int x : a) r += x;
return r;
}
// Хорошо - ясная цель
public int sumArray(int[] numbers) {
int sum = 0;
for (int number : numbers) {
sum += number;
}
return sum;
}
// Или ещё лучше
public int sumArray(int[] numbers) {
return Arrays.stream(numbers).sum();
}
Правила читаемости:
- Осмысленные имена переменных, методов, классов
- Коротко функции (максимум 50 строк)
- Понятная логика без nested conditions
- Комментарии только для ПОЧЕМУ, не для ЧТО
2. Простота (Simplicity)
KISS (Keep It Simple, Stupid) — не усложняй без необходимости.
// Переусложнено
public Optional<User> findUser(String email) {
try {
return Optional.ofNullable(
userRepository.findByEmail(email)
.orElseThrow(NoSuchElementException::new)
);
} catch (Exception e) {
logger.error("Error finding user", e);
return Optional.empty();
}
}
// Просто и ясно
public Optional<User> findUser(String email) {
return userRepository.findByEmail(email);
}
Всегда выбирай простое решение. Сложность часто добавляется, но редко удаляется.
3. Тестируемость (Testability)
Хорошо написанный код легко тестировать.
// Сложно тестировать
public class OrderService {
public void processOrder(Order order) {
Database.save(order);
EmailClient.send("Order confirmed");
PaymentGateway.charge(order.getTotal());
}
}
// Легко тестировать
public class OrderService {
private final OrderRepository orderRepository;
private final EmailService emailService;
private final PaymentService paymentService;
public void processOrder(Order order) {
orderRepository.save(order);
emailService.sendConfirmation(order);
paymentService.charge(order);
}
}
4. SOLID принципы
S - Single Responsibility — одна ответственность:
// Плохо
public class User {
public void save() { /* DB */ }
public void sendEmail() { /* Email */ }
public void generateReport() { /* Report */ }
}
// Правильно
public class User { /* только данные */ }
public class UserRepository { /* сохранение */ }
public class EmailService { /* email */ }
public class UserReportGenerator { /* отчёты */ }
O - Open/Closed — открыт для расширения, закрыт для модификации:
// Плохо - много условий
public class PaymentProcessor {
public void process(String type, Payment payment) {
if ("credit_card".equals(type)) {
// логика Visa
} else if ("paypal".equals(type)) {
// логика PayPal
}
}
}
// Правильно
public interface PaymentGateway {
void process(Payment payment);
}
public class CreditCardGateway implements PaymentGateway {
public void process(Payment payment) { /* Visa */ }
}
L - Liskov Substitution — полиморфизм работает правильно
I - Interface Segregation — узкие интерфейсы:
// Плохо - большой интерфейс
public interface UserService {
void createUser(User user);
void updateUser(User user);
void deleteUser(String id);
void sendEmail(String email);
void generateReport();
}
// Правильно - узкие интерфейсы
public interface UserRepository {
void save(User user);
void update(User user);
void delete(String id);
}
D - Dependency Inversion — зависит от абстракций:
// Плохо
public class OrderService {
private PostgresDatabase db = new PostgresDatabase();
}
// Правильно
public class OrderService {
private final Database db;
public OrderService(Database db) {
this.db = db;
}
}
5. Отсутствие дублирования (DRY)
Don't Repeat Yourself — не копируй код.
// Дублирование
public void processOrder() {
if (order == null) throw new IllegalArgumentException();
if (order.getTotal() <= 0) throw new IllegalArgumentException();
}
public void processPayment() {
if (payment == null) throw new IllegalArgumentException();
if (payment.getAmount() <= 0) throw new IllegalArgumentException();
}
// Правильно - общая валидация
private <T> void validateNotNull(T value) {
if (value == null) throw new IllegalArgumentException();
}
private void validatePositive(BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException();
}
}
6. Обработка ошибок
// Плохо - скрываешь ошибку
try {
database.save(user);
} catch (Exception e) {
// Молча игнорируем
}
// Правильно
try {
database.save(user);
} catch (DatabaseException e) {
logger.error("Failed to save user: {}", user.getId(), e);
throw new ApplicationException("Could not save user", e);
}
7. Производительность
Хороший код должен быть эффективным:
// Неэффективно - N+1 query problem
for (Order order : orders) {
customer = database.findCustomer(order.getCustomerId());
process(order, customer);
}
// Правильно
Map<String, Customer> customers = database.findAllCustomers();
for (Order order : orders) {
Customer customer = customers.get(order.getCustomerId());
process(order, customer);
}
8. Документация
/**
* Обрабатывает заказ и отправляет подтверждение.
* Если платёж не прошёл, выбрасывает PaymentException.
*
* @param order заказ для обработки
* @throws PaymentException если платёж не удался
* @throws DatabaseException если ошибка БД
*/
public void processOrder(Order order) throws PaymentException, DatabaseException {
// ...
}
Чеклист хорошего кода
- Читаемо — любой разработчик поймёт
- Просто — нет ненужной сложности
- Тестируемо — есть unit тесты (80%+ coverage)
- SOLID — следует принципам
- DRY — нет дублирования
- Обработаны ошибки — нет молчаливого игнорирования
- Производительно — нет очевидных bottleneck'ов
- Документировано — сложная логика объяснена
- Поддерживаемо — можно изменять без страха
Золотое правило
Пиши код для людей, а не для компьютеров. Компьютеру всё равно, какие имена у переменных, а вот твоему коллеге (или тебе через год) будет очень благодарна.