3

I searched about this subjet arround here in Stack Overflow. I found the following solution:

Explanation of Visitor Pattern

Now, my case is simillar to this one. I need, however, to avoid the use of "instanceOf".

I have a game that has towers named MonkeyTower, CannonTower, OctoTower... and some other classes uses the instanceOf to compare.

Here's an example of a class that uses the 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;

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

What i've created is a class for visitor that visits every tower:

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

And, in each tower I've created has a method accept that returns itself

Now, I'm stuck in what to put inside the method visit and how to use the pattern to switch all the instanceOf's.

Thanks.

Community
  • 1
  • 1
user1987003
  • 37
  • 1
  • 8

3 Answers3

1

You should use core Object Oriented Programming patterns, namely inheritance, not a visitor pattern. You have several different types of towers, and similar actions (create, manipulate, toString, etc.) that should be implemented differently for each type of tower. A classic example of inheritance.

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

And so on.

dimo414
  • 47,227
  • 18
  • 148
  • 244
  • 1
    Note that the Visitor Pattern _is_ OOP. – jaco0646 Jan 08 '16 at 14:13
  • Of course you're right. I trust you know what I mean, though. Can you suggest a better way of phrasing it? "Core OOP"? "Basic OOP"? – dimo414 Jan 08 '16 at 17:51
  • If the intent was to follow @ravindra's comment and implement the [Factory Method pattern](https://sourcemaking.com/design_patterns/factory_method) then I would phrase it as, "_You should use the Factory Method pattern, not the Visitor pattern._" – jaco0646 Jan 08 '16 at 19:17
  • At least from what I've seen, I don't see a need for factories; I'm simply suggesting method overriding in subclasses. – dimo414 Jan 08 '16 at 19:56
  • this is a project for university... i cant compare strings to check if it's a monkey tower or a cannon tower. The main objective of this project is to use patterns to avoid some code. this is one of the places where a pattern (or more than one) should be used. – user1987003 Jan 09 '16 at 13:09
  • Where am I suggesting you do string comparisons? Inheritance *is* a pattern. – dimo414 Jan 09 '16 at 17:32
  • @user1987003 His answer uses the [factory pattern](https://en.m.wikipedia.org/wiki/Factory_method_pattern#Example_implementations). Also, you should never force design patterns. Design patterns are there to solve problems. Forcing these patterns could result in misuse of them, which may lead to more problems – Vince Jan 09 '16 at 17:41
0

You have to use FactoryMethod orAbstractFactory pattern (If you want to createe ManipulatorDactory independemt of Tower) instead of Visitor.

Have a look at this Factory Method and Abstract Facotry for more details.

Some sample code:

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{

}

output:

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

Related SE questions :

Factory Pattern. When to use factory methods?

What is the basic difference between the Factory and Abstract Factory Patterns?

Community
  • 1
  • 1
Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
  • the abstract factory shouldn't be used when we want to create a different types of famillies ? we are working with only a type -> tower. in this case we need to return new objects of towers, right? but, not in all cases. i was thinking on continue with visitor, but for the cases that needs to return a new object, implement a prototype – user1987003 Jan 09 '16 at 13:38
  • My answer implemented only FactoryMethod pattern. Concrete tower is returning proper manipulator object. I have handled the conditions you quoted in question. – Ravindra babu Jan 09 '16 at 13:58
0

If you want to do all with visitors you need to create visitors for each case, i.e. for tower and manipulator creation and for file saving. Also I think in this case you don't need to create accept method for each tower, but you can. Accept method will receive some general Visitor and all what it will do is just call visitor.visit method. Something like:

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

Create some common Visitor interface, with empty methods:

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

Let's create visitor for BloonsTower.java to create towers (imho your code looks a little strange - you get tower and create the same tower):

@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 became:

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

To create Manipulator:

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

ManiplatorCreator :

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

To save file:

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

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
        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();
    }
Valeriy K.
  • 2,616
  • 1
  • 30
  • 53