2016-02-05 4 views
1

Я пытаюсь использовать шаблон посетителя с расширениями классов. У меня есть список классов животных, каждый из которых является другим животным. Когда я вызываю посетителя, он выполняет только разговор (Animal a), а не конкретный экземпляр объекта. Смотрите ниже:Образец шаблона Java

class Animal {} 
class Cat extends Animal {} 
class Dog extends Animal {} 
class Poodle extends Dog {} 

class Visitor { 
    public void talk(Animal a) { System.out.println("?"); } 
    public void talk(Cat a) { System.out.println("Meow"); } 
    public void talk(Dog a) { System.out.println("bark"); } 
    public void talk(Poodle a) { System.out.println("Arf"); } 
}  

public class Demo{ 

    public static void main(String []args){ 
     Visitor visitor = new Visitor(); 
     Animal list[] = { new Cat(), new Dog(), new Poodle() }; 

     for (Animal a : list) 
      visitor.talk(a); 
    } 
} 

Выход:

? 
? 
? 

В то время как я ожидал:

Meow 
bark 
Arf 

Любая идея, как реализовать посетителя, не подвергая много InstanceOf внутри одного разговора() метод?

+0

какое описание шаблона посетителя вы использовали? – wero

+0

Если вы хотите пройти через список как Животные и не переопределять функции, вы должны проверить с помощью 'instanceof', а затем отбросить объект. Например, внутри вашего списка 'if (экземпляр cat) {visitor.talk (((Cat) a) .talk()}'. Но я ВЫ рекомендую просто переопределить метод, так как вы уже используете inheretance –

+1

Как ненавистник шаблон посетителя, я спрашиваю, почему бы просто не полагаться на типичную иерархию интерфейса/класса, а не накладывать на него шаблон? – ChiefTwoPencils

ответ

5

Ключевым элементом Visitor pattern является double-dispatch: вам нужно вызвать метод посетителя от фактического подкласса:

abstract class Animal { 
    abstract void accept(Visitor v); 
} 

class Dog extends Animal { 
    @Override void accept(Visitor v) { v.talk(this); } 
} 

// ... etc for other animals. 

Затем вы проходите посетителя к животному, а не животное, чтобы посетитель :

for (Animal a : list) 
    a.accept(visitor); 

причина этого заключается в том, что компилятор выбирает перегрузку Visitor для вызова - это не s избранных во время выполнения. Весь компилятор знает, что a - это Animal, поэтому единственным способом, который он знает, является безопасным для вызова является перегрузка Visitor.visit(Animal).

+0

Ух, у меня болит голова. Скажите, пожалуйста, я не единственный, кто видит это как неправильное использование шаблон. Ваш пример хорош в отношении реализации, но разве это не замалчивается тем фактом, что эти отношения не имеют никакого отношения к посещению друг друга? В отличие от столкновений, например, где типы посещаемых и посетивших участников * важны * этот пример не должен взаимодействовать с посетителем, чтобы «поговорить». Решение проблемы OP кажется полиморфным, а не шаблоном. +1, чтобы показать, что это полезно в этом контексте. – ChiefTwoPencils

+1

Вы правы, что это излишний пример для этого простого примера. –

+0

... но простой пример также позволяет просто показать, где это неправильно, так что легче понять, как правильно его реализовать, где это * *. –

0

Шаблон посетителя - это в основном механизм разрешения поведения по многим причинам. Чтобы быть полезным/применимым к вашему примеру с животными, нам нужно добавить посетителя, как указал Энди Тернер.

Как насчет включения «Тренеры животных» в качестве посетителей? Различные тренеры-животные могли заставить разных животных говорить по-другому (или нет).

Итак, во-первых, сущность (интерфейс для животных). Мне нравится думать об этом как «Вещи» быть «действовало Актер»:

public interface IAnimal { 

    public String getAnimalName(); 

    // aka... public void accept(Visitor v) 
    public void allowAnimalTrainerToMakeMeSpeak(IAnimalTrainer animalTrainer); 
} 

Тогда давайте определим тип посетителя (тренер интерфейс животных).Мне нравится думать посетителю как «Актер», действующей на различных «вещей» (юридические лица):

public interface IAnimalTrainer { 

    public String getTrainerName(); 

    //aka... public void visit(Dog dog); 
    public void animalTrainerMakesAnimalSpeak(Dog dog); 
    public void animalTrainerMakesAnimalSpeak(Cat cat); 
    public void animalTrainerMakesAnimalSpeak(Poodle poodle); 
} 

Теперь позволяет создавать животных (реализовывать интерфейс IAnimal):

Cat:

public class Cat implements IAnimal { 

    private String animalName; 

    @Override 
    public String getAnimalName() { 
     return animalName; 
    } 
    public void setAnimalName(String animalName) { 
     this.animalName = animalName; 
    } 

    public Cat(String animalName) { 
     this.animalName = animalName; 
    } 
    public Cat() { 
     // Default constructor 
    } 

    @Override 
    public void allowAnimalTrainerToMakeMeSpeak(IAnimalTrainer animalTrainer) { 
     animalTrainer.animalTrainerMakesAnimalSpeak(this); 
    } 
} 

Собака:

public class Dog implements IAnimal { 

