Как не копировать код, а создать один метод?

у меня есть метод

    private void positionMagican() {
        int x;
        int y;
        boolean magicanIsCreated;
        magicanIsCreated = false;
        while (!magicanIsCreated){
            x = random.nextInt(sizeX);
            y = random.nextInt(sizeY);
            if (field.getFieldable(x,y) instanceof Empty){
                mag = new Magician(x,y,sizeX,sizeY,field,player,this);
                field.setFieldable(x,y,mag);
                magicanIsCreated = true;
            }
        }
    }

И точно такие же методы, только вместо Магикана змея, бочки и т.д.

Вот пример

    private void positionGoblin() {
        int x;
        int y;
        boolean goblinIsCreated;
        goblinIsCreated = false;
        while (!goblinIsCreated){
            x = random.nextInt(sizeX);
            y = random.nextInt(sizeY);
            if (field.getFieldable(x,y) instanceof Empty){
                goblin = new Goblin(x,y,player,field,this,sizeX,sizeY);
                field.setFieldable(x,y,goblin);
                goblinIsCreated = true; 
            }
        } 
     } ``` 

Здесь различия только в классе объекта и в его параметрах, из-за этого в проекте много одинаковых методов, и я не понимаю, как создать один метод, в который можно было бы ввести нужный параметр. Можно ли создать метод, объединяющий эти методы? Я не понимаю. Как обеспечить создание объекта нужного класса с нужными параметрами.

Передайте фабричную функцию в качестве обратного вызова, например Magician::new, Goblin::new и т. д. Убедитесь, что все они принимают одни и те же параметры в одном и том же порядке.

Bergi 30.06.2024 22:30

Предположительно, все эти классы реализуют общий интерфейс? Как это называется? Т.е. какой тип третьего параметра setFieldable?

Bergi 30.06.2024 22:31

все они используют один интерфейс, а третий параметр — объект для двумерного массива интерфейса Cell.

Morozov 310 30.06.2024 22:57
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
3
69
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Оберните свои конструкторы в единый интерфейс.

Конструкторы Magician и Goblin принимают параметры в разном порядке, но это нормально, потому что это одна из проблем, которую может решить интерфейс:

private interface CharacterGenerator {
    GameCharacter createCharacter(int x,
                                  int y,
                                  int sizeX,
                                  int sizeY,
                                  Field field,
                                  Player player,
                                  Game game);
}

(Я предполагаю, что Маг и Гоблин наследуют общего родителя, либо общий суперкласс, либо общий интерфейс, который я для примера назвал GameCharacter.)

Я решил использовать порядок, соответствующий конструктору Magician, но это не имеет значения, поскольку каждая реализация этого интерфейса может поступать с конструкторами по своему усмотрению, включая их переупорядочение:

private static class MagicianGenerator
implements CharacterGenerator {
    @Override
    public GameCharacter createCharacter(int x,
                                         int y,
                                         int sizeX,
                                         int sizeY,
                                         Field field,
                                         Player player,
                                         Game game) {

        return new Magician(x, y, sizeX, sizeY, field, player, game);
    }
}

private static class GoblinGenerator
implements CharacterGenerator {
    @Override
    public GameCharacter createCharacter(int x,
                                         int y,
                                         int sizeX,
                                         int sizeY,
                                         Field field,
                                         Player player,
                                         Game game) {

        return new Goblin(x, y, player, field, game, sizeX, sizeY);
    }
}

Теперь вы можете создать один метод позиционирования вместо двух методов и передать объект, реализующий FeatureGenerator, этому одному методу позиционирования:

private void positionCharacter(CharacterGenerator generator) {
    while (true) {
        int x = random.nextInt(sizeX);
        int y = random.nextInt(sizeY);
        if (field.getFieldable(x,y) instanceof Empty) {
            GameCharacter c = generator.createCharacter(
                x, y, sizeX, sizeY, field, player, this);
            field.setFieldable(x, y, c);
            break;
        }
    }
}

И этот метод будет вызываться с использованием одного из этих вызовов:

positionCharacter(new MagicianGenerator());
positionCharacter(new GoblinGenerator());

Обратите внимание, что я удалил вашу логическую переменную «create». Вам это не нужно. Просто используйте break, чтобы выйти из цикла.

Как отмечали другие, вы могли бы сделать это гораздо более кратким, если бы конструктор Magician и конструктор Goblin принимали одни и те же аргументы в точно таком же порядке, поскольку это позволило бы вам рассматривать интерфейс CharacterGenerator как функциональный интерфейс, и это позволит вам использовать ссылки на методы вместо явных классов реализации:

positionCharacter(Magician::new);
positionCharacter(Goblin::new);

Это сложный код, но этот вопрос и этот ответ можно считать ярким примером того, почему в последние годы люди чрезмерно склоняются к функциональному программированию и слепой критике ООП. Проще говоря, то, что должно быть рефакторингом для одного метода с добавленным параметром (будь то enum или даже boolean — это не тот случай) и проверкой if для создания Goblin или Magician, усложнено различными ненужными функциями языка и шаблонами псевдопроектирования. Это всего лишь мои два цента. Это противоположность KISS, это KICKME или Keep It Complication Keep Me Employed.

Yamin 30.06.2024 23:39

@Yamin Аргумент перечисления — разумная идея, если к нему не собираются добавлять дженерики. А если вы придумываете оператор switch или серию операторов if-else, он не будет особенно масштабируемым, если в игре будет 70 типов персонажей.

VGR 30.06.2024 23:46

VGR спасибо за этот шедевральный код ( ͡° ͜ʖ ͡°)

Morozov 310 30.06.2024 23:54

@Yamin Извините, но у меня слишком много игровых объектов, чтобы обрабатывать каждый отдельно.

Morozov 310 30.06.2024 23:56

@Yamin Я не вижу здесь никакой «слепой критики ООП»? Фабрика — это довольно стандартный шаблон ООП, и она гораздо лучше подходит для этого, чем параметр перечисления.

Bergi 01.07.2024 00:08

Другие вопросы по теме