← Назад к вопросам
Приведи пример S из SOLID
1.8 Middle🔥 171 комментариев
#SOLID и паттерны проектирования#ООП
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Приведи пример S из SOLID
S — Single Responsibility Principle (Принцип единственной ответственности)
Класс должен иметь только одну причину для изменения. Он должен отвечать за одно и только одно.
Плохой пример: нарушение SRP
public class User {
private String name;
private String email;
private String password;
// Ответственность 1: управление данными пользователя
public void setPassword(String password) {
this.password = password;
}
// Ответственность 2: валидация
public boolean isValidEmail() {
return email.contains("@");
}
// Ответственность 3: сохранение в БД
public void saveToDatabase() {
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/users");
PreparedStatement ps = conn.prepareStatement("INSERT INTO users VALUES (?, ?, ?)");
ps.setString(1, name);
ps.setString(2, email);
ps.setString(3, password);
ps.executeUpdate();
}
// Ответственность 4: отправка письма
public void sendWelcomeEmail() {
String emailBody = "Welcome, " + name + "!";
// SMTP отправка...
}
// Ответственность 5: логирование
public void log(String message) {
System.out.println("[" + System.currentTimeMillis() + "] " + message);
}
}
Проблемы:
- User класс отвечает за 5 разных вещей
- Если изменится схема БД → изменяем User
- Если изменится email сервис → изменяем User
- Если изменится формат логирования → изменяем User
- Класс сложный, трудно тестировать, трудно переиспользовать
Хороший пример: соблюдение SRP
Разделяем ответственность на разные классы:
1. Класс для данных пользователя:
public class User {
private String name;
private String email;
private String password;
public User(String name, String email, String password) {
this.name = name;
this.email = email;
this.password = password;
}
public String getName() { return name; }
public String getEmail() { return email; }
public String getPassword() { return password; }
}
2. Класс для валидации:
public class UserValidator {
public boolean isValidEmail(String email) {
return email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
}
public boolean isValidPassword(String password) {
return password.length() >= 8;
}
}
3. Класс для работы с БД:
public class UserRepository {
private DataSource dataSource;
public UserRepository(DataSource dataSource) {
this.dataSource = dataSource;
}
public void save(User user) {
String sql = "INSERT INTO users (name, email, password) VALUES (?, ?, ?)";
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, user.getName());
ps.setString(2, user.getEmail());
ps.setString(3, user.getPassword());
ps.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException("Failed to save user", e);
}
}
public User findByEmail(String email) {
// SELECT * FROM users WHERE email = ?
return new User(...);
}
}
4. Класс для отправки писем:
public class EmailService {
private JavaMailSender mailSender;
public EmailService(JavaMailSender mailSender) {
this.mailSender = mailSender;
}
public void sendWelcomeEmail(User user) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(user.getEmail());
message.setSubject("Welcome!");
message.setText("Welcome, " + user.getName() + "!");
mailSender.send(message);
}
}
5. Класс для логирования:
public class Logger {
public void log(String level, String message) {
System.out.println("[" + level + "] " + message);
}
}
6. Класс для бизнес-логики (use case):
public class CreateUserUseCase {
private final UserValidator validator;
private final UserRepository repository;
private final EmailService emailService;
private final Logger logger;
public CreateUserUseCase(UserValidator validator,
UserRepository repository,
EmailService emailService,
Logger logger) {
this.validator = validator;
this.repository = repository;
this.emailService = emailService;
this.logger = logger;
}
public void createUser(String name, String email, String password) {
logger.log("INFO", "Creating user: " + email);
// Валидация
if (!validator.isValidEmail(email)) {
throw new IllegalArgumentException("Invalid email");
}
if (!validator.isValidPassword(password)) {
throw new IllegalArgumentException("Password too short");
}
// Создание пользователя
User user = new User(name, email, password);
// Сохранение в БД
repository.save(user);
// Отправка письма
emailService.sendWelcomeEmail(user);
logger.log("INFO", "User created successfully: " + email);
}
}
Преимущества SRP
- Простота в тестировании:
@Test
public void testUserValidator() {
UserValidator validator = new UserValidator();
assertTrue(validator.isValidEmail("test@example.com"));
assertFalse(validator.isValidEmail("invalid"));
}
@Test
public void testEmailService() {
EmailService service = new EmailService(mockMailSender);
User user = new User("John", "john@example.com", "password123");
service.sendWelcomeEmail(user);
verify(mockMailSender).send(any());
}
- Легко переиспользовать:
// Можем использовать EmailService в других местах
public class PasswordResetService {
private EmailService emailService;
public void resetPassword(User user) {
// ...
emailService.sendPasswordResetEmail(user);
}
}
- Легко менять реализацию:
// Хотим менять EmailService с SMTP на SMS?
public interface NotificationService {
void notify(User user, String message);
}
public class SMTPEmailService implements NotificationService {
// ...
}
public class SMSService implements NotificationService {
// ...
}
public class CreateUserUseCase {
private NotificationService notificationService; // Не зависит от реализации!
// ...
}
- Класс легче понять:
- User знает только о пользователе
- UserRepository знает только о БД
- EmailService знает только об отправке писем
Правило большого пальца
Если вы не можете описать класс одним предложением БЕЗ слов "и", "также", "плюс" — класс имеет больше одной ответственности:
- ❌ "User класс управляет пользователем И сохраняет его в БД И отправляет письма"
- ✓ "User класс хранит данные пользователя"
- ✓ "UserRepository класс сохраняет и читает пользователей из БД"
- ✓ "EmailService класс отправляет письма пользователям"
Итоги
Single Responsibility Principle:
- Один класс = одна ответственность
- Одна причина для изменения
- Улучшает тестируемость, переиспользуемость и поддерживаемость
- Делает код более гибким и расширяемым