    private String animalName; 

    @Override 
    public String getAnimalName() { 
     return animalName; 
    } 
    public void setAnimalName(String animalName) { 
     this.animalName = animalName; 
    } 

    public Dog(String animalName) { 
     this.animalName = animalName; 
    } 
    public Dog() { 
     // Default constructor 
    } 

    @Override 
    public void allowAnimalTrainerToMakeMeSpeak(IAnimalTrainer animalTrainer) { 
     animalTrainer.animalTrainerMakesAnimalSpeak(this); 
    } 
} 

Пудель:

public class Poodle extends Dog { 

    public Poodle(String animalName) { 
     super(animalName); 
    } 
    public Poodle() { 
     super(); 
    } 

    @Override 
    public void allowAnimalTrainerToMakeMeSpeak(IAnimalTrainer animalTrainer) { 
     animalTrainer.animalTrainerMakesAnimalSpeak(this); 
    } 
} 

Теперь позволяет создавать дрессировщиков, Фил и Джек (реализовать интерфейс IAnimalTrainer):

дрессировщик Фил:

public class AnimalTrainerPhil implements IAnimalTrainer { 

    private String trainerName = "Phil"; 

    @Override 
    public String getTrainerName() { 
     return trainerName; 
    } 
    public void setTrainerName(String trainerName) { 
     this.trainerName = trainerName; 
    } 

    @Override 
    public void animalTrainerMakesAnimalSpeak(Dog dog) { 
     System.out.println(
       "Animal trainer " 
       + getTrainerName() 
       + " gets " 
       + dog.getAnimalName() 
       + " the dog to say: BARK!!"); 
    } 

    @Override 
    public void animalTrainerMakesAnimalSpeak(Cat cat) { 
     System.out.println(
       "Animal trainer " 
       + getTrainerName() 
       + " gets " 
       + cat.getAnimalName() 
       + " the cat to say: MEOW!!"); 
    } 

    @Override 
    public void animalTrainerMakesAnimalSpeak(Poodle poodle) { 
     animalTrainerMakesAnimalSpeak((Dog)poodle); 
    } 
} 

дрессировщик Джек:

public class AnimalTrainerJack implements IAnimalTrainer { 

    private String trainerName = "Jack"; 

    @Override 
    public String getTrainerName() { 
     return trainerName; 
    } 
    public void setTrainerName(String trainerName) { 
     this.trainerName = trainerName; 
    } 

    @Override 
    public void animalTrainerMakesAnimalSpeak(Dog dog) { 
     System.out.println(
       "Animal trainer " 
       + getTrainerName() 
       + " gets " 
       + dog.getAnimalName() 
       + " the dog to say: Bark bark."); 
    } 

    @Override 
    public void animalTrainerMakesAnimalSpeak(Cat cat) { 
     System.out.println(
       "Animal trainer " 
       + getTrainerName() 
       + " gets " 
       + cat.getAnimalName() 
       + " the cat to say: Meoooow."); 
    } 

    @Override 
    public void animalTrainerMakesAnimalSpeak(Poodle poodle) { 
     System.out.println(
       "Animal trainer " 
       + getTrainerName() 
       + " gets " 
       + poodle.getAnimalName() 
       + " the poodle to say: Yip! Yip!"); 
    } 
} 

Теперь давайте тянуть все это вместе и заведите всех тренеров по животным (Фил и Джек), чтобы заставить всех животных (Кошка, Собака, Пудель) поговорить через класс менеджера:

public class ManagerOfAnimalTrainersAndAnimals { 

    public static void main(String[] args) { 

     ArrayList<IAnimal> allAnimals = new ArrayList<>(); 
     allAnimals.add(new Dog("Henry")); 
     allAnimals.add(new Cat("Priscilla")); 
     allAnimals.add(new Poodle("Spike")); 

     ArrayList<IAnimalTrainer> allAnimalTrainers = new ArrayList<>(); 
     allAnimalTrainers.add(new AnimalTrainerPhil()); 
     allAnimalTrainers.add(new AnimalTrainerJack()); 

     // Allow all animal trainers to get each animal to speak 
     for (IAnimalTrainer animalTrainer : allAnimalTrainers) { 
      for (IAnimal animal : allAnimals) { 
       animal.allowAnimalTrainerToMakeMeSpeak(animalTrainer); 
      } 
     } 
    } 
} 

Вот результат:

Animal trainer Phil gets Henry the dog to say: BARK!! 
Animal trainer Phil gets Priscilla the cat to say: MEOW!! 
Animal trainer Phil gets Spike the dog to say: BARK!! 
Animal trainer Jack gets Henry the dog to say: Bark bark. 
Animal trainer Jack gets Priscilla the cat to say: Meoooow. 
Animal trainer Jack gets Spike the poodle to say: Yip! Yip! 

Это делает его относительно легко добавлять новые тренер, которые имеют доступ к конкретным вещам животного типа (например, если вы добавили коготь информации для кошек и лапы Информации для собак , ...), поскольку они действуют на разных животных. Я думаю, что проблема с традиционным примером «посетителя» - слишком туманная и недостаточно конкретная. Пример такси не сделал это для меня. Надеюсь, этот пример поможет.