Избегайте использования InstanceOf с шаблоном посетителя - Java

Я искал об этом объекте в Stack Overflow. Я нашел следующее решение:

Объяснение шаблона посетителя

Теперь мой случай похож на этот. Мне нужно, однако, чтобы избежать использования "instanceOf".

У меня есть игра, в которой названы башни MonkeyTower, CannonTower, OctoTower... и некоторые другие классы используют instanceOf сравнивать.

Вот пример класса, который использует instanceOf:

BloonsTower.java

public void mousePressed(Point p) {
        Tower t = null;
        selectedTower = towerInfo[ insertTowerIdx ].getTower();

        if( selectedTower instanceof MonkeyTower )
            t = tCreator.createMonkey();
        else if( selectedTower instanceof OctoTower )
            t = tCreator.createOctogonal();
        else if( selectedTower instanceof CannonTower )
            t = tCreator.createCannon();
        else if( selectedTower instanceof MortarTower )
            t = tCreator.createMortar();
        setMoney( money - towerInfo[ insertTowerIdx ].getPrice() );
        t.setPosition( p );
        world.addTower(t);
        currentState = new SelectTowerState();
    }

ManipulatorCreator.java

if( t instanceof MonkeyTower )
        return null;
    else if( t instanceof OctoTower )
        return new OctoManipulator( t );
    else if( t instanceof CannonTower )
        return null;
    else if( t instanceof MortarTower )
        return new MortarManipulator( (MortarTower)t );
    return man;

И GameWriter:

public void saveFile( File file, int round, int money, int lives, World m ) throws IOException {
    PrintWriter out = new PrintWriter( new BufferedWriter( new FileWriter( file) ) );

    out.println( round );
    out.println( money );
    out.println( lives );
    Tower []torres = m.getTowers();
    out.println( torres.length );   // escrever o nº de torres
    for( Tower t : torres ){
        Point p = t.getComponent().getPosicao();
        // escrever a posição e o tipo de torre
        out.print(p.x+"\t" + p.y+"\t" );
        if( t instanceof MonkeyTower )
            out.println( "macaco" );
        else if( t instanceof OctoTower )
            out.println( "octo" );
        else if( t instanceof CannonTower )
            out.println( "canhao" );
        else if( t instanceof MortarTower )
            out.println( "morteiro" );
    }

    out.close();
}

Я создал класс для посетителя, который посещает каждую башню:

public class TowerVisitor implements Visitor{

    public void visit(MonkeyTower monkey) {
        // TODO Auto-generated method stub
    }

    public void visit(CannonTower cannon) {
        // TODO Auto-generated method stub
    }

    public void visit(MortarTower mortar) {
        // TODO Auto-generated method stub
    }

    public void visit(OctoTower octo) {
        // TODO Auto-generated method stub
    }
}

И в каждой башне, которую я создал, есть метод accept это возвращает себя

Теперь я застрял в том, что положить в метод visit и как использовать шаблон для переключения всех instanceOf"S.

Благодарю.

3 ответа

Вы должны использовать основные шаблоны объектно-ориентированного программирования, а именно наследование, а не шаблон посетителя. У вас есть несколько различных типов башен и аналогичные действия (создание, управление, toString и т. Д.), Которые должны быть реализованы по-разному для каждого типа башни. Классический пример наследования.

public abstract class Tower {
  public abstract Tower create();
  public abstract Manipulator manipulate();
}

---

public class MortarTower extends Tower {
  @Override
  public MortarTower create() {
    return new MortarTower();
  }

  @Override
  public MortarManipulator manipulate() {
    return new MortarManipulator(this);
  }

  @Override
  public String toString() {
    return "morteiro";
  }
}

---

public void mousePressed(Point p) {
  selectedTower = towerInfo[insertTowerIdx].getTower();
  setMoney(money - towerInfo[insertTowerIdx].getPrice());
  Tower t = selectedTower.create();
  t.setPosition(p);
  world.addTower(t);
  currentState = new SelectTowerState();
}

И так далее.

Если вы хотите делать все с посетителями, вам нужно создавать посетителей для каждого случая, т.е. для создания башни и манипулятора и для сохранения файла. Также я думаю, что в этом случае вам не нужно создавать метод accept для каждой башни, но вы можете. Метод Accept получит некоего общего посетителя, и все, что он будет делать, это просто вызвать метод visitor.visit. Что-то вроде:

          public class CannonTower {
    public void accept(CommonVisitor visitor) {
        visitor.visit(this);
    }
}

Создайте общий интерфейс посетителя с пустыми методами:

          public interface CommonVisitor {
    default void visit(MonkeyTower monkey) {
        //  nothing to do to not implement when it is not required at all
    }

    default void visit(CannonTower cannon) {
        //  nothing to do
    }

    default void visit(MortarTower mortar) {
        //  nothing to do
    }

    default void visit(OctoTower octo) {
        //  nothing to do
    }
}

Давайте создадим посетителя для BloonsTower.java для создания башен (имхо ваш код выглядит немного странно - вы получаете башню и создаете такую ​​же башню):

      @Getter
public class TowerCreatorVisitor implements CommonVisitor {
    public TowerCreatorVisitor(TowerCreator tCreator) {
        this.tCreator = tCreator;
    }

    TowerCreator tCreator;
    Tower tower;

    public void visit(MonkeyTower monkey) {
        tower = tCreator.createMonkey();
    }

