Обобщить наследуемые классы в абстрактном JAVA

Рейтинг: 2Ответов: 2Опубликовано: 28.04.2023

Нужно обобщить два класса. Есть абстрактный класс:

public abstract class AbstractRobot extends UpdatableUnit {
    // --------------------------- Перемещение ------------------------------------
    public abstract boolean canMoveTo(Cell to);

    public abstract void move(Direction direct);
}

А также два наследующих класса:

public class StrongRobot extends AbstractRobot  {
    // --------------------------- Перемещение ------------------------------------
    @Override
    public boolean canMoveTo(Cell to) {return true;}

    @Override
    public void move(Direction direct) {

// Сделать вытаскивание предмета из ячейки, если ячейка не пустая, и переход в нее

        Cell pos = typedOwner();

        Cell newPos = pos.neighbor(direct);
        if(newPos == null) {
            return;
        }

        if(!canMoveTo(newPos)) {
            newPos.extractUnit();
        }

        pos.extractUnit();

        newPos.putUnit(this);

        fireStateChanged();
    }

    // -----------------------------------------------------------------
    @Override
    public String toString() {

        String msg;
        msg = "R(∞)";

        return msg;
    }
}

И, соответственно:

public class SimpleRobot extends AbstractRobot {

    // ------------------------------- Заряд ---------------------------------
    private int _charge = 25;
    
    private static final int REQUIRED_CHARGE_FOR_MOVE = 1;
    
    public int charge() {
        return _charge;
    }
    
    public boolean isAvailableCharge(int chargeQuery) {
        return chargeQuery <= _charge;
    }
    
    protected int reduceCharge(int chargeQuery) {
        int retrievedCharge = Math.min(_charge, chargeQuery);
        _charge -= retrievedCharge;
        return retrievedCharge;
    }

    // --------------------------- Перемещение ------------------------------------
    @Override
    public boolean canMoveTo(Cell to) {
        return to.isEmpty();
    }

    @Override
    public void move(Direction direct) {
       
        Cell pos = typedOwner();
        
        if(!isAvailableCharge(REQUIRED_CHARGE_FOR_MOVE)) {
            return;
        }
        
        Cell newPos = pos.neighbor(direct);
        if(newPos == null) {
            return;
        }

        if(!canMoveTo(newPos)) {
            return;
        }
        
        pos.extractUnit();
        newPos.putUnit(this);
        reduceCharge(REQUIRED_CHARGE_FOR_MOVE);

        fireStateChanged();
    }

    // -----------------------------------------------------------------

    @Override
    public String toString() {

        String msg;
        msg = "R(" + charge() + ")";

        return msg;
    }
}

Как вы могли заметить, перемещение в этих двух классах совпадает на 80%. Цель - обобщить классы, то есть вынести в абстрактный класс перемещение.

Ответы

▲ 3

Во-первых, обобщение классов в Java обозначает скорее параметризацию класса неким общим типом T, то есть в названии класса появляется указание на общий (generic) тип: public abstract class AbstractRobot<T> extends UpdatableUnit<T>

В данном же случае требуется вынести некий общий функционал из реализаций абстрактных методов в класс-родитель, то есть использовать паттерн шаблонный метод (template method) для рефакторинга поведения в методе move.

Соответственно, метод move переносится в класс-родитель и перестаёт быть абстрактным, однако в нём должны быть добавлены вызовы других-методов, в которых следует реализовать "подшаги", чтобы можно было реализовать существующие различия в логике между реализациями StrongRobot и SimpleRobot.
Если "подшаги" определены как абстрактные методы, то их понадобится переопределять при помощи заглушек в классах, где они не требуются, поэтому может быть лучше определить эти заглушки в родительском классе.

public abstract class AbstractRobot extends UpdatableUnit {

    public abstract boolean canMoveTo(Cell to);

    public void move(Direction direct) {
        if(!checkCondition()) {
            return;
        }
        
        Cell pos = typedOwner();
        
        Cell newPos = pos.neighbor(direct);
        if(newPos == null) {
            return;
        }

        if(!canMoveTo(newPos)) {
            return;
        }
        
        pos.extractUnit();
        newPos.putUnit(this);

        afterMove();

        fireStateChanged();
    }

    /**
        Метод для проверки условия движения
        @return true если движение разрешено
     */
    protected boolean checkCondition() { return true; }

    /**
        Метод для вызова перед отправкой события stateChanged
      */
    protected void afterMove() {}
}

Соответственно, реализация метода move в классе StrongRobot удаляется и заглушки для реализации шагов checkCondition / afterMove не нужны.

А в классе SimpleRobot потребуется переопределить только "подшаги":

public class SimpleRobot extends AbstractRobot {
    // ...

    @Override
    protected boolean checkCondition() { 
        return isAvailableCharge(REQUIRED_CHARGE_FOR_MOVE); 
    }

    @Override
    protected void afterMove() {
        reduceCharge(REQUIRED_CHARGE_FOR_MOVE);
    }
}
▲ 0

Возможно, не лучший вариант, но можно сделать и так.

  1. В абстрактный класс помещаете метод move() и выносите туда всё общее (В абстрактном классе могут быть неабстрактные методы).
  2. В каждом классе-наследники переопределяете метод move(), а первой строчкой вызываете метод из суперкласса с помощью super():
public void move(Direction direct) {
    super.move(direct);
    //Какие-то специфичные действия для конкретного объекта
}