2015-03-20 8 views
17

Qual è il modo migliore di manipolare l'ordine mentre le cose vengono eseguite in base ad alcune condizioni (oltre a scriverle di nuovo con il diverso ordine)?Variare l'ordine del metodo di chiamata per diverse istanze di una classe

Diciamo che c'è una classe Persona e ogni oggetto di Persona rappresenta un diverso umano.

class Person{ 
    int eatingPriority = 3; 
    int sleepingPriority = 2; 
    int recreationPriority = 1; 

    void eat() {/*eats*/} 
    void sleep() {/*sleeps*/} 
    void watchTv() {/*watches tv*/} 

    void satisfyNeeds() { 
     //HOW TO DO THIS 
    } 
} 

Come posso fare i metodi satisfyNeeds() chiamano gli altri tre metodi in base alla loro priorità?

Nota: desidero chiarire che le priorità possono cambiare da Persona a Persona.

+2

Il metodo più semplice è quello di creare una classe astratta con 2 metodi: getPriority() 'e' 'run()'. Quindi creare un insance per ogni attività. Infine, puoi mettere le istanze di quelle attività in una lista e ordinarle per priorità. –

+2

@ArnaudDenoyelle o utilizzare un 'PriorityQueue' – SpaceTrucker

+0

Mi è stato chiesto lo scopo del programma. Stiamo cercando di trovare il modo più pulito di manipolare il flusso del programma. In questo esempio, supponiamo che stiamo creando un'IA per un gioco che ha simulato le persone in esso. Se sei affamato e stanco dormi o mangi prima? Se la tua priorità per dormire è più alta, prima dormi, altrimenti mangi prima di dormire. Inoltre è possibile modificare le priorità al volo. Possiamo misurare il sonno e le esigenze alimentari e impostare una priorità basata su questo. Si noti che il codice nella domanda è una versione semplificata per dimostrare il problema. – WVrock

risposta

7

È possibile utilizzare questo codice

import java.util.Arrays; // must be imported 
int[] priorities = {sleepPriority, eatPriority, recreationPriority}; 
Arrays.sort(priorities); 
for (int i=priorities.length-1; 0<=i; i--) { 
    int priority = priorities[i]; 
    if (priority == sleepingPriority) { sleep(); } 
    if (priority == eatingPriority) { eat(); } 
    if (priority == recreationPriority) { watchTv(); } 
} 

Fondamentalmente, si mette le priorità in un array, ordina l'array e gestisce un ciclo for su di esso per eseguire le funzioni.

+0

richiede -1 al valore iniziale i. Provoca outOfBoundsException. 'int i = priority.length -1' – WVrock

+0

Oh .. Lo controllerò ora. –

+0

OK, l'ho risolto. Dovrebbe funzionare ora. Ho aggiunto il-1 come sottolineato da @WVrock. –

4

Si dovrebbe introdurre una Carta in Person di classe, in cui la priorità i metodi, per esempio:

class Person { 

... 
private Map<Integer, Method> methodsPriority = new HashMap<>(); 
... 
public Person setEatingPriority(int priority) { 

    methodsPriority.put(priority, /* put 'eat' method reference here*/); 
    return this; 
} 

public Person setSleepingPriority(int priority) { 

    methodsPriority.put(priority, /* put 'sleep' method reference here*/); 
    return this; 
} 

public Person setWatchingTVPriority(int priority) { 

    methodsPriority.put(priority, /* put 'watch TV' method reference here*/); 
    return this; 
} 

public void satisfyNeeds() { 

    Collection<Integer> keys = methodsPriority.keySet(); 
    Collections.sort(keys); 
    for(Integer key: keys) 
    methodsPriority.get(key).invoke(this); 
} 



... 
} 

e può essere utilizzato in prossimo maniera:

Person Anna = new Person() 
.setEatingPriority(1) 
.setSleepingPriority(2) 
.setWatchingTVPriority(3); 

Person Bob = new Person() 
.setEatingPriority(3) 
.setSleepingPriority(2) 
.setWatchingTVPriority(1); 

Anna.satisfyNeeds(); 
Bob.satisfyNeeds(); 
13

Si può fare questo con 1 classe e 1 interfaccia.

public class Person { 
    int eatingPriority = 3; 
    int sleepingPriority = 2; 
    int recreationPriority = 1; 

    PriorityQueue<Action> actions; 

    void eat() { } 

    void sleep() { } 

    void watchTv() { } 

