Search code examples
javajava-8dry

How to not copy the code, but create one method?


I have a method

    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;
            }
        }
    }

And exactly the same methods, but instead of Magican there is a snake, barrels, etc.

Here's an example

    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; 
            }
        } 
     } ``` 

Here the differences are only in the class of the object and in its parameters, because of this there are many of the same methods in the project, and I don’t understand how to create one method into which the desired parameter could be entered. Is it possible to create a method that combines these methods? I don't understand. How to ensure that an object is created of the required class with the required parameters.


Solution

  • Wrap your constructors in a single interface.

    Your Magician and Goblin constructors take their parameters in a different order, but that’s okay, because that is one of the things an interface can solve:

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

    (I am going to assume that Magician and Goblin inherit a common parent, either a common superclass or a common interface—which, for the sake of example, I have named GameCharacter.)

    I chose to use the order that corresponds to the Magician constructor, but it doesn’t matter, because each implementation of that interface can choose to do with the constructors as it pleases, including reordering them:

    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);
        }
    }
    

    Now you can create one positioning method instead of two methods, and pass an object that implements CharacterGenerator to that one positioning method:

    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;
            }
        }
    }
    

    And that method would be called using one of these invocations:

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

    Notice that I removed your boolean ‘created’ variable. You don’t need it. Just use break to exit your loop.

    As others have pointed out, you could make this much more concise if you were to make the Magician constructor and the Goblin constructor take the same arguments in exactly the same order, since that would allow you to treat the CharacterGenerator interface as a functional interface, and it would allow you to use method references instead of explicit implementing classes:

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