← Назад к вопросам

Использовалось ли наследование в предыдущем проекте

1.0 Junior🔥 131 комментариев
#Soft Skills и карьера#ООП

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Использовалось ли наследование в предыдущем проекте

Да, наследование использовалось активно и в различных контекстах. Позвольте рассказать о конкретных примерах из моего опыта.

1. Наследование в Entity классах

В проектах с использованием JPA/Hibernate часто применяется наследование для моделирования иерархий сущностей:

// Базовый класс для всех сущностей
@MappedSuperclass
public abstract class BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;
    
    @CreationTimestamp
    protected LocalDateTime createdAt;
    
    @UpdateTimestamp
    protected LocalDateTime updatedAt;
    
    public Long getId() { return id; }
}

// Конкретные entity классы
@Entity
@Table(name = "users")
public class User extends BaseEntity {
    private String email;
    private String passwordHash;
    
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Order> orders;
}

@Entity
@Table(name = "products")
public class Product extends BaseEntity {
    private String name;
    private BigDecimal price;
    private String description;
}

2. Наследование для различных типов платежей

// Базовый класс для всех платежей
public abstract class Payment {
    protected String transactionId;
    protected BigDecimal amount;
    protected LocalDateTime timestamp;
    
    public abstract void process() throws PaymentException;
    public abstract void refund() throws PaymentException;
    public abstract String getStatus();
}

// Конкретные реализации
public class CreditCardPayment extends Payment {
    private String cardNumber;
    private String cardHolder;
    
    @Override
    public void process() throws PaymentException {
        validateCardData();
        connectToPaymentGateway();
        chargeCard();
    }
    
    @Override
    public void refund() throws PaymentException {
        refundViaGateway();
    }
    
    @Override
    public String getStatus() {
        return "Credit Card - " + cardNumber.substring(cardNumber.length() - 4);
    }
}

public class PayPalPayment extends Payment {
    private String paypalEmail;
    
    @Override
    public void process() throws PaymentException {
        validatePayPalAccount();
        initiatePayPalTransaction();
    }
    
    @Override
    public void refund() throws PaymentException {
        refundViaPayPal();
    }
    
    @Override
    public String getStatus() {
        return "PayPal - " + paypalEmail;
    }
}

public class BankTransferPayment extends Payment {
    private String bankAccount;
    private String bankCode;
    
    @Override
    public void process() throws PaymentException {
        validateBankData();
        initiateBankTransfer();
    }
    
    @Override
    public void refund() throws PaymentException {
        refundToBank();
    }
    
    @Override
    public String getStatus() {
        return "Bank Transfer - " + bankAccount;
    }
}

// Использование полиморфизма
public class PaymentProcessor {
    public void processPayment(Payment payment) throws PaymentException {
        payment.process();
        logTransaction(payment.getStatus());
    }
}

3. Наследование в иерархии исключений

// Базовый класс для доменных исключений
public abstract class DomainException extends RuntimeException {
    private final String errorCode;
    
    public DomainException(String message, String errorCode) {
        super(message);
        this.errorCode = errorCode;
    }
    
    public String getErrorCode() {
        return errorCode;
    }
}

// Конкретные исключения
public class UserNotFoundException extends DomainException {
    public UserNotFoundException(Long userId) {
        super("User not found: " + userId, "USER_NOT_FOUND");
    }
}

public class InsufficientFundsException extends DomainException {
    private BigDecimal required;
    private BigDecimal available;
    
    public InsufficientFundsException(BigDecimal required, BigDecimal available) {
        super("Insufficient funds", "INSUFFICIENT_FUNDS");
        this.required = required;
        this.available = available;
    }
}

public class InvalidOrderStateException extends DomainException {
    public InvalidOrderStateException(String currentState, String attemptedAction) {
        super("Cannot " + attemptedAction + " in state " + currentState, 
              "INVALID_ORDER_STATE");
    }
}

4. Наследование в Service слое

// Базовый сервис с общей функциональностью
public abstract class BaseService<T, ID> {
    protected final JpaRepository<T, ID> repository;
    protected final Logger logger;
    
    public BaseService(JpaRepository<T, ID> repository) {
        this.repository = repository;
        this.logger = LoggerFactory.getLogger(getClass());
    }
    
    public T getById(ID id) {
        return repository.findById(id)
            .orElseThrow(() -> new NotFoundException("Not found"));
    }
    
    public List<T> getAll() {
        return repository.findAll();
    }
    
    public void delete(ID id) {
        repository.deleteById(id);
        logger.info("Deleted: " + id);
    }
    
    protected abstract void validate(T entity);
}

// Конкретный сервис
@Service
public class OrderService extends BaseService<Order, Long> {
    private final PaymentProcessor paymentProcessor;
    
    public OrderService(OrderRepository repository, 
                        PaymentProcessor paymentProcessor) {
        super(repository);
        this.paymentProcessor = paymentProcessor;
    }
    
    @Transactional
    public Order createOrder(CreateOrderRequest request) {
        Order order = new Order();
        validate(order);
        order = repository.save(order);
        logger.info("Order created: " + order.getId());
        return order;
    }
    
    @Override
    protected void validate(Order order) {
        if (order.getItems().isEmpty()) {
            throw new ValidationException("Order must have items");
        }
    }
}

5. Наследование с использованием Interface

// В современных проектах часто предпочитаю интерфейсы
public interface Repository<T, ID> {
    Optional<T> findById(ID id);
    List<T> findAll();
    T save(T entity);
    void delete(ID id);
}

public interface PaymentGateway {
    TransactionResult charge(PaymentDetails details) throws PaymentException;
    RefundResult refund(String transactionId) throws PaymentException;
}

// Конкретные реализации через implements вместо extends
public class StripeGateway implements PaymentGateway {
    @Override
    public TransactionResult charge(PaymentDetails details) throws PaymentException {
        // Stripe API интеграция
    }
    
    @Override
    public RefundResult refund(String transactionId) throws PaymentException {
        // Stripe refund logic
    }
}

Когда избегал наследования

// ❌ Плохой пример - нарушение Liskov Substitution Principle
public class Square extends Rectangle {
    @Override
    public void setWidth(int width) {
        this.width = width;
        this.height = width;  // Нарушение! Square не может быть Rectangle
    }
}

// ✅ Лучше использовать композицию
public class Shape {
    public interface Renderer { void render(); }
}

public class Square implements Shape.Renderer {
    private int side;
    @Override
    public void render() { /* ... */ }
}

Выводы

Наследование — мощный инструмент, но требует осторожного применения:

  1. Используй для моделирования отношений IS-A
  2. Следи за Liskov Substitution Principle
  3. Предпочитай композицию наследованию где возможно
  4. Не более 2-3 уровней глубины иерархии
  5. Используй abstract классы для общей реализации
  6. Используй интерфейсы для определения контрактов
Использовалось ли наследование в предыдущем проекте | PrepBro