Jeśli nie przeszkadza oddanie kod wykrywania kolizji w obiektach siebie, można wyeliminować jeden bok kontroli robiąc coś takiego:
public abstract class Shape {
public abstract boolean collidesWith (Shape s);
}
public class Ball extends Shape {
@Override public boolean collidesWith (Shape s) {
if (s instanceof Block)
return Collision.blockBall((Block)s, this);
else if (s instanceof Ball)
return Collision.ballBall(this, (Ball)s);
else
return false;
}
}
public class Block extends Shape {
@Override public boolean collidesWith (Shape s) {
if (s instanceof Block)
return Collision.blockBlock(this, (Block)s);
else if (s instanceof Ball)
return Collision.blockBall(this, (Ball)s);
else
return false;
}
}
public class Collision {
public static boolean blockBlock (Block a, Block b) { ... }
public static boolean blockBall (Block a, Ball b) { ... }
public static boolean ballBall (Ball a, Ball b) { ... }
}
To również daje swobodę implementacji algorytmów do kolizji pewne kombinacje Kształtów w Kształcie, jeśli to konieczne - możesz nawet pozbyć się Kolizji i po prostu zrobić np Block.collideWithBall, Block.collideWithBlock i Ball.collideWithBlock, nazywając tych, w stosownych przypadkach, np .:
public abstract class Shape {
public abstract boolean collidesWith (Shape s);
}
public class Ball extends Shape {
@Override public boolean collidesWith (Shape s) {
if (s instanceof Block)
return collidesWithBlock((Block)s);
else if (s instanceof Ball)
return collidesWithBall((Ball)s);
else
return false;
}
public boolean collidesWithBall (Ball b) {
...
}
public boolean collidesWithBlock (Block b) {
...
}
}
public class Block extends Shape {
@Override public boolean collidesWith (Shape s) {
if (s instanceof Block)
return collidesWithBlock((Block)s);
else if (s instanceof Ball)
return ((Ball)s).collidesWithBlock(this);
else
return false;
}
public boolean collidesWithBlock (Block b) {
...
}
}
Osobiście trochę jak ten ostatni lepsze, ponieważ utrzymuje kolizji kodu zawartego w odpowiednich klas. Zauważ, że Block.collidesWithBall jest niepotrzebny, ponieważ można użyć Ball.collidesWithBlock.
Nadal musisz aktualizować powyższy kod za każdym razem, gdy dodajesz nowy kształt. Jeżeli wydajność nie jest problemem, można zrobić coś takiego jak:
public abstract class CollisionAlgorithm {
public abstract boolean canCollide (Class<? extends Shape> a, Class<? extends Shape> b);
public abstract boolean collide (Shape a, Shape b);
}
public class Collider {
private static final List<CollisionAlgorithm> algorithms;
public static void registerAlgorithm (CollisionAlgorithm a) {
algorithms.append(a);
}
public static CollisionAlgorithm findAlgorithm (Class<? extends Shape> a, Class<? extends Shape> b) {
for (CollisionAlgorithm algo : algorithms)
if (algo.canCollide(a, b))
return algo;
return null;
}
public static boolean collide (Shape a, Shape b) {
if (a == null || b == null)
return false;
CollisionAlgorithm algo = findAlgorithm(a.getClass(), b.getClass());
if (algo != null)
return algo.collide(a, b);
algo = findAlgorithm(b.getClass(), a.getClass()); // try swapped order
if (algo != null)
return algo.collide(b, a);
return false;
}
}
// usage: first register algorithms
Collider.registerAlgorithm(new BallBallAlgorithm());
Collider.registerAlgorithm(new BallBlockAlgorithm());
Collider.registerAlgorithm(new BlockBlockAlgorithm());
// then
Shape myShape1 = ...;
Shape myShape2 = ...;
boolean collide = Collider.collide(myShape1, myShape2);
Uwaga: Wpisałem to tutaj szybko, a to oznaczało, do zilustrowania koncepcji - wiele możliwości poprawy. Na przykład można użyć mapy z dwiema klasami Shape jako kluczem do poprawy wydajności, lub CollisionAlgorithm można podać ogólne parametry, aby wyeliminować potrzebę rzutowania kształtów. Należy jednak pamiętać, że to podejście wymaga sprawdzenia w kontenerze algorytmu za każdym razem, gdy trzeba wykonać test kolizji.
Nadal pracuję nad twoim głównym pytaniem, ale z jednej strony możesz rozważyć użycie 'if (s1 instanceof Ball)' zamiast 'getClass()'. Jest to nieco bardziej standardowy i wydajny sposób pisania. – snickers10m
Czy to są twoje własne klasy? ('Kształt',' Piłka', 'Blokuj') Jeśli nie, prawdopodobnie możesz znaleźć gotowe wykrywanie kolizji w dowolnym interfejsie API, z którego korzystasz. – snickers10m
Nie wiesz, jak ważna jest wydajność, ale możesz zarejestrować listę algorytmów kolizji (lub użyć mapy z przypisanymi klasami Shape), a następnie znaleźć odpowiedni algorytm na liście, biorąc pod uwagę klasy kształtu. Zobacz nowy przykład dodany do mojej odpowiedzi poniżej. –