    public Person() { 
     actions = new PriorityQueue<Action>(new Comparator<Action>() { 
      @Override 
      public int compare(Action o1, Action o2) { 
       return o2.getPriority() - o1.getPriority(); 
      } 
     }); 

     actions.add(new Action() { 
      @Override 
      public int getPriority() { 
       return eatingPriority; 
      } 
      @Override 
      public void execute() { 
       eat(); 
      } 
     }); 

     actions.add(new Action() { 
      @Override 
      public int getPriority() { 
       return sleepingPriority; 
      } 
      @Override 
      public void execute() { 
       sleep(); 
      } 
     }); 

     actions.add(new Action() { 
      @Override 
      public int getPriority() { 
       return recreationPriority; 
      } 
      @Override 
      public void execute() { 
       watchTv(); 
      } 
     }); 
    } 

    public void satisfyNeeds() { 
     for (Action action : actions) { 
      action.execute(); 
     } 
    } 

    interface Action { 
     public int getPriority(); 
     public void execute(); 
    } 
} 
+0

È anche possibile mantenere la struttura della classe e chiamare i metodi eat(), sleep() e watchTv() dai metodi execute() appropriati. – Maksim

+1

Numero eccessivo di piastre ... – Mik378

+2

Grazie! Bella idea, è ragionevole usare PriorityQueue qui invece di ArrayList. – Maksim

10

Ecco un altro possibile implementazione:

abstract class Need { 
    abstract void satisfy(); 
} 

class Eat extends Need { 
    @Override 
    public void satisfy() { /* eat ...*/} 
} 

class Sleep extends Need { 
    @Override 
    public void satisfy() { /* sleep ...*/} 
} 

class DrinkBeer extends Need { 
    @Override 
    public void satisfy() { /* drink beer ...*/} 
} 

class Person{ 
    // TreeMap will sort the map in the key's natural order (a int here) 
    private Map<Integer, Need> needs = new TreeMap<>();  

Person() { 
    add(new Eat(), 3); 
    add(new Sleep(), 2); 
    add(new DrinkBeer(), 1); 
} 

void add(Need need, int priority) { 
    needs.put(Integer.valueOf(priority), need); 
} 

void satisfyNeeds() { 
    for(Need need : needs.values()) 
     need.satisfy(); 
    } 
} 
+0

'needs.put (Integer.valueOf (priority), need)' può essere scritto semplicemente come 'needs.put (priority, need)'. – VGR

5

Trovare il giusto ordine dei tre elementi può essere fatto semplicemente in questo modo:

void satisfyNeeds() { 
    boolean eatFirst = eatingPriority>Math.max(sleepingPriority,recreationPriority); 
    if(eatFirst) eat(); 
    if(sleepingPriority>recreationPriority) { 
     sleep(); 
     watchTv(); 
    } 
    else { 
     watchTv(); 
     sleep(); 
    } 
    if(!eatFirst) eat(); 
    } 

Naturalmente, non scala se aumenti il ​​numero di azioni. Per un numero più alto potresti guardare una delle altre risposte.

9

Questa soluzione richiede Java 8:

class Person { 

    void eat() {}; 
    void sleep() {}; 
    void watchTv() {}; 

    // Being in a List you can easily reorder the needs when you want to 
    List<Runnable> needs = Arrays.asList(this::eat, this::sleep); 

    // Alternatively, you can use a Map<Runnable, Integer> where the value is your 
    // priority and sort it (see http://stackoverflow.com/q/109383/1296402) 

    void satisfyNeeds() { 
     needs.forEach(r -> r.run()); 
    } 
} 
+0

Puoi elaborare? Dovrei scrivere 'needs = Arrays.asList (() -> eat(),() -> sleep());' con l'ordine desiderato ogni volta che voglio riordinarlo? – WVrock

+0

@WVrock È possibile definire l'ordine e successivamente riordinarlo ogni volta che cambia. Oppure usa una 'Mappa' se hai valori di priorità statici come numeri interi (vedi il link). Forse hai "preset", come l'elenco finale needsDuringDayTime = ....; lista finale needsDuringNightTime = ... 'e quindi li si imposta in' satisfNeeds() ':' Lista needs = isDayTime()? needsDuringDayTime: needsDuringNightTime'. – steffen

+0

@WVrock Oppure immagina di avere contatori o timestamp per gli ultimi pasti, i tempi di sonno o le sessioni TV e quindi le esigenze cambiano in modo dinamico. Ordinate la 'Lista' con un' Comparatore 'personalizzato come 'Collections.sort (needs, customComparator);' – steffen