Как реализовать паттерн Посетитель для подтипов абстрактного класса или интерфейса?

У меня возникла эта конкретная проблема, с которой я не мог разобраться с другими вопросами. Я пытаюсь реализовать посетителя для игры, посетитель - это класс Attack, и он должен искать в матрице ячейки, которые могут содержать Characters, затем, если персонаж враг, а не друг, повредить его.

У меня возникли проблемы с тем, чтобы не использовать InstanceOf для посещения персонажа, так как это нарушает принцип открытого-закрытого. Вот мой код:

Интерфейс для посетителей

public interface Visitor {
    public void visit(GroundCell c);
    public void visit(MountainCell c);
    public void visit(BuildingCell c);
    public void visit(WaterCell c);
    public void visit(Foe f);
    public void visit(Friend f);}

Атака на абстрактный класс

public abstract class Attack implements Visitor {

}

Атака на конкретный класс

public class TankAttack extends Attack{

...

@Override
public void visit(GroundCell c) {
    //here, i'd like to call  c.getCharacter.accept(this) 
}

Но я получаю сообщение об ошибке Я должен сначала реализовать Visit(Character c), когда он нужен только для его подклассов.

Что я должен сделать в классе TankAttack, чтобы посетить подклассы Friend или Foe Character и не сломать дизайн с помощью InstanceOf?

Изменить для уточнения: друг и враг являются подклассами персонажа.


person JulianP    schedule 03.03.2019    source источник


Ответы (2)


Посетитель так не работает.

Все классы, объявленные здесь как параметр, являются посещаемыми классами:

public void visit(GroundCell c);
public void visit(MountainCell c);
public void visit(BuildingCell c);
public void visit(WaterCell c);
public void visit(Foe f);
public void visit(Friend f);}

Все они должны определять метод accept(Visitor visitor), который означает "Я согласен на посещение посетителем".
Внутри них мы вызываем: visitor.visit(this) для эффективного выполнения посещения с ожидаемой перегрузкой (двойная отправка).

Таким образом, вы можете написать:

Attack attack = new TankAttack(); // visitor
Visited visitedOne = new GroundCell(); // visited one
Visited visitedTwo = new MountainCell(); // visited two

// accept the visitor and perform the double dispatch to invoke the specific method
visitedOne.accept(attack); 
visitedTwo.accept(attack); 

Или, в более общем случае, вы предоставляете метод с базовым типом посетителя в качестве параметра, например:

public void attack(Visited visited, Attack attack){
     visited.accept(attack); // no need instance of
}

И используйте его:

attack(new GroundCell(), attack); 
attack(new MoutainCell(), attack); 
person davidxxx    schedule 03.03.2019
comment
Я полагаю, что у меня проблема с универсальностью. Что не так, так это не мои посещаемые классы, а тот факт, что я хочу посетить подклассы класса, возвращаемого методом. Я получаю персонажа из Groundcell.getCharacter(), и мне нужно посетить его, который может быть другом или врагом. Компилятор просит меня реализовать метод Accept(Visitor v) в Character, когда он мне там особо не нужен. - person JulianP; 03.03.2019
comment
Как и в теле вашего визита(), которое вы хотите вызвать accept() (хотя это наоборот), я думаю, что вы не уловили общую идею шаблона. Почему бы вам не подумать, что Character (то есть, по-видимому, посещаемый класс) не нужно определять метод accept(). Так работают шаблоны. - person davidxxx; 03.03.2019
comment
Таким образом, если символы реализуют accept, когда я посещаю возвращенный персонаж, он вызовет Character.accept(), а не тот, который мне нужен в подклассе (friend.accept() или foe.accept()), который будет иметь другое поведение. Это моя проблема. Мне нужно иметь возможность определить, каким экземпляром будет персонаж, чтобы я мог вызвать его конкретное поведение. - person JulianP; 03.03.2019

Я решил это через несколько часов. Эта проблема была вызвана не ошибочной реализацией шаблона Посетитель, а тем, что суперкласс называется Character, так же как и java.lang.Character. Будьте осторожны, чтобы не перезаписать этот класс при программировании java-игры.

person JulianP    schedule 03.03.2019