Metodi di sovrascrittura con firma più rigida

0

Sto programmando in Java e ho il seguente problema:

Mi piacerebbe fare il rilevamento delle collisioni. Per questo, ho bisogno di diversi tipi di BoundingBox es. Per esempio, diciamo che ho entità che hanno una scatola di collisione circolare e entità che hanno un rettangolo di delimitazione. Quindi ho bisogno di due classi, BoundingBoxCircle e BoundingBoxRectangle per implementare il rilevamento delle collisioni. Inoltre, mi piacerebbe avere una classe astratta BoundingBox che ha il metodo astratto collidesWith(...) , che prende come argomento un qualche tipo di BoundingBox e restituisce se è attualmente in collisione con qualche altro BoundingBox .

Ora per il problema: non riesco a trovare un buon modo per strutturarlo a livello di programmazione. Essenzialmente, ho bisogno di implementare diversi rilevamenti di collisione tra rettangoli e cerchi (e potenzialmente altre forme). Mi piacerebbe anche essere in grado di chiamare questi metodi "bene". Quando ho un determinato riquadro di delimitazione, ad esempio BoundingBoxCircle , vorrei poter chiamare boundingBoxCircle.collidesWith(rectangle) e inviarlo dinamicamente al metodo che gestisce il rilevamento delle collisioni rettangolo-cerchio. Tuttavia, questo non funziona:

public abstract class BoundingBox {
    public abstract boolean collidesWith(BoundingBox b);
}

public class BoundingBoxCircle {
    @Override
    public boolean collidesWith(BoundingBoxRectangle b) {
         ...
    }
}

Perché la firma del metodo sovrascritto non è la stessa.

Quindi posso pensare a due opzioni:

  • Fornisci tutte le firme dei metodi nella superclasse
  • Sostituisci collidesWith in tutte le sottoclassi

Il primo ha il problema che dovrei fornire le firme circleCollidesWithRectangle , rectangleCollidesWithCircle (ecc.) nella classe BoundingBox , che è abbastanza brutta, perché essenzialmente ho "informazioni" in BoundingBox che dovrebbe davvero essere parte delle sue sottoclassi.

Il secondo ha il problema che avrei bisogno di sovrascrivere il metodo collidesWith(BoundingBox b) in ogni sottoclasse, il che significa che non so quale tipo b sia - quindi dovrei controllare tutti i casi in ogni sottoclasse; in sostanza:

class BoundingBoxRectangle extends BoundingBox {
    @Override
    public boolean collidesWith(BoundingBox b) {
         if (b typeof BoundingBoxRectangle) {
             ...
         } else if (b typeof BoundingBoxCirce) {
             ...
         } else {
              throw new IllegalStateException("unexpected type");
         }
    }
}

... che potrebbe portare a una quantità folle di dichiarazioni if se inizi a programmare più sottoclassi per BoundingBox . Ha anche lo svantaggio che potrei dimenticare di aggiungere un'istruzione if in futuro, che è potenzialmente difficile da trovare, perché lancio solo un'eccezione se la collisione viene controllata per due entità specifiche.

Mi piacerebbe davvero implementarlo in un modo che mi permette di vedere i metodi mancanti in fase di compilazione e non richiede la ripetizione del codice.

    
posta PawkyPenguin 09.11.2017 - 12:00
fonte

3 risposte

5

Puoi eseguire il doppio invio in una singola lingua di spedizione con il modello di visitatore . Questo ha fondamentalmente un'interfaccia come:

bool collidesWith(BoundingBox visitor) {
    return visitor.collidesWithVisit(this);
}

bool collidesWithVisit(BoundingBoxRectangle other) {
    return false;
}

bool collidesWithVisit(BoundingBoxCircle other) {
    return false;
}

L'interfaccia principale è attraverso la prima funzione, quindi richiamerà l'implementazione appropriata di seguito.

    
risposta data 09.11.2017 - 13:58
fonte
2

Il solito consiglio, se vuoi modellare questa funzionalità in Java, è trasformare il tuo modello fino a quando non può essere implementato con una sola spedizione. Ad esempio, potresti avere una proprietà o un metodo specifico per la forma Extent che non calcola le collisioni, ma solo dove raggiunge esattamente una forma, e quindi una percentuale agnostica CollisionManager che controlla effettivamente le collisioni, in base alle informazioni che riceve da chiamate getExtent() inviate dinamicamente su entrambi gli oggetti.

Questo può significare che devi cambiare un po 'il tuo modello, ma l'alternativa sarebbe passare a un linguaggio che permetta la pubblicazione multipla in modo nativo - e la maggior parte delle persone non è disposta a iniziare a usare Common Lisp solo per quello.

    
risposta data 09.11.2017 - 12:15
fonte
0

Come vedo, sarebbe scorretto avere un metodo astratto collidesWith in BoundingBox poiché anche se sai come funziona la tua implementazione, non è abbastanza informazioni per essere in grado di determinare se c'è una collisione.

Dovresti creare un CollisionManager contenente un elenco di classi CollisionHandler in questo modo:

public interface CollisionHandler {
    public boolean canHandle(Class<? extends BoundingBox> class1, Class<? extends BoundingBox> class2);
    public boolean collidesWith(BoundingBox instance1, BoundingBox instance2);
}

public class CollisionManager {
    List<CollisionHandler> handlers= new ArrayList<CollisionHandler>();

    public void add(CollisionHandler handler) {
        handlers.add(handler);
    }

    public boolean collidesWith(BoundingBox instance1, BoundingBox instance2) {
        for(CollisionHandler handler : handlers) {
            if(handler.canHandle(instance1.getClass(), instance2.getClass()) {
                return handler.collidesWith(instance1, instance2);
            }
        }
        // If we got here, we don't know how to handle this case.
        throw new CollisionNotHandledException();
    }
}

Da qui, dovresti scrivere un CollisionHandler per ogni caso. Forse potresti scrivere un CollisionHandler che si limita a verificare che le due classi siano uguali e, in tal caso, chiede all'istanza BoundingBox di eseguire il controllo autonomamente:

public class SelfCollisionHandler {
    public boolean canHandle(Class<? extends BoundingBox> class1, Class<? extends BoundingBox> class2) {
        return class1.equals(class2);
    }

    public boolean collidesWith(BoundingBox instance1, BoundingBox instance2) {
        return instance1.collidesWith(instance2);
    }
}

Quindi ogni implementazione di BoundingBox sarebbe responsabile dell'implementazione della collisione con la sua istanza. In altri casi, dovresti gestire una percentuale specifica di% co_de dedicata a quel caso particolare, come la collisione tra CollisionHandler e BoundingBoxRectangle .

    
risposta data 09.11.2017 - 14:43
fonte

Leggi altre domande sui tag