Не знаю как избавиться от большого количества параметров в конструкторе

Рейтинг: 0Ответов: 1Опубликовано: 05.02.2023

Всем привет. Делаю свой первый проект и решил воспользоваться паттерном Command. В этом паттерне есть специальный класс CommandContainer. И так как каждая группа команд работает с несколькими сервисами, то конструктор будет иметь в будущем много параметров. Пример конструктора моего конструктора:

public class CommandContainer {
    
    private final Map<String, Command> commandMap = new HashMap<>();
    private final Command unknownCommand;

    public CommandContainer(SendBotMessageService sendBotMessageService, WorkerService workerService) {

        commandMap.put(CommandName.START.getCommandName(), new StartCommand(sendBotMessageService));
        commandMap.put(CommandName.HELP.getCommandName(), new HelpCommand(sendBotMessageService));
        commandMap.put(CommandName.CREATE_WORKER.getCommandName(),
                       new CreateWorkerCommand(sendBotMessageService, workerService));

        unknownCommand = new UnknownCommand(sendBotMessageService);
    }

    public Command retrieveCommand(String commandIdentifier) {
        return commandMap.getOrDefault(commandIdentifier, unknownCommand);
    }
}

Ну и помимо SendBotMessageService sendBotMessageService, WorkerService workerService нужно будет добавлять и другие сервисы, которые работают с командами. Подскажите как это можно упростить?

Ответы

▲ 2Принят

Учитывая, что в тегах вопроса присутствует spring, можно воспользоваться группировкой бинов в коллекции .

Судя по коду - все команды являются наследниками класса Command (либо - реализуют интерфейс Command, в данном случае это не играет роли).

Тогда, код можно упростить до:

@Component
public class CommandContainer {
    
    private final Map<String, Command> commandMap;
    private final Command unknownCommand;

    public CommandContainer(List<Command> commands, UnknownCommand unknownCommand) {
        // технически - получать отдельно UnknownCommand не нужно - её вполне можно вычленить из общего списка commands
        commandMap = commands.stream()
            .filter(command->!Objects.equals(command.getCommandName(), unknownCommand.getCommandName())
            .collect(Collectors.toUnmodifiableMap(Command::getCommandName, command->command, (o1, o2)->o1);
        this.unknownCommand = unknownCommand;
    }

    public Command retrieveCommand(String commandIdentifier) {
        return commandMap.getOrDefault(commandIdentifier, unknownCommand);
    }
}

Каждый бин, наследующийся (или реализующий) Command, в своем конструкторе пускай получает тот набор бинов, который ему нужен. Это будут его личные потребности, о которых контейнеру знать совершенно не интересно. Главное, чтобы все параметры были бинами:

@Component
public class UnknownCommand extends Command {

    private final SendBotMessageService sendBotMessageService;

    public UnknownCommand(SendBotMessageService sendBotMessageService){
        this.sendBotMessageService = sendBotMessageService;
    }

    public String getCommandName(){
      return "Unknown";
    }

}

Всё остальное будет проблемами Spring: определить параметры конструктора каждой команды (и контейнера команд), построить граф зависимостей, и, наконец, в правильном порядке вызвать конструкторы с подстановкой требуемых объектов.