deps: Refactor dependencies (#3224)

* remove spring dep
move junit, logging, mockito under dep mgmt

* upgrade anti-corruption-layer deps

* async method invocation

* balking, bloc

* bridge to bytecode

* caching

* callback - cqrs

* component - health check

* hexagonal - metadata mapping

* rest of the patterns

* remove checkstyle, take spotless into use
This commit is contained in:
Ilkka Seppälä
2025-03-29 19:34:27 +02:00
committed by GitHub
parent 371439aeaa
commit 0ca162a55c
1863 changed files with 14403 additions and 17632 deletions
@@ -30,76 +30,77 @@ import java.util.concurrent.ConcurrentHashMap;
import lombok.extern.slf4j.Slf4j;
/**
* <p>The idea behind the <b>Spatial Partition</b> design pattern is to enable efficient location
* of objects by storing them in a data structure that is organised by their positions. This is
* The idea behind the <b>Spatial Partition</b> design pattern is to enable efficient location of
* objects by storing them in a data structure that is organised by their positions. This is
* especially useful in the gaming world, where one may need to look up all the objects within a
* certain boundary, or near a certain other object, repeatedly. The data structure can be used to
* store moving and static objects, though in order to keep track of the moving objects, their
* positions will have to be reset each time they move. This would mean having to create a new
* instance of the data structure each frame, which would use up additional memory, and so this
* pattern should only be used if one does not mind trading memory for speed and the number of
* objects to keep track of is large to justify the use of the extra space.</p>
* objects to keep track of is large to justify the use of the extra space.
*
* <p>In our example, we use <b>{@link QuadTree} data structure</b> which divides into 4 (quad)
* sub-sections when the number of objects added to it exceeds a certain number (int field
* capacity). There is also a
* <b>{@link Rect}</b> class to define the boundary of the quadtree. We use an abstract class
* <b>{@link Point}</b>
* with x and y coordinate fields and also an id field so that it can easily be put and looked up in
* the hashmap. This class has abstract methods to define how the object moves (move()), when to
* check for collision with any object (touches(obj)) and how to handle collision
* (handleCollision(obj)), and will be extended by any object whose position has to be kept track of
* in the quadtree. The <b>{@link SpatialPartitionGeneric}</b> abstract class has 2 fields - a
* hashmap containing all objects (we use hashmap for faster lookups, insertion and deletion)
* and a quadtree, and contains an abstract method which defines how to handle interactions between
* objects using the quadtree.</p>
* capacity). There is also a <b>{@link Rect}</b> class to define the boundary of the quadtree. We
* use an abstract class <b>{@link Point}</b> with x and y coordinate fields and also an id field so
* that it can easily be put and looked up in the hashmap. This class has abstract methods to define
* how the object moves (move()), when to check for collision with any object (touches(obj)) and how
* to handle collision (handleCollision(obj)), and will be extended by any object whose position has
* to be kept track of in the quadtree. The <b>{@link SpatialPartitionGeneric}</b> abstract class
* has 2 fields - a hashmap containing all objects (we use hashmap for faster lookups, insertion and
* deletion) and a quadtree, and contains an abstract method which defines how to handle
* interactions between objects using the quadtree.
*
* <p>Using the quadtree data structure will reduce the time complexity of finding the objects
* within a certain range from <b>O(n^2) to O(nlogn)</b>, increasing the speed of computations
* immensely in case of large number of objects, which will have a positive effect on the rendering
* speed of the game.</p>
* speed of the game.
*/
@Slf4j
public class App {
static void noSpatialPartition(int numOfMovements, Map<Integer, Bubble> bubbles) {
//all bubbles have to be checked for collision for all bubbles
// all bubbles have to be checked for collision for all bubbles
var bubblesToCheck = bubbles.values();
//will run numOfMovement times or till all bubbles have popped
// will run numOfMovement times or till all bubbles have popped
while (numOfMovements > 0 && !bubbles.isEmpty()) {
bubbles.forEach((i, bubble) -> {
// bubble moves, new position gets updated
// and collisions are checked with all bubbles in bubblesToCheck
bubble.move();
bubbles.replace(i, bubble);
bubble.handleCollision(bubblesToCheck, bubbles);
});
bubbles.forEach(
(i, bubble) -> {
// bubble moves, new position gets updated
// and collisions are checked with all bubbles in bubblesToCheck
bubble.move();
bubbles.replace(i, bubble);
bubble.handleCollision(bubblesToCheck, bubbles);
});
numOfMovements--;
}
//bubbles not popped
// bubbles not popped
bubbles.keySet().forEach(key -> LOGGER.info("Bubble {} not popped", key));
}
static void withSpatialPartition(
int height, int width, int numOfMovements, Map<Integer, Bubble> bubbles) {
//creating quadtree
// creating quadtree
var rect = new Rect(width / 2D, height / 2D, width, height);
var quadTree = new QuadTree(rect, 4);
//will run numOfMovement times or till all bubbles have popped
// will run numOfMovement times or till all bubbles have popped
while (numOfMovements > 0 && !bubbles.isEmpty()) {
//quadtree updated each time
// quadtree updated each time
bubbles.values().forEach(quadTree::insert);
bubbles.forEach((i, bubble) -> {
//bubble moves, new position gets updated, quadtree used to reduce computations
bubble.move();
bubbles.replace(i, bubble);
var sp = new SpatialPartitionBubbles(bubbles, quadTree);
sp.handleCollisionsUsingQt(bubble);
});
bubbles.forEach(
(i, bubble) -> {
// bubble moves, new position gets updated, quadtree used to reduce computations
bubble.move();
bubbles.replace(i, bubble);
var sp = new SpatialPartitionBubbles(bubbles, quadTree);
sp.handleCollisionsUsingQt(bubble);
});
numOfMovements--;
}
//bubbles not popped
// bubbles not popped
bubbles.keySet().forEach(key -> LOGGER.info("Bubble {} not popped", key));
}
@@ -108,7 +109,6 @@ public class App {
*
* @param args command line args
*/
public static void main(String[] args) {
var bubbles1 = new ConcurrentHashMap<Integer, Bubble>();
var bubbles2 = new ConcurrentHashMap<Integer, Bubble>();
@@ -117,8 +117,8 @@ public class App {
var b = new Bubble(rand.nextInt(300), rand.nextInt(300), i, rand.nextInt(2) + 1);
bubbles1.put(i, b);
bubbles2.put(i, b);
LOGGER.info("Bubble {} with radius {} added at ({},{})",
i, b.radius, b.coordinateX, b.coordinateY);
LOGGER.info(
"Bubble {} with radius {} added at ({},{})", i, b.radius, b.coordinateX, b.coordinateY);
}
var start1 = System.currentTimeMillis();
@@ -33,7 +33,6 @@ import lombok.extern.slf4j.Slf4j;
* Bubble class extends Point. In this example, we create several bubbles in the field, let them
* move and keep track of which ones have popped and which ones remain.
*/
@Slf4j
public class Bubble extends Point<Bubble> {
private static final SecureRandom RANDOM = new SecureRandom();
@@ -46,15 +45,15 @@ public class Bubble extends Point<Bubble> {
}
void move() {
//moves by 1 unit in either direction
// moves by 1 unit in either direction
this.coordinateX += RANDOM.nextInt(3) - 1;
this.coordinateY += RANDOM.nextInt(3) - 1;
}
boolean touches(Bubble b) {
//distance between them is greater than sum of radii (both sides of equation squared)
// distance between them is greater than sum of radii (both sides of equation squared)
return (this.coordinateX - b.coordinateX) * (this.coordinateX - b.coordinateX)
+ (this.coordinateY - b.coordinateY) * (this.coordinateY - b.coordinateY)
+ (this.coordinateY - b.coordinateY) * (this.coordinateY - b.coordinateY)
<= (this.radius + b.radius) * (this.radius + b.radius);
}
@@ -64,12 +63,12 @@ public class Bubble extends Point<Bubble> {
}
void handleCollision(Collection<? extends Point> toCheck, Map<Integer, Bubble> allBubbles) {
var toBePopped = false; //if any other bubble collides with it, made true
var toBePopped = false; // if any other bubble collides with it, made true
for (var point : toCheck) {
var otherId = point.id;
if (allBubbles.get(otherId) != null //the bubble hasn't been popped yet
&& this.id != otherId //the two bubbles are not the same
&& this.touches(allBubbles.get(otherId))) { //the bubbles touch
if (allBubbles.get(otherId) != null // the bubble hasn't been popped yet
&& this.id != otherId // the two bubbles are not the same
&& this.touches(allBubbles.get(otherId))) { // the bubbles touch
allBubbles.get(otherId).pop(allBubbles);
toBePopped = true;
}
@@ -33,7 +33,6 @@ import java.util.Map;
*
* @param <T> T will be type subclass
*/
public abstract class Point<T> {
public int coordinateX;
@@ -46,9 +45,7 @@ public abstract class Point<T> {
this.id = id;
}
/**
* defines how the object moves.
*/
/** defines how the object moves. */
abstract void move();
/**
@@ -63,7 +60,7 @@ public abstract class Point<T> {
* handling interactions/collisions with other objects.
*
* @param toCheck contains the objects which need to be checked
* @param all contains hashtable of all points on field at this time
* @param all contains hashtable of all points on field at this time
*/
abstract void handleCollision(Collection<? extends Point> toCheck, Map<Integer, T> all);
}
@@ -33,7 +33,6 @@ import java.util.Map;
* insert(Point) and query(range) methods to insert a new object and find the objects within a
* certain (rectangular) range respectively.
*/
public class QuadTree {
Rect boundary;
int capacity;
@@ -93,13 +92,9 @@ public class QuadTree {
}
Collection<Point> query(Rect r, Collection<Point> relevantPoints) {
//could also be a circle instead of a rectangle
// could also be a circle instead of a rectangle
if (this.boundary.intersects(r)) {
this.points
.values()
.stream()
.filter(r::contains)
.forEach(relevantPoints::add);
this.points.values().stream().filter(r::contains).forEach(relevantPoints::add);
if (this.divided) {
this.northwest.query(r, relevantPoints);
this.northeast.query(r, relevantPoints);
@@ -28,14 +28,13 @@ package com.iluwatar.spatialpartition;
* The Rect class helps in defining the boundary of the quadtree and is also used to define the
* range within which objects need to be found in our example.
*/
public class Rect {
double coordinateX;
double coordinateY;
double width;
double height;
//(x,y) - centre of rectangle
// (x,y) - centre of rectangle
Rect(double x, double y, double width, double height) {
this.coordinateX = x;
@@ -58,4 +57,3 @@ public class Rect {
|| this.coordinateY - this.height / 2 >= other.coordinateY + other.height / 2);
}
}
@@ -31,7 +31,6 @@ import java.util.Map;
* This class extends the generic SpatialPartition abstract class and is used in our example to keep
* track of all the bubbles that collide, pop and stay un-popped.
*/
public class SpatialPartitionBubbles extends SpatialPartitionGeneric<Bubble> {
private final Map<Integer, Bubble> bubbles;
@@ -48,7 +47,7 @@ public class SpatialPartitionBubbles extends SpatialPartitionGeneric<Bubble> {
var rect = new Rect(b.coordinateX, b.coordinateY, 2D * b.radius, 2D * b.radius);
var quadTreeQueryResult = new ArrayList<Point>();
this.bubblesQuadTree.query(rect, quadTreeQueryResult);
//handling these collisions
// handling these collisions
b.handleCollision(quadTreeQueryResult, this.bubbles);
}
}
@@ -32,7 +32,6 @@ import java.util.Map;
*
* @param <T> T will be type of object (that extends Point)
*/
public abstract class SpatialPartitionGeneric<T> {
Map<Integer, T> playerPositions;
@@ -33,10 +33,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import org.junit.jupiter.api.Test;
/**
* Testing methods in Bubble class.
*/
/** Testing methods in Bubble class. */
class BubbleTest {
@Test
@@ -45,7 +42,7 @@ class BubbleTest {
var initialX = b.coordinateX;
var initialY = b.coordinateY;
b.move();
//change in x and y < |2|
// change in x and y < |2|
assertTrue(b.coordinateX - initialX < 2 && b.coordinateX - initialX > -2);
assertTrue(b.coordinateY - initialY < 2 && b.coordinateY - initialY > -2);
}
@@ -55,7 +52,7 @@ class BubbleTest {
var b1 = new Bubble(0, 0, 1, 2);
var b2 = new Bubble(1, 1, 2, 1);
var b3 = new Bubble(10, 10, 3, 1);
//b1 touches b2 but not b3
// b1 touches b2 but not b3
assertTrue(b1.touches(b2));
assertFalse(b1.touches(b3));
}
@@ -68,7 +65,7 @@ class BubbleTest {
bubbles.put(1, b1);
bubbles.put(2, b2);
b1.pop(bubbles);
//after popping, bubble no longer in hashMap containing all bubbles
// after popping, bubble no longer in hashMap containing all bubbles
assertNull(bubbles.get(1));
assertNotNull(bubbles.get(2));
}
@@ -86,7 +83,7 @@ class BubbleTest {
bubblesToCheck.add(b2);
bubblesToCheck.add(b3);
b1.handleCollision(bubblesToCheck, bubbles);
//b1 touches b2 and not b3, so b1, b2 will be popped
// b1 touches b2 and not b3, so b1, b2 will be popped
assertNull(bubbles.get(1));
assertNull(bubbles.get(2));
assertNotNull(bubbles.get(3));
@@ -33,10 +33,7 @@ import java.util.Random;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
/**
* Testing QuadTree class.
*/
/** Testing QuadTree class. */
class QuadTreeTest {
@Test
@@ -47,22 +44,21 @@ class QuadTreeTest {
var p = new Bubble(rand.nextInt(300), rand.nextInt(300), i, rand.nextInt(2) + 1);
points.add(p);
}
var field = new Rect(150, 150, 300, 300); //size of field
var queryRange = new Rect(70, 130, 100, 100); //result = all points lying in this rectangle
//points found in the query range using quadtree and normal method is same
var field = new Rect(150, 150, 300, 300); // size of field
var queryRange = new Rect(70, 130, 100, 100); // result = all points lying in this rectangle
// points found in the query range using quadtree and normal method is same
var points1 = QuadTreeTest.quadTreeTest(points, field, queryRange);
var points2 = QuadTreeTest.verify(points, queryRange);
assertEquals(points1, points2);
}
static Hashtable<Integer, Point> quadTreeTest(Collection<Point> points, Rect field, Rect queryRange) {
//creating quadtree and inserting all points
static Hashtable<Integer, Point> quadTreeTest(
Collection<Point> points, Rect field, Rect queryRange) {
// creating quadtree and inserting all points
var qTree = new QuadTree(queryRange, 4);
points.forEach(qTree::insert);
return qTree
.query(field, new ArrayList<>())
.stream()
return qTree.query(field, new ArrayList<>()).stream()
.collect(Collectors.toMap(p -> p.id, p -> p, (a, b) -> b, Hashtable::new));
}
@@ -29,10 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
/**
* Testing Rect class.
*/
/** Testing Rect class. */
class RectTest {
@Test
@@ -40,7 +37,7 @@ class RectTest {
var r = new Rect(10, 10, 20, 20);
var b1 = new Bubble(2, 2, 1, 1);
var b2 = new Bubble(30, 30, 2, 1);
//r contains b1 and not b2
// r contains b1 and not b2
assertTrue(r.contains(b1));
assertFalse(r.contains(b2));
}
@@ -50,7 +47,7 @@ class RectTest {
var r1 = new Rect(10, 10, 20, 20);
var r2 = new Rect(15, 15, 20, 20);
var r3 = new Rect(50, 50, 20, 20);
//r1 intersects r2 and not r3
// r1 intersects r2 and not r3
assertTrue(r1.intersects(r2));
assertFalse(r1.intersects(r3));
}
@@ -30,10 +30,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
import java.util.HashMap;
import org.junit.jupiter.api.Test;
/**
* Testing SpatialPartition_Bubbles class.
*/
/** Testing SpatialPartition_Bubbles class. */
class SpatialPartitionBubblesTest {
@Test
@@ -55,7 +52,7 @@ class SpatialPartitionBubblesTest {
qt.insert(b4);
var sp = new SpatialPartitionBubbles(bubbles, qt);
sp.handleCollisionsUsingQt(b1);
//b1 touches b3 and b4 but not b2 - so b1,b3,b4 get popped
// b1 touches b3 and b4 but not b2 - so b1,b3,b4 get popped
assertNull(bubbles.get(1));
assertNotNull(bubbles.get(2));
assertNull(bubbles.get(3));