Come progettare una soluzione che ha bisogno di dinamismo usando l'ereditarietà [chiusa]

-2

Ho sotto codice / classi / interfacce:

  1. Un'interfaccia:

     public interface Animal<A extends Animal<A>>{
     void fight(A otherAnimal);
     }
    

2. Classe di tigre che sta implementando Animal

    public class Tiger implements Animal{
    @Override
    public void fight(Tiger otherAnimal) {
        System.out.println("Tiger Fights");
    } 
} 

3. Ho una classe Lion che implementa anche Animal

  Public class Lion implements Animal{
 @Override
 public void fight(Lion otherAnimal) {
    System.out.println("Lion Fights");
}
  1. Ho una classe di test che chiameremo fight:

    public class TestClass{
    public static void main(String[] args){
       Animal tiger = new Tiger();
       Animal lion = new Lion();
       tiger.fight(lion); 
       } 
    }
    

Ora quando eseguo la classe di test, l'ovvio risultato è "Tiger Fights".

Come dovrei ridisegnare in modo che quando chiamo lotta alla tigre con qualcosa di diverso dalla tigre, dovrei chiamare le loro rispettive classi.

Quindi nell'esempio sopra dovrebbe invocare il combattimento di Leone anche se il combattimento è chiamato su oggetto di tigre.

Ho bisogno dell'output come "combattimento di leoni" anche se sto invocando un oggetto di tigre. Significa in realtà che voglio che lo sviluppatore abbia la libertà di passare qualsiasi tipo di oggetto a qualsiasi classe. La mia aspettativa è basata sul tipo di oggetto che deve essere chiamato il rispettivo metodo sovrascritto.

Ho bisogno di questo dinamismo perché possono esserci N numero di diversi animali (gatto, cane, lupo ecc.) che potrebbero essere aggiunti nel prossimo futuro.

È possibile progettare in modo tale che l'interfaccia di My Animal decida quale classe chiamare a seconda del tipo di oggetto passato?

    
posta TonyAdityaStark 27.02.2015 - 12:09
fonte

3 risposte

4

Prima di tutto, stai configurando l'interfaccia Animal con i generici per fare in modo che l'istanza Animal passata a combattere sia uguale a quella che implementa Animal . In altre parole, Lion può combattere solo un Lion . Un Tiger può combattere solo un Tiger . Dai suoi suoni, vuoi essere in grado di combattere qualsiasi animale. Se non è necessario utilizzare A per qualsiasi altra ragione nell'interfaccia, abbandonarla. Non è utile.

public interface Animal {
  void fight(Animal otherAnimal);
}

In secondo luogo, ora che stai ricevendo un'istanza Animal , devi sapere se si tratta di un'istanza della stessa classe che implementa Animal .

Per fare questo, potresti semplicemente scrivere:

public class Tiger implements Animal{
    @Override
    public void fight(Animal otherAnimal) {
        if(otherAnimal.getClass().equals(this.getClass()) {
            System.out.println("Two tigers fight");
        } else {
            System.out.println("Tiger fights a " + otherAnimal.getClass().getName());
        }
    } 
} 

Funziona bene fino a quando non hai bisogno di sapere che tipo otherAnimal è. In altre parole, se inizi a costruire se .. else elenca per coprire ogni possibilità, stai sbagliando. La tigre dovrebbe sapere come combatte gli altri animali, e tutto ciò che potrebbe cambiare il risultato dovrebbe essere qualcosa che hanno tutte le istanze di Animal .

Ad esempio, se vuoi attaccare lo stile "Pokemon" con ogni animale con la propria barra della salute, non devi lanciarlo su Lion per ottenere i punti salute di Lion . Devi solo creare un metodo "getHealthPoints" in Animal .

Se utilizzi Java 8, puoi persino utilizzare i valori predefiniti nell'interfaccia e eseguire l'override solo se la sottoclasse deve comportarsi in modo diverso:

public interface Animal {
  default void fight(Animal otherAnimal) {
     if(otherAnimal.getClass().equals(this.getClass()) {
         System.out.println("Two " + this.getClass().getName() + "s fight");
     } else {
         System.out.println(this.getClass().getName() + " fights a " + otherAnimal.getClass().getName());
     }
  }
}

Per questo particolare esempio, questo dovrebbe essere sufficiente, anche se stai cercando un esempio di pattern Visitor, wikipedia l'articolo sul pattern Visitor fornisce una spiegazione decente. Si noti che la struttura è più gerarchica e serve principalmente per eseguire un'operazione su ciascun oggetto in essa contenuto. Questo sarebbe un po 'eccessivo per questa particolare istanza, quindi non modifico l'esempio sopra.

Spero che ti aiuti.

    
risposta data 27.02.2015 - 15:43
fonte
1

Se stai cercando una classe per il codice che chiama un vincitore di 2 animali in lotta, allora la soluzione è una terza classe che lo fa. Immagina di dover scrivere un metodo compare che confronta oggetti di 10 classi molto diverse. Ovviamente non si può avere il codice facendo il confronto diffuso in tutte quelle 10 classi. È molto più pulito scrivere una classe Comparator .

Se stai solo chiedendo come scrivere codice x operation y dove puoi codificare operation per ogni x e y tipo coppia separatamente, allora stai cercando il doppio invio, solitamente implementato da Visitor modello .

Modifica: usi in modo improprio anche i tipi generici. Stai aggiungendo il tipo generico di A al tipo di intero animale solo per il tipo di metodo di combattimento di destinazione. Devi solo aggiungere un tipo generico al solo metodo.

La corretta dichiarazione di animale sarebbe:

public interface Animal {
    <A extends Animal> void fight(A otherAnimal);
}
    
risposta data 27.02.2015 - 16:10
fonte
1

Da quello che hai scritto, è ancora un po 'oscuro quello che cerchi, ma immagino che quando una "Tigre" combatte un "Leone", non vuoi aver stampato "Combattimenti di tigri" o "Combattimenti dei leoni" , ma qualcosa come "Tiger combatte il leone". E questo non è niente per cui hai bisogno di una doppia spedizione o del modello di visitatore.

Separa semplicemente la "parte di uscita" dalla parte "chi combatte":

public interface Animal {
   String Race();
   int Strength();
}

public class Tiger implements Animal{
  @Override
  public String Race() {
      return "Tiger";
  } 
  @Override
  public int Strength() {
      return 10;
  } 

(sì, la parte Race potrebbe essere implementata più elegantemente usando this.getClass().getName() , ma questo è a scopo dimostrativo). Ho aggiunto un metodo Strength aggiuntivo per rendere più chiaro come funziona e come implementare la logica effettiva con esso. Posiziona il metodo di combattimento (la parte di output) da qualche altra parte e implementalo in termini di "Animali":

public class Arena {
public void fight(Animal a1, Animal a2) {
    System.out.println(a1.Race() + " fights " + a2.Race());
    if(a1.Strength()>a2.Strength)
    {
         System.out.println(a1.Race() + " wins");
    }
    else if(a1.Strength()<a2.Strength)
    {
         System.out.println(a2.Race() + " wins");
    }
    else
    {
         System.out.println("Tie.");
    }
} 

Ora puoi facilmente aggiungere altri animali diversi, senza toccare nessuna delle classi di animali esistenti o toccare il metodo fight .

    
risposta data 28.02.2015 - 09:47
fonte

Leggi altre domande sui tag