    public void visit(CannonTower cannon) {
        tower = tCreator.createCannon();
    }

    public void visit(MortarTower mortar) {
        tower = tCreator.createMortar();
    }

    public void visit(OctoTower octo) {
        tower = tCreator.createOcto();
    }
   }

BloonsTower.java стал:

        public void mousePressed(Point p) {
    Tower t = null;
    selectedTower = towerInfo[ insertTowerIdx ].getTower();

    TowerCreatorVisitor towerCreatorVisitor = new TowerCreatorVisitor(tCreator);
    selectedTower.accept(towerCreatorVisitor);
    // you may not use accept at all, then just 
    // towerCreatorVisitor.visit(selectedTower);
    // up to you)
    t = towerCreatorVisitor.getTower();

    setMoney( money - towerInfo[ insertTowerIdx ].getPrice() );
    t.setPosition( p );
    world.addTower(t);
    currentState = new SelectTowerState();
}

Чтобы создать манипулятор:

          @Getter
public class ManipulatorCreatorVisitor implements CommonVisitor {
    Manipulator manipulator = new Manipulator();


    public void visit(MonkeyTower monkey) {
        tower = null;
    }

        public void visit(CannonTower cannon) {
            tower = null;
        }
    
        public void visit(MortarTower mortar) {
            manipulator = new MortarManipulator(mortar);
        }
    
        public void visit(OctoTower octo) {
            manipulator = new OctoManipulator(octo);
        }
    }

Создатель маниплатора:

      public Manipulator getManipulator(Tower t) {
    ManipulatorCreatorVisitor manipulatorCreatorVisitor = new ManipulatorCreatorVisitor();
    t.accept(manipulatorCreatorVisitor);
    return manipulatorCreatorVisitor.getManipulator();
}

Чтобы сохранить файл:

        @AllArgsConstructor
public class FileWriterVisitor implements CommonVisitor {
    PrintWriter out;

    public void visit(MonkeyTower monkey) {
        out.println( "macaco" );
    }

    public void visit(CannonTower cannon) {
        out.println( "canhao" );
    }

    public void visit(MortarTower mortar) {
        out.println( "morteiro" );    }

    public void visit(OctoTower octo) {
        out.println( "octo" );    }
}

Автор игры:

      public void saveFile( File file, int round, int money, int lives, World m ) throws IOException {
        PrintWriter out = new PrintWriter( new BufferedWriter( new FileWriter( file) ) );

        out.println( round );
        out.println( money );
        out.println( lives );
        Tower []torres = m.getTowers();
        out.println( torres.length );   // escrever o nº de torres
        FileWriterVisitor fileWriterVisitor = new FileWriterVisitor(out);
        for( Tower t : torres ){
            Point p = t.getComponent().getPosicao();
            // escrever a posição e o tipo de torre
            out.print(p.x+"\t" + p.y+"\t" );
            fileWriterVisitor.visit(t);
            // or t.accept(fileWriterVisitor);
        }

        out.close();
    }

Вы должны использовать FactoryMethod или же AbstractFactory шаблон (если вы хотите создать ManipulatorDactory независимо от Tower) вместо Visitor.

Взгляните на этот Factory Method и Abstract Facotry для более подробной информации.

Пример кода:

public class FactoryMethodDemo{

    public FactoryMethodDemo(){

    }
    public Tower getTower(String type){
        if ( type.equalsIgnoreCase("monkey")){
            return new MonkeyTower();
        }else if ( type.equalsIgnoreCase("octo")){
            return new OctoTower();
        }else if ( type.equalsIgnoreCase("canon")){
            return new CannonTower();
        }else if ( type.equalsIgnoreCase("mortal")){
            return new MortarTower();
        }
        return null;
    }
    public static void main(String args[]){
        FactoryMethodDemo factory = new FactoryMethodDemo();
        Tower tower = factory.getTower(args[0]);
        System.out.println("Tower:"+tower);
    }
}
abstract class Tower{
    Manipulator manipulator = null;
    public Tower(){

    }
    public String toString(){
        return this.getClass().getSimpleName()+":manipulator:"+manipulator;
    }
    public Manipulator getManipulator(){
        return manipulator;
    }
}
class MonkeyTower extends Tower{
    public MonkeyTower(){

    }
}
class OctoTower extends Tower{
    public OctoTower(){
        manipulator = new OctoManipulator();
    }

}
class CannonTower extends Tower{
    public CannonTower(){

    }
}
class MortarTower  extends Tower{
    public MortarTower(){
        manipulator = new MortarManipulator();
    }

}
class Manipulator{

}
class OctoManipulator extends Manipulator{

}
class MortarManipulator extends Manipulator{

}

выход:

D:\Study\Java>java FactoryMethodDemo monkey
Tower:MonkeyTower:manipulator:null

D:\Study\Java>java FactoryMethodDemo mortal
Tower:MortarTower:manipulator:MortarManipulator@4187a8e0

D:\Study\Java>java FactoryMethodDemo octo
Tower:OctoTower:manipulator:OctoManipulator@46fb3d6

D:\Study\Java>java FactoryMethodDemo canon
Tower:CannonTower:manipulator:null

Связанные вопросы SE:

Заводская модель. Когда использовать фабричные методы?

В чем принципиальная разница между фабричными и абстрактными фабричными моделями?

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