← Назад к вопросам
Использовалось ли наследование в предыдущем проекте
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() { /* ... */ }
}
Выводы
Наследование — мощный инструмент, но требует осторожного применения:
- Используй для моделирования отношений IS-A
- Следи за Liskov Substitution Principle
- Предпочитай композицию наследованию где возможно
- Не более 2-3 уровней глубины иерархии
- Используй abstract классы для общей реализации
- Используй интерфейсы для определения контрактов