← Назад к вопросам
Как организовать работу методов в классе в зависимости от выбранного режима класса
1.8 Middle🔥 161 комментариев
#SOLID и паттерны проектирования
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Как организовать работу методов в зависимости от выбранного режима класса
Проблема
Часто класс должен вести себя по-разному в зависимости от режима (mode). Например:
- Режим DEBUG vs PRODUCTION
- Режим READ vs WRITE
- Режим ONLINE vs OFFLINE
Плохой подход — множественные if/else вычеты внутри каждого метода.
Решение 1: State Pattern (предпочтительно)
Используй паттерн State для инкапсуляции логики каждого режима:
// Интерфейс для каждого режима
public interface FileProcessorMode {
void processFile(File file);
void saveFile(File file, String content);
void deleteFile(File file);
}
// Режим для production
public class ProductionMode implements FileProcessorMode {
private static final Logger logger = LoggerFactory.getLogger(ProductionMode.class);
@Override
public void processFile(File file) {
logger.info("Processing file: " + file.getName());
// Production логика
}
@Override
public void saveFile(File file, String content) {
logger.info("Saving file: " + file.getName());
// Обычное сохранение
}
@Override
public void deleteFile(File file) {
logger.info("Deleting file: " + file.getName());
// Удаление
}
}
// Режим для debug
public class DebugMode implements FileProcessorMode {
private static final Logger logger = LoggerFactory.getLogger(DebugMode.class);
@Override
public void processFile(File file) {
logger.debug("[DEBUG] Processing file: " + file.getName());
logger.debug("File size: " + file.length() + " bytes");
// Debug логика с дополнительной информацией
}
@Override
public void saveFile(File file, String content) {
logger.debug("[DEBUG] Would save file: " + file.getName());
logger.debug("Content length: " + content.length());
// Не сохраняем реально в debug
}
@Override
public void deleteFile(File file) {
logger.debug("[DEBUG] Would delete file: " + file.getName());
// Не удаляем реально в debug
}
}
// Основной класс
@Service
public class FileProcessor {
private final FileProcessorMode mode;
// Конструктор инъектирует нужный режим
public FileProcessor(FileProcessorMode mode) {
this.mode = mode;
}
public void process(File file) {
mode.processFile(file); // Делегируем режиму
}
public void save(File file, String content) {
mode.saveFile(file, content); // Делегируем режиму
}
public void delete(File file) {
mode.deleteFile(file); // Делегируем режиму
}
}
// Spring конфигурация
@Configuration
public class ModeConfig {
@Bean
@ConditionalOnProperty(name = "app.mode", havingValue = "debug")
public FileProcessorMode debugMode() {
return new DebugMode();
}
@Bean
@ConditionalOnProperty(name = "app.mode", havingValue = "production")
public FileProcessorMode productionMode() {
return new ProductionMode();
}
}
Решение 2: Strategy Pattern с Enum
public enum ProcessingMode {
DEBUG(new DebugStrategy()),
PRODUCTION(new ProductionStrategy()),
SAFE(new SafeStrategy());
private final Strategy strategy;
ProcessingMode(Strategy strategy) {
this.strategy = strategy;
}
public Strategy getStrategy() {
return strategy;
}
// Интерфейс Strategy
public interface Strategy {
void execute(String data);
void validate(String data);
}
// Реализация для DEBUG
private static class DebugStrategy implements Strategy {
@Override
public void execute(String data) {
System.out.println("[DEBUG] Executing: " + data);
}
@Override
public void validate(String data) {
System.out.println("[DEBUG] Validation disabled");
}
}
// Реализация для PRODUCTION
private static class ProductionStrategy implements Strategy {
@Override
public void execute(String data) {
if (validate(data)) {
// Выполняем
}
}
@Override
public void validate(String data) {
if (data == null || data.isEmpty()) {
throw new IllegalArgumentException("Data cannot be empty");
}
}
}
// Реализация для SAFE
private static class SafeStrategy implements Strategy {
@Override
public void execute(String data) {
validate(data);
try {
// Защищённое выполнение
} catch (Exception e) {
// Graceful error handling
}
}
@Override
public void validate(String data) {
// Строгая валидация
}
}
}
// Использование
@Service
public class DataProcessor {
private final ProcessingMode mode;
public DataProcessor(@Value("${app.processing.mode}") String modeStr) {
this.mode = ProcessingMode.valueOf(modeStr.toUpperCase());
}
public void processData(String data) {
mode.getStrategy().execute(data);
}
}
Решение 3: Decorator Pattern
Для добавления поведения к существующему функционалу:
// Базовый интерфейс
public interface DataHandler {
void handle(String data);
}
// Базовая реализация
public class BasicDataHandler implements DataHandler {
@Override
public void handle(String data) {
System.out.println("Handling: " + data);
}
}
// Декоратор для логирования
public class LoggingDecorator implements DataHandler {
private final DataHandler handler;
private final Logger logger;
public LoggingDecorator(DataHandler handler) {
this.handler = handler;
this.logger = LoggerFactory.getLogger(LoggingDecorator.class);
}
@Override
public void handle(String data) {
logger.info("Processing data: " + data);
handler.handle(data);
logger.info("Processing complete");
}
}
// Декоратор для валидации
public class ValidationDecorator implements DataHandler {
private final DataHandler handler;
public ValidationDecorator(DataHandler handler) {
this.handler = handler;
}
@Override
public void handle(String data) {
if (data == null || data.isEmpty()) {
throw new IllegalArgumentException("Data is invalid");
}
handler.handle(data);
}
}
// Построение конвеера декораторов
public class DataProcessorConfig {
@Bean
public DataHandler dataHandler(
@Value("${app.debug:false}") boolean debug,
@Value("${app.validate:true}") boolean validate
) {
DataHandler handler = new BasicDataHandler();
if (debug) {
handler = new LoggingDecorator(handler);
}
if (validate) {
handler = new ValidationDecorator(handler);
}
return handler;
}
}
Решение 4: Template Method Pattern
public abstract class DataProcessor {
// Template method — общий алгоритм
public final void processData(String data) {
validate(data);
execute(data);
log(data);
}
// Обязательные этапы (должны переопределяться)
protected abstract void validate(String data);
protected abstract void execute(String data);
// Опциональный этап (по умолчанию ничего)
protected void log(String data) {
System.out.println("Processed: " + data);
}
}
// Реализация для production
public class ProductionProcessor extends DataProcessor {
@Override
protected void validate(String data) {
if (data == null || data.isEmpty()) {
throw new IllegalArgumentException("Invalid data");
}
}
@Override
protected void execute(String data) {
System.out.println("Executing in production: " + data);
}
@Override
protected void log(String data) {
System.out.println("[PRODUCTION] Processed: " + data);
}
}
// Реализация для debug
public class DebugProcessor extends DataProcessor {
@Override
protected void validate(String data) {
System.out.println("[DEBUG] Skipping validation");
}
@Override
protected void execute(String data) {
System.out.println("[DEBUG] Would execute: " + data);
}
@Override
protected void log(String data) {
System.out.println("[DEBUG] Processed: " + data + " (length: " + data.length() + ")");
}
}
Решение 5: Command Pattern
public interface Command {
void execute();
void undo();
}
public class FileCommand implements Command {
private final File file;
private final FileProcessorMode mode;
private String originalContent;
public FileCommand(File file, FileProcessorMode mode) {
this.file = file;
this.mode = mode;
}
@Override
public void execute() {
mode.processFile(file);
}
@Override
public void undo() {
// Отмена последней операции
}
}
// Очередь команд
public class CommandInvoker {
private final Queue<Command> history = new LinkedList<>();
public void executeCommand(Command command) {
command.execute();
history.offer(command);
}
public void undoLastCommand() {
Command command = history.poll();
if (command != null) {
command.undo();
}
}
}
Сравнение подходов
| Паттерн | Используй когда | Преимущества | Недостатки |
|---|---|---|---|
| State | Много режимов с разной логикой | Чистый код, легко расширять | Много классов |
| Strategy | Алгоритм может быть выбран runtime | Гибкость | Нужен контекст |
| Decorator | Нужна комбинация поведений | Гибкая комбинация | Может быть сложным |
| Template Method | Общий алгоритм с вариациями | Переиспользование кода | Жёсткая иерархия |
| Command | Нужна история операций | Отмена/повтор | Extra classes |
Рекомендуемый подход для 90% случаев
// Enum + Spring @ConditionalOnProperty
public enum AppMode {
DEBUG,
PRODUCTION,
TESTING
}
@Configuration
public class ModeConfig {
@Bean
public AppMode appMode(@Value("${app.mode:production}") String mode) {
return AppMode.valueOf(mode.toUpperCase());
}
}
@Service
public class MyService {
private final AppMode mode;
public MyService(AppMode mode) {
this.mode = mode;
}
public void doSomething() {
switch (mode) {
case DEBUG:
doDebugVersion();
break;
case PRODUCTION:
doProductionVersion();
break;
case TESTING:
doTestingVersion();
break;
}
}
}
Итог
- Для сложных режимов → State Pattern
- Для выбора алгоритма → Strategy Pattern
- Для добавления функционала → Decorator Pattern
- Для пошагового алгоритма → Template Method
- Для истории операций → Command Pattern
Выбирай паттерн в зависимости от complexity твоего случая и следуй SOLID принципам!