- Added comprehensive course structure with 11 modules - Included theoretical content, practical examples, and tasks - Multi-language code examples (Java, Rust, Node.js, Python) - Detailed analysis of architectural approaches and trade-offs - Complete caching strategies documentation - Architecture examples from real-world systems (Twitter, Netflix, Uber) - Structured navigation and learning path - Ready for educational use
1041 lines
31 KiB
Markdown
1041 lines
31 KiB
Markdown
# Принципы проектирования
|
|
|
|
## Навигация
|
|
- [🏠 Главная страница курса](../../../README.md)
|
|
- [📚 Модуль 1](../README.md)
|
|
|
|
---
|
|
|
|
## SOLID принципы
|
|
|
|
### 1. Single Responsibility Principle (SRP)
|
|
**Принцип единственной ответственности**
|
|
|
|
> Класс должен иметь только одну причину для изменения
|
|
|
|
**Пример нарушения:**
|
|
```java
|
|
// Нарушение SRP - класс делает слишком много
|
|
class UserManager {
|
|
private UserRepository userRepository;
|
|
private EmailService emailService;
|
|
private PasswordEncoder passwordEncoder;
|
|
private AuditService auditService;
|
|
|
|
public User createUser(CreateUserRequest request) {
|
|
// 1. Валидация данных
|
|
if (request.getEmail() == null || !isValidEmail(request.getEmail())) {
|
|
throw new ValidationException("Invalid email");
|
|
}
|
|
|
|
// 2. Хеширование пароля
|
|
String hashedPassword = passwordEncoder.encode(request.getPassword());
|
|
|
|
// 3. Создание пользователя
|
|
User user = new User(request.getName(), request.getEmail(), hashedPassword);
|
|
user = userRepository.save(user);
|
|
|
|
// 4. Отправка приветственного email
|
|
emailService.sendWelcomeEmail(user.getEmail());
|
|
|
|
// 5. Аудит
|
|
auditService.logUserCreation(user.getId());
|
|
|
|
return user;
|
|
}
|
|
|
|
public void updateUser(Long userId, UpdateUserRequest request) {
|
|
// Аналогично много ответственностей...
|
|
}
|
|
|
|
public void deleteUser(Long userId) {
|
|
// Аналогично много ответственностей...
|
|
}
|
|
}
|
|
```
|
|
|
|
**Правильная реализация:**
|
|
```java
|
|
// Разделение ответственностей
|
|
class User {
|
|
private Long id;
|
|
private String name;
|
|
private String email;
|
|
private String passwordHash;
|
|
|
|
// Только данные пользователя и бизнес-логика
|
|
public void changePassword(String newPassword, PasswordEncoder encoder) {
|
|
this.passwordHash = encoder.encode(newPassword);
|
|
}
|
|
|
|
public boolean isEmailValid() {
|
|
return email != null && email.contains("@");
|
|
}
|
|
}
|
|
|
|
class UserValidator {
|
|
public void validateCreateRequest(CreateUserRequest request) {
|
|
if (request.getName() == null || request.getName().trim().isEmpty()) {
|
|
throw new ValidationException("Name is required");
|
|
}
|
|
if (request.getEmail() == null || !isValidEmail(request.getEmail())) {
|
|
throw new ValidationException("Invalid email");
|
|
}
|
|
if (request.getPassword() == null || request.getPassword().length() < 8) {
|
|
throw new ValidationException("Password must be at least 8 characters");
|
|
}
|
|
}
|
|
|
|
private boolean isValidEmail(String email) {
|
|
return email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
|
|
}
|
|
}
|
|
|
|
class UserRepository {
|
|
public User save(User user) {
|
|
// Только операции с БД
|
|
return userRepository.save(user);
|
|
}
|
|
|
|
public Optional<User> findById(Long id) {
|
|
return userRepository.findById(id);
|
|
}
|
|
}
|
|
|
|
class UserNotificationService {
|
|
public void sendWelcomeEmail(String email) {
|
|
// Только отправка уведомлений
|
|
emailService.sendWelcomeEmail(email);
|
|
}
|
|
}
|
|
|
|
class UserAuditService {
|
|
public void logUserCreation(Long userId) {
|
|
// Только аудит
|
|
auditService.logUserCreation(userId);
|
|
}
|
|
}
|
|
|
|
// Координатор, который использует все сервисы
|
|
class UserService {
|
|
private UserValidator validator;
|
|
private UserRepository repository;
|
|
private UserNotificationService notificationService;
|
|
private UserAuditService auditService;
|
|
private PasswordEncoder passwordEncoder;
|
|
|
|
public User createUser(CreateUserRequest request) {
|
|
// 1. Валидация
|
|
validator.validateCreateRequest(request);
|
|
|
|
// 2. Создание пользователя
|
|
String hashedPassword = passwordEncoder.encode(request.getPassword());
|
|
User user = new User(request.getName(), request.getEmail(), hashedPassword);
|
|
|
|
// 3. Сохранение
|
|
user = repository.save(user);
|
|
|
|
// 4. Уведомления
|
|
notificationService.sendWelcomeEmail(user.getEmail());
|
|
|
|
// 5. Аудит
|
|
auditService.logUserCreation(user.getId());
|
|
|
|
return user;
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. Open/Closed Principle (OCP)
|
|
**Принцип открытости/закрытости**
|
|
|
|
> Программные сущности должны быть открыты для расширения, но закрыты для модификации
|
|
|
|
**Пример нарушения:**
|
|
```java
|
|
// Нарушение OCP - нужно модифицировать класс для добавления новых типов
|
|
class OrderProcessor {
|
|
public void processOrder(Order order) {
|
|
switch (order.getPaymentType()) {
|
|
case CREDIT_CARD:
|
|
processCreditCardPayment(order);
|
|
break;
|
|
case PAYPAL:
|
|
processPayPalPayment(order);
|
|
break;
|
|
case BANK_TRANSFER:
|
|
processBankTransferPayment(order);
|
|
break;
|
|
// При добавлении нового типа платежа нужно модифицировать этот класс
|
|
}
|
|
}
|
|
|
|
private void processCreditCardPayment(Order order) {
|
|
// логика обработки кредитной карты
|
|
}
|
|
|
|
private void processPayPalPayment(Order order) {
|
|
// логика обработки PayPal
|
|
}
|
|
|
|
private void processBankTransferPayment(Order order) {
|
|
// логика обработки банковского перевода
|
|
}
|
|
}
|
|
```
|
|
|
|
**Правильная реализация:**
|
|
```java
|
|
// Соблюдение OCP - открыто для расширения, закрыто для модификации
|
|
interface PaymentProcessor {
|
|
boolean canProcess(PaymentType paymentType);
|
|
PaymentResult processPayment(PaymentRequest request);
|
|
}
|
|
|
|
class CreditCardProcessor implements PaymentProcessor {
|
|
@Override
|
|
public boolean canProcess(PaymentType paymentType) {
|
|
return paymentType == PaymentType.CREDIT_CARD;
|
|
}
|
|
|
|
@Override
|
|
public PaymentResult processPayment(PaymentRequest request) {
|
|
// логика обработки кредитной карты
|
|
return new PaymentResult(true, "Payment processed via credit card");
|
|
}
|
|
}
|
|
|
|
class PayPalProcessor implements PaymentProcessor {
|
|
@Override
|
|
public boolean canProcess(PaymentType paymentType) {
|
|
return paymentType == PaymentType.PAYPAL;
|
|
}
|
|
|
|
@Override
|
|
public PaymentResult processPayment(PaymentRequest request) {
|
|
// логика обработки PayPal
|
|
return new PaymentResult(true, "Payment processed via PayPal");
|
|
}
|
|
}
|
|
|
|
class CryptocurrencyProcessor implements PaymentProcessor {
|
|
@Override
|
|
public boolean canProcess(PaymentType paymentType) {
|
|
return paymentType == PaymentType.CRYPTOCURRENCY;
|
|
}
|
|
|
|
@Override
|
|
public PaymentResult processPayment(PaymentRequest request) {
|
|
// логика обработки криптовалюты
|
|
return new PaymentResult(true, "Payment processed via cryptocurrency");
|
|
}
|
|
}
|
|
|
|
// Основной класс не нужно модифицировать для добавления новых процессоров
|
|
class OrderProcessor {
|
|
private List<PaymentProcessor> processors;
|
|
|
|
public OrderProcessor(List<PaymentProcessor> processors) {
|
|
this.processors = processors;
|
|
}
|
|
|
|
public PaymentResult processOrder(Order order) {
|
|
PaymentProcessor processor = processors.stream()
|
|
.filter(p -> p.canProcess(order.getPaymentType()))
|
|
.findFirst()
|
|
.orElseThrow(() -> new UnsupportedPaymentTypeException(
|
|
"No processor found for payment type: " + order.getPaymentType()));
|
|
|
|
PaymentRequest request = new PaymentRequest(order.getAmount(), order.getPaymentDetails());
|
|
return processor.processPayment(request);
|
|
}
|
|
}
|
|
|
|
// Конфигурация процессоров
|
|
@Configuration
|
|
public class PaymentProcessorConfig {
|
|
|
|
@Bean
|
|
public List<PaymentProcessor> paymentProcessors() {
|
|
return Arrays.asList(
|
|
new CreditCardProcessor(),
|
|
new PayPalProcessor(),
|
|
new CryptocurrencyProcessor() // Новый процессор добавляется без модификации существующего кода
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. Liskov Substitution Principle (LSP)
|
|
**Принцип подстановки Лисков**
|
|
|
|
> Объекты производного класса должны быть заменяемы объектами базового класса
|
|
|
|
**Пример нарушения:**
|
|
```java
|
|
// Нарушение LSP
|
|
class Rectangle {
|
|
protected int width;
|
|
protected int height;
|
|
|
|
public void setWidth(int width) {
|
|
this.width = width;
|
|
}
|
|
|
|
public void setHeight(int height) {
|
|
this.height = height;
|
|
}
|
|
|
|
public int getArea() {
|
|
return width * height;
|
|
}
|
|
}
|
|
|
|
class Square extends Rectangle {
|
|
@Override
|
|
public void setWidth(int width) {
|
|
this.width = width;
|
|
this.height = width; // Нарушение LSP - изменяет поведение
|
|
}
|
|
|
|
@Override
|
|
public void setHeight(int height) {
|
|
this.height = height;
|
|
this.width = height; // Нарушение LSP - изменяет поведение
|
|
}
|
|
}
|
|
|
|
// Проблема: код, работающий с Rectangle, может сломаться с Square
|
|
public void testRectangle(Rectangle rectangle) {
|
|
rectangle.setWidth(5);
|
|
rectangle.setHeight(4);
|
|
assert rectangle.getArea() == 20; // Для Square это будет 16!
|
|
}
|
|
```
|
|
|
|
**Правильная реализация:**
|
|
```java
|
|
// Соблюдение LSP
|
|
abstract class Shape {
|
|
public abstract int getArea();
|
|
}
|
|
|
|
class Rectangle extends Shape {
|
|
private int width;
|
|
private int height;
|
|
|
|
public Rectangle(int width, int height) {
|
|
this.width = width;
|
|
this.height = height;
|
|
}
|
|
|
|
public void setWidth(int width) {
|
|
this.width = width;
|
|
}
|
|
|
|
public void setHeight(int height) {
|
|
this.height = height;
|
|
}
|
|
|
|
@Override
|
|
public int getArea() {
|
|
return width * height;
|
|
}
|
|
}
|
|
|
|
class Square extends Shape {
|
|
private int side;
|
|
|
|
public Square(int side) {
|
|
this.side = side;
|
|
}
|
|
|
|
public void setSide(int side) {
|
|
this.side = side;
|
|
}
|
|
|
|
@Override
|
|
public int getArea() {
|
|
return side * side;
|
|
}
|
|
}
|
|
|
|
// Теперь любой Shape можно заменить другим Shape
|
|
public void testShape(Shape shape) {
|
|
int area = shape.getArea();
|
|
// Логика работает корректно для любого типа Shape
|
|
}
|
|
```
|
|
|
|
### 4. Interface Segregation Principle (ISP)
|
|
**Принцип разделения интерфейсов**
|
|
|
|
> Клиенты не должны зависеть от интерфейсов, которые они не используют
|
|
|
|
**Пример нарушения:**
|
|
```java
|
|
// Нарушение ISP - слишком большой интерфейс
|
|
interface Worker {
|
|
void work();
|
|
void eat();
|
|
void sleep();
|
|
void code();
|
|
void design();
|
|
void test();
|
|
void deploy();
|
|
}
|
|
|
|
class Developer implements Worker {
|
|
@Override
|
|
public void work() { /* разработка */ }
|
|
|
|
@Override
|
|
public void eat() { /* еда */ }
|
|
|
|
@Override
|
|
public void sleep() { /* сон */ }
|
|
|
|
@Override
|
|
public void code() { /* кодирование */ }
|
|
|
|
@Override
|
|
public void design() { /* не используется */ }
|
|
|
|
@Override
|
|
public void test() { /* не используется */ }
|
|
|
|
@Override
|
|
public void deploy() { /* не используется */ }
|
|
}
|
|
|
|
class Designer implements Worker {
|
|
@Override
|
|
public void work() { /* работа */ }
|
|
|
|
@Override
|
|
public void eat() { /* еда */ }
|
|
|
|
@Override
|
|
public void sleep() { /* сон */ }
|
|
|
|
@Override
|
|
public void code() { /* не используется */ }
|
|
|
|
@Override
|
|
public void design() { /* дизайн */ }
|
|
|
|
@Override
|
|
public void test() { /* не используется */ }
|
|
|
|
@Override
|
|
public void deploy() { /* не используется */ }
|
|
}
|
|
```
|
|
|
|
**Правильная реализация:**
|
|
```java
|
|
// Соблюдение ISP - разделение интерфейсов
|
|
interface Human {
|
|
void eat();
|
|
void sleep();
|
|
}
|
|
|
|
interface Worker extends Human {
|
|
void work();
|
|
}
|
|
|
|
interface Developer extends Worker {
|
|
void code();
|
|
}
|
|
|
|
interface Designer extends Worker {
|
|
void design();
|
|
}
|
|
|
|
interface Tester extends Worker {
|
|
void test();
|
|
}
|
|
|
|
interface DevOps extends Worker {
|
|
void deploy();
|
|
}
|
|
|
|
// Теперь каждый класс реализует только нужные ему интерфейсы
|
|
class JavaDeveloper implements Developer {
|
|
@Override
|
|
public void eat() { /* еда */ }
|
|
|
|
@Override
|
|
public void sleep() { /* сон */ }
|
|
|
|
@Override
|
|
public void work() { /* работа */ }
|
|
|
|
@Override
|
|
public void code() { /* Java кодирование */ }
|
|
}
|
|
|
|
class UIDesigner implements Designer {
|
|
@Override
|
|
public void eat() { /* еда */ }
|
|
|
|
@Override
|
|
public void sleep() { /* сон */ }
|
|
|
|
@Override
|
|
public void work() { /* работа */ }
|
|
|
|
@Override
|
|
public void design() { /* UI дизайн */ }
|
|
}
|
|
|
|
// Можно комбинировать интерфейсы
|
|
class FullStackDeveloper implements Developer, Designer {
|
|
@Override
|
|
public void eat() { /* еда */ }
|
|
|
|
@Override
|
|
public void sleep() { /* сон */ }
|
|
|
|
@Override
|
|
public void work() { /* работа */ }
|
|
|
|
@Override
|
|
public void code() { /* кодирование */ }
|
|
|
|
@Override
|
|
public void design() { /* дизайн */ }
|
|
}
|
|
```
|
|
|
|
### 5. Dependency Inversion Principle (DIP)
|
|
**Принцип инверсии зависимостей**
|
|
|
|
> Модули высокого уровня не должны зависеть от модулей низкого уровня
|
|
|
|
**Пример нарушения:**
|
|
```java
|
|
// Нарушение DIP - высокоуровневый модуль зависит от низкоуровневого
|
|
class EmailService {
|
|
public void sendEmail(String to, String subject, String body) {
|
|
// Прямая зависимость от конкретной реализации
|
|
SMTPClient smtpClient = new SMTPClient();
|
|
smtpClient.connect("smtp.gmail.com", 587);
|
|
smtpClient.send(to, subject, body);
|
|
smtpClient.disconnect();
|
|
}
|
|
}
|
|
|
|
class OrderService {
|
|
private EmailService emailService;
|
|
|
|
public OrderService() {
|
|
this.emailService = new EmailService(); // Жесткая зависимость
|
|
}
|
|
|
|
public void processOrder(Order order) {
|
|
// Обработка заказа
|
|
order.setStatus(OrderStatus.PROCESSED);
|
|
|
|
// Отправка уведомления
|
|
emailService.sendEmail(
|
|
order.getCustomerEmail(),
|
|
"Order Confirmation",
|
|
"Your order has been processed"
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
**Правильная реализация:**
|
|
```java
|
|
// Соблюдение DIP - зависимость от абстракций
|
|
interface NotificationService {
|
|
void sendNotification(String recipient, String subject, String message);
|
|
}
|
|
|
|
interface EmailProvider {
|
|
void sendEmail(String to, String subject, String body);
|
|
}
|
|
|
|
class SMTPEmailProvider implements EmailProvider {
|
|
@Override
|
|
public void sendEmail(String to, String subject, String body) {
|
|
SMTPClient smtpClient = new SMTPClient();
|
|
smtpClient.connect("smtp.gmail.com", 587);
|
|
smtpClient.send(to, subject, body);
|
|
smtpClient.disconnect();
|
|
}
|
|
}
|
|
|
|
class SendGridEmailProvider implements EmailProvider {
|
|
@Override
|
|
public void sendEmail(String to, String subject, String body) {
|
|
SendGridClient client = new SendGridClient();
|
|
client.send(to, subject, body);
|
|
}
|
|
}
|
|
|
|
class EmailNotificationService implements NotificationService {
|
|
private EmailProvider emailProvider;
|
|
|
|
public EmailNotificationService(EmailProvider emailProvider) {
|
|
this.emailProvider = emailProvider; // Инъекция зависимости
|
|
}
|
|
|
|
@Override
|
|
public void sendNotification(String recipient, String subject, String message) {
|
|
emailProvider.sendEmail(recipient, subject, message);
|
|
}
|
|
}
|
|
|
|
class SMSNotificationService implements NotificationService {
|
|
private SMSProvider smsProvider;
|
|
|
|
public SMSNotificationService(SMSProvider smsProvider) {
|
|
this.smsProvider = smsProvider;
|
|
}
|
|
|
|
@Override
|
|
public void sendNotification(String recipient, String subject, String message) {
|
|
smsProvider.sendSMS(recipient, message);
|
|
}
|
|
}
|
|
|
|
// Высокоуровневый модуль зависит от абстракции
|
|
class OrderService {
|
|
private NotificationService notificationService;
|
|
|
|
public OrderService(NotificationService notificationService) {
|
|
this.notificationService = notificationService; // Инъекция зависимости
|
|
}
|
|
|
|
public void processOrder(Order order) {
|
|
// Обработка заказа
|
|
order.setStatus(OrderStatus.PROCESSED);
|
|
|
|
// Отправка уведомления через абстракцию
|
|
notificationService.sendNotification(
|
|
order.getCustomerEmail(),
|
|
"Order Confirmation",
|
|
"Your order has been processed"
|
|
);
|
|
}
|
|
}
|
|
|
|
// Конфигурация зависимостей
|
|
@Configuration
|
|
public class AppConfig {
|
|
|
|
@Bean
|
|
public EmailProvider emailProvider() {
|
|
return new SMTPEmailProvider(); // Можно легко заменить на SendGridEmailProvider
|
|
}
|
|
|
|
@Bean
|
|
public NotificationService notificationService(EmailProvider emailProvider) {
|
|
return new EmailNotificationService(emailProvider);
|
|
}
|
|
|
|
@Bean
|
|
public OrderService orderService(NotificationService notificationService) {
|
|
return new OrderService(notificationService);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Дополнительные принципы
|
|
|
|
### DRY (Не повторяйся)
|
|
**Не повторяйся**
|
|
|
|
- Избегайте дублирования кода
|
|
- Выносите общую логику в отдельные функции/классы
|
|
- Используйте абстракции для переиспользования
|
|
|
|
**Пример нарушения:**
|
|
```java
|
|
// Нарушение DRY - дублирование кода
|
|
class UserService {
|
|
public void createUser(String email, String password) {
|
|
if (email == null || email.isEmpty()) {
|
|
throw new IllegalArgumentException("Email cannot be null or empty");
|
|
}
|
|
if (!email.contains("@")) {
|
|
throw new IllegalArgumentException("Invalid email format");
|
|
}
|
|
if (password == null || password.length() < 8) {
|
|
throw new IllegalArgumentException("Password must be at least 8 characters");
|
|
}
|
|
// создание пользователя
|
|
}
|
|
|
|
public void updateUser(String email, String password) {
|
|
if (email == null || email.isEmpty()) {
|
|
throw new IllegalArgumentException("Email cannot be null or empty");
|
|
}
|
|
if (!email.contains("@")) {
|
|
throw new IllegalArgumentException("Invalid email format");
|
|
}
|
|
if (password == null || password.length() < 8) {
|
|
throw new IllegalArgumentException("Password must be at least 8 characters");
|
|
}
|
|
// обновление пользователя
|
|
}
|
|
}
|
|
```
|
|
|
|
**Правильная реализация:**
|
|
```java
|
|
// Соблюдение DRY - выделение общей логики
|
|
class ValidationUtils {
|
|
public static void validateEmail(String email) {
|
|
if (email == null || email.isEmpty()) {
|
|
throw new IllegalArgumentException("Email cannot be null or empty");
|
|
}
|
|
if (!email.contains("@")) {
|
|
throw new IllegalArgumentException("Invalid email format");
|
|
}
|
|
}
|
|
|
|
public static void validatePassword(String password) {
|
|
if (password == null || password.length() < 8) {
|
|
throw new IllegalArgumentException("Password must be at least 8 characters");
|
|
}
|
|
}
|
|
}
|
|
|
|
class UserService {
|
|
public void createUser(String email, String password) {
|
|
ValidationUtils.validateEmail(email);
|
|
ValidationUtils.validatePassword(password);
|
|
// создание пользователя
|
|
}
|
|
|
|
public void updateUser(String email, String password) {
|
|
ValidationUtils.validateEmail(email);
|
|
ValidationUtils.validatePassword(password);
|
|
// обновление пользователя
|
|
}
|
|
}
|
|
```
|
|
|
|
### KISS (Будь проще)
|
|
**Делай проще**
|
|
|
|
- Простые решения часто лучше сложных
|
|
- Избегайте преждевременной оптимизации
|
|
- Читаемость кода важнее "умности"
|
|
|
|
**Пример нарушения:**
|
|
```java
|
|
// Нарушение KISS - излишняя сложность
|
|
class ComplexCalculator {
|
|
public double calculate(String expression) {
|
|
if (expression == null || expression.trim().isEmpty()) {
|
|
throw new IllegalArgumentException("Expression cannot be null or empty");
|
|
}
|
|
|
|
String trimmedExpression = expression.trim();
|
|
if (!trimmedExpression.matches("^[0-9+\\-*/\\s()]+$")) {
|
|
throw new IllegalArgumentException("Invalid characters in expression");
|
|
}
|
|
|
|
// Сложная логика парсинга и вычисления...
|
|
return evaluateComplexExpression(trimmedExpression);
|
|
}
|
|
|
|
private double evaluateComplexExpression(String expr) {
|
|
// Очень сложная реализация
|
|
return 0.0;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Правильная реализация:**
|
|
```java
|
|
// Соблюдение KISS - простое решение
|
|
class SimpleCalculator {
|
|
public double add(double a, double b) {
|
|
return a + b;
|
|
}
|
|
|
|
public double subtract(double a, double b) {
|
|
return a - b;
|
|
}
|
|
|
|
public double multiply(double a, double b) {
|
|
return a * b;
|
|
}
|
|
|
|
public double divide(double a, double b) {
|
|
if (b == 0) {
|
|
throw new IllegalArgumentException("Division by zero");
|
|
}
|
|
return a / b;
|
|
}
|
|
}
|
|
|
|
// Использование
|
|
SimpleCalculator calc = new SimpleCalculator();
|
|
double result = calc.add(calc.multiply(2, 3), calc.divide(10, 2)); // 2*3 + 10/2 = 11
|
|
```
|
|
|
|
### YAGNI (Вам это не понадобится)
|
|
**Тебе это не понадобится**
|
|
|
|
- Не реализуйте функциональность "на будущее"
|
|
- Добавляйте функции только когда они действительно нужны
|
|
- Избегайте over-engineering
|
|
|
|
**Пример нарушения:**
|
|
```java
|
|
// Нарушение YAGNI - преждевременная оптимизация
|
|
class UserRepository {
|
|
private Map<String, User> cache = new ConcurrentHashMap<>();
|
|
private List<User> users = new ArrayList<>();
|
|
private UserIndexer indexer = new UserIndexer();
|
|
private UserValidator validator = new UserValidator();
|
|
private UserMetricsCollector metrics = new UserMetricsCollector();
|
|
|
|
public void saveUser(User user) {
|
|
// Валидация (может не понадобиться)
|
|
validator.validate(user);
|
|
|
|
// Индексация (может не понадобиться)
|
|
indexer.index(user);
|
|
|
|
// Кэширование (может не понадобиться)
|
|
cache.put(user.getId(), user);
|
|
|
|
// Сохранение
|
|
users.add(user);
|
|
|
|
// Метрики (может не понадобиться)
|
|
metrics.recordSave(user);
|
|
}
|
|
}
|
|
```
|
|
|
|
**Правильная реализация:**
|
|
```java
|
|
// Соблюдение YAGNI - только необходимая функциональность
|
|
class UserRepository {
|
|
private List<User> users = new ArrayList<>();
|
|
|
|
public void saveUser(User user) {
|
|
if (user == null) {
|
|
throw new IllegalArgumentException("User cannot be null");
|
|
}
|
|
users.add(user);
|
|
}
|
|
|
|
public User findById(String id) {
|
|
if (id == null) {
|
|
throw new IllegalArgumentException("ID cannot be null");
|
|
}
|
|
|
|
return users.stream()
|
|
.filter(u -> u.getId().equals(id))
|
|
.findFirst()
|
|
.orElse(null);
|
|
}
|
|
}
|
|
|
|
// Когда понадобится кэширование - добавим
|
|
// Когда понадобится валидация - добавим
|
|
// Когда понадобится индексация - добавим
|
|
```
|
|
|
|
## Принципы модульности
|
|
|
|
### Слабая связанность (Loose Coupling)
|
|
- Компоненты минимально зависят друг от друга
|
|
- Изменения в одном компоненте не влияют на другие
|
|
- Легче тестировать и поддерживать
|
|
|
|
**Пример нарушения:**
|
|
```java
|
|
// Нарушение - тесная связанность
|
|
class OrderService {
|
|
private MySQLDatabase database;
|
|
private SMTPEmailService emailService;
|
|
private FileLogger logger;
|
|
|
|
public OrderService() {
|
|
this.database = new MySQLDatabase("localhost", "orders_db");
|
|
this.emailService = new SMTPEmailService("smtp.gmail.com", 587);
|
|
this.logger = new FileLogger("/var/log/orders.log");
|
|
}
|
|
|
|
public void processOrder(Order order) {
|
|
// Прямая зависимость от конкретных реализаций
|
|
database.save(order);
|
|
emailService.sendEmail(order.getCustomerEmail(), "Order processed");
|
|
logger.log("Order processed: " + order.getId());
|
|
}
|
|
}
|
|
```
|
|
|
|
**Правильная реализация:**
|
|
```java
|
|
// Соблюдение - слабая связанность
|
|
interface OrderRepository {
|
|
void save(Order order);
|
|
}
|
|
|
|
interface NotificationService {
|
|
void sendNotification(String recipient, String message);
|
|
}
|
|
|
|
interface LoggingService {
|
|
void log(String message);
|
|
}
|
|
|
|
class OrderService {
|
|
private OrderRepository repository;
|
|
private NotificationService notificationService;
|
|
private LoggingService loggingService;
|
|
|
|
public OrderService(OrderRepository repository,
|
|
NotificationService notificationService,
|
|
LoggingService loggingService) {
|
|
this.repository = repository;
|
|
this.notificationService = notificationService;
|
|
this.loggingService = loggingService;
|
|
}
|
|
|
|
public void processOrder(Order order) {
|
|
repository.save(order);
|
|
notificationService.sendNotification(
|
|
order.getCustomerEmail(),
|
|
"Order processed"
|
|
);
|
|
loggingService.log("Order processed: " + order.getId());
|
|
}
|
|
}
|
|
|
|
// Конкретные реализации
|
|
class MySQLOrderRepository implements OrderRepository {
|
|
@Override
|
|
public void save(Order order) {
|
|
// MySQL специфичная логика
|
|
}
|
|
}
|
|
|
|
class EmailNotificationService implements NotificationService {
|
|
@Override
|
|
public void sendNotification(String recipient, String message) {
|
|
// Email специфичная логика
|
|
}
|
|
}
|
|
|
|
class FileLoggingService implements LoggingService {
|
|
@Override
|
|
public void log(String message) {
|
|
// File logging специфичная логика
|
|
}
|
|
}
|
|
```
|
|
|
|
### Высокая сплоченность (High Cohesion)
|
|
- Элементы внутри модуля тесно связаны
|
|
- Модуль имеет четко определенную ответственность
|
|
- Все функции модуля работают для достижения одной цели
|
|
|
|
**Пример нарушения:**
|
|
```java
|
|
// Нарушение - низкая сплоченность
|
|
class UtilityClass {
|
|
public void sendEmail(String to, String subject, String body) {
|
|
// Отправка email
|
|
}
|
|
|
|
public void calculateTax(double amount, double rate) {
|
|
// Расчет налога
|
|
}
|
|
|
|
public void validatePassword(String password) {
|
|
// Валидация пароля
|
|
}
|
|
|
|
public void formatDate(Date date, String format) {
|
|
// Форматирование даты
|
|
}
|
|
|
|
public void compressFile(String filePath) {
|
|
// Сжатие файла
|
|
}
|
|
}
|
|
```
|
|
|
|
**Правильная реализация:**
|
|
```java
|
|
// Соблюдение - высокая сплоченность
|
|
class EmailService {
|
|
public void sendEmail(String to, String subject, String body) {
|
|
validateEmail(to);
|
|
String formattedBody = formatEmailBody(body);
|
|
deliverEmail(to, subject, formattedBody);
|
|
}
|
|
|
|
private void validateEmail(String email) {
|
|
// Валидация email адреса
|
|
}
|
|
|
|
private String formatEmailBody(String body) {
|
|
// Форматирование тела письма
|
|
return body;
|
|
}
|
|
|
|
private void deliverEmail(String to, String subject, String body) {
|
|
// Доставка email
|
|
}
|
|
}
|
|
|
|
class TaxCalculator {
|
|
public double calculateTax(double amount, double rate) {
|
|
validateAmount(amount);
|
|
validateRate(rate);
|
|
return amount * rate;
|
|
}
|
|
|
|
private void validateAmount(double amount) {
|
|
if (amount < 0) {
|
|
throw new IllegalArgumentException("Amount cannot be negative");
|
|
}
|
|
}
|
|
|
|
private void validateRate(double rate) {
|
|
if (rate < 0 || rate > 1) {
|
|
throw new IllegalArgumentException("Rate must be between 0 and 1");
|
|
}
|
|
}
|
|
}
|
|
|
|
class PasswordValidator {
|
|
public boolean isValid(String password) {
|
|
return hasMinimumLength(password) &&
|
|
containsUppercase(password) &&
|
|
containsLowercase(password) &&
|
|
containsDigit(password);
|
|
}
|
|
|
|
private boolean hasMinimumLength(String password) {
|
|
return password.length() >= 8;
|
|
}
|
|
|
|
private boolean containsUppercase(String password) {
|
|
return password.chars().anyMatch(Character::isUpperCase);
|
|
}
|
|
|
|
private boolean containsLowercase(String password) {
|
|
return password.chars().anyMatch(Character::isLowerCase);
|
|
}
|
|
|
|
private boolean containsDigit(String password) {
|
|
return password.chars().anyMatch(Character::isDigit);
|
|
}
|
|
}
|
|
```
|