diff --git a/pom.xml b/pom.xml
index 8dd299954c7cd00346d4747029af4e5808880b3b..173602cce489762953bb386e0be015f4ced3fcad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
 	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>vsb-cs-java1</groupId>
-	<artifactId>lab05v1</artifactId>
+	<artifactId>lab07v1</artifactId>
 	<version>0.0.1-SNAPHOST</version>
 	<packaging>jar</packaging>
 	<properties>
@@ -54,4 +54,16 @@
 			<scope>test</scope>
 		</dependency>
 	</dependencies>
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.13.0</version>
+				<configuration>
+					<failOnError>false</failOnError>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
 </project>
diff --git a/src/main/java/lab/App.java b/src/main/java/lab/App.java
index 735a8b46f274cea82e92e2aa1445583b12e8eb00..9543b11402f10c5e733e14fb53c490df31336adf 100644
--- a/src/main/java/lab/App.java
+++ b/src/main/java/lab/App.java
@@ -15,6 +15,9 @@ import javafx.stage.WindowEvent;
  */
 public class App extends Application {
 
+	static {
+		System.out.println("aaa");
+	}
 	private GameController gameController;
 	public static void main(String[] args) {
 		launch(args);
diff --git a/src/main/java/lab/BulletAnimated.java b/src/main/java/lab/BulletAnimated.java
index d74427ca60b113770dec07e4796e5cf4dc43836d..e0de23723c455e00b19138217348e1db2687c929 100644
--- a/src/main/java/lab/BulletAnimated.java
+++ b/src/main/java/lab/BulletAnimated.java
@@ -1,5 +1,8 @@
 package lab;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import javafx.geometry.Point2D;
 import javafx.geometry.Rectangle2D;
 import javafx.scene.canvas.GraphicsContext;
@@ -11,6 +14,8 @@ public class BulletAnimated extends Bullet {
 	private final Point2D initVelocity;
 	private Cannon cannon;
 	private Image image = new Image(this.getClass().getResourceAsStream("fireball-transparent.gif"));
+	private List<HitListener> hitListeners = 
+			new ArrayList<>();
 
 	public BulletAnimated(World world, Cannon cannon, Point2D position, Point2D velocity, Point2D acceleration) {
 		super(world, position, velocity, acceleration);
@@ -27,7 +32,8 @@ public class BulletAnimated extends Bullet {
 	@Override
 	public void hitBy(Collisionable another) {
 		if (another instanceof Ufo) {
-			reload();
+			world.remove(this);
+			fireUfoDestroyed();
 		}
 	}
 
@@ -35,5 +41,19 @@ public class BulletAnimated extends Bullet {
 		position = cannon.getPosition();
 		velocity = new Point2D(0, 0);
 	}
+
+	public boolean addHitListener(HitListener e) {
+		return hitListeners.add(e);
+	}
+
+	public boolean removeHitListener(HitListener o) {
+		return hitListeners.remove(o);
+	}
+	
+	private void fireUfoDestroyed() {
+		for (HitListener hitListener : hitListeners) {
+			hitListener.ufoDestroyed();
+		}
+	}
 	
 }
\ No newline at end of file
diff --git a/src/main/java/lab/GameController.java b/src/main/java/lab/GameController.java
index 3ad72637660c92fecd268de5ee0d3c212a5e9d09..35211711b0769e81d4f543db4d9eb0d147efd3ce 100644
--- a/src/main/java/lab/GameController.java
+++ b/src/main/java/lab/GameController.java
@@ -1,11 +1,10 @@
 package lab;
 
-import javafx.beans.value.ChangeListener;
-import javafx.beans.value.ObservableValue;
 import javafx.event.ActionEvent;
 import javafx.fxml.FXML;
 import javafx.geometry.Point2D;
 import javafx.scene.canvas.Canvas;
+import javafx.scene.control.Label;
 import javafx.scene.control.Slider;
 
 public class GameController {
@@ -21,18 +20,37 @@ public class GameController {
 
 	private DrawingThread timer;
 
+    @FXML
+    private Label hits;
+    private int hitcount = 0;
 	@FXML
 	void fire(ActionEvent event) {
 		double angle = timer.getWorld().getCannon().getAngle();
 		double angleRad = Math.toRadians(angle);
 		double speedValue = speed.getValue();
-		timer.getWorld().getBulletAnimated().reload();
 		Point2D velocity = new Point2D(
 				Math.cos(angleRad)*speedValue, 
 				Math.sin(angleRad)*speedValue); 
-		timer.getWorld().getBulletAnimated().setVelocity(velocity);
+		BulletAnimated bulletAnimated = new BulletAnimated(
+				timer.getWorld(), 
+				timer.getWorld().getCannon(),
+				timer.getWorld().getCannon().getPosition(), 
+				velocity, World.GRAVITY);
+		timer.getWorld().add(bulletAnimated);
+		bulletAnimated.addHitListener(this::increaseHits);
+		bulletAnimated.addHitListener(
+				() -> System.out.println("au!!!!"));
 	}
 
+	private void updateHits() {
+		hits.setText(String.format("Hit count: %03d", hitcount));
+	}
+
+	private void increaseHits() {
+		hitcount++;
+		updateHits();
+	}
+	
 	@FXML
 	void initialize() {
 		assert angle != null : "fx:id=\"angle\" was not injected: check your FXML file 'gameWindow.fxml'.";
@@ -40,12 +58,9 @@ public class GameController {
 		assert speed != null : "fx:id=\"speed\" was not injected: check your FXML file 'gameWindow.fxml'.";
 		timer = new DrawingThread(canvas);
 		timer.start();
-		angle.valueProperty().addListener(new ChangeListener<Number>() {
-			@Override
-			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
-				timer.getWorld().getCannon().setAngle(newValue.doubleValue());
-			}
-		});
+		angle.valueProperty().addListener(
+				(observable, oldValue, newValue) -> 
+					timer.getWorld().getCannon().setAngle(newValue.doubleValue()));
 	}
 
 	public void stop() {
diff --git a/src/main/java/lab/HitListener.java b/src/main/java/lab/HitListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..833d7e3e5af31946fafc4f7883eab795287ed765
--- /dev/null
+++ b/src/main/java/lab/HitListener.java
@@ -0,0 +1,6 @@
+package lab;
+
+@FunctionalInterface
+public interface HitListener {
+	void ufoDestroyed();
+}
diff --git a/src/main/java/lab/Ufo.java b/src/main/java/lab/Ufo.java
index 78ea525119625e1f61f338e2811b29e1c7ee946d..c55bfd776d67b082e28ce5d4a79c34425fca5f5a 100644
--- a/src/main/java/lab/Ufo.java
+++ b/src/main/java/lab/Ufo.java
@@ -54,7 +54,7 @@ public class Ufo extends WorldEntity implements Collisionable {
 	@Override
 	public void hitBy(Collisionable another) {
 		if(another instanceof BulletAnimated || another instanceof Bullet) {
-			changeDirection();
+			world.remove(this);
 		}
 	}
 }
diff --git a/src/main/java/lab/World.java b/src/main/java/lab/World.java
index 1f7b4e53a758e0ba63d8a34ef21d822ccf72b401..d34bdf6cb2b6ca210b0e15b586322a1384ce528b 100644
--- a/src/main/java/lab/World.java
+++ b/src/main/java/lab/World.java
@@ -1,32 +1,40 @@
 package lab;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
 
 import javafx.geometry.Point2D;
 import javafx.scene.canvas.GraphicsContext;
 
 public class World {
 
+	public static final Point2D GRAVITY = new Point2D(0, 9.81);
 	private final double width;
 
 	private final double height;
 
-	private DrawableSimulable[] entities;
+	private List<DrawableSimulable> entities;
+	private Collection<DrawableSimulable> entitiesToRemove = new LinkedList<DrawableSimulable>();
+	private Collection<DrawableSimulable> entitiesToAdd = new LinkedList<DrawableSimulable>();
+	
 	private Cannon cannon;
-	private BulletAnimated bulletAnimated;
+//	private BulletAnimated bulletAnimated;
 
 	public World(double width, double height) {
 		this.width = width;
 		this.height = height;
-		entities = new DrawableSimulable[8];
+		entities = new ArrayList<>();
 		cannon = new Cannon(this, new Point2D(0, height - 20), -45);
-		bulletAnimated = new BulletAnimated(this, cannon, new Point2D(0, height), new Point2D(50, -80),
-				new Point2D(0, 9.81));
-		entities[0] = cannon;
-		entities[1] = new Bullet(this, new Point2D(0, height), new Point2D(30, -30), new Point2D(0, 9.81));
-		entities[2] = bulletAnimated;
-		for (int i = 3; i < entities.length; i++) {
-			entities[i] = new Ufo(this);
+//		bulletAnimated = new BulletAnimated(this, cannon, new Point2D(0, height), new Point2D(50, -80),
+//				GRAVITY);
+		entities.add(cannon);
+		entities.add(new Bullet(this, new Point2D(0, height), new Point2D(30, -30), new Point2D(0, 9.81)));
+//		entities.add(bulletAnimated);
+		for (int i = 0; i < 3; i++) {
+			entities.add(new Ufo(this));
 		}
 	}
 
@@ -44,10 +52,10 @@ public class World {
 		for (DrawableSimulable entity : entities) {
 			entity.simulate(deltaT);
 		}
-		for (int i = 0; i < entities.length; i++) {
-			if (entities[i] instanceof Collisionable c1) {
-				for (int j = i + 1; j < entities.length; j++) {
-					if (entities[j] instanceof Collisionable c2) {
+		for (int i = 0; i < entities.size(); i++) {
+			if (entities.get(i) instanceof Collisionable c1) {
+				for (int j = i + 1; j < entities.size(); j++) {
+					if (entities.get(j) instanceof Collisionable c2) {
 						if (c1.intersect(c2.getBoundingBox())) {
 							c1.hitBy(c2);
 							c2.hitBy(c1);
@@ -56,11 +64,23 @@ public class World {
 				}
 			}
 		}
+		entities.removeAll(entitiesToRemove);
+		entities.addAll(entitiesToAdd);
+		entitiesToAdd.clear();
+		entitiesToRemove.clear();
 	}
 
 	public double getWidth() {
 		return width;
 	}
+	
+	public void add(DrawableSimulable entity) {
+		entitiesToAdd.add(entity);
+	}
+	public void remove(DrawableSimulable entity) {
+		entitiesToRemove.add(entity);
+		
+	}
 
 	public double getHeight() {
 		return height;
@@ -70,8 +90,8 @@ public class World {
 		return cannon;
 	}
 
-	public BulletAnimated getBulletAnimated() {
-		return bulletAnimated;
-	}
-
+//	public BulletAnimated getBulletAnimated() {
+//		return bulletAnimated;
+//	}
+//
 }
\ No newline at end of file
diff --git a/src/main/resources/lab/gameWindow.fxml b/src/main/resources/lab/gameWindow.fxml
index b65e058f8949927a1d3b0baf0007e3373fa33316..c2b94f192a11c3e3bd9bc79f608fae84cc19f2ed 100644
--- a/src/main/resources/lab/gameWindow.fxml
+++ b/src/main/resources/lab/gameWindow.fxml
@@ -4,6 +4,7 @@
 <?import javafx.scene.Cursor?>
 <?import javafx.scene.canvas.Canvas?>
 <?import javafx.scene.control.Button?>
+<?import javafx.scene.control.Label?>
 <?import javafx.scene.control.Slider?>
 <?import javafx.scene.layout.BorderPane?>
 <?import javafx.scene.layout.HBox?>
@@ -33,10 +34,13 @@
       </HBox>
    </bottom>
    <center>
-      <Canvas fx:id="canvas" height="287.0" width="582.0" BorderPane.alignment="CENTER">
+      <Canvas fx:id="canvas" height="306.0" width="582.0" BorderPane.alignment="CENTER">
          <BorderPane.margin>
             <Insets />
          </BorderPane.margin>
       </Canvas>
    </center>
+   <top>
+      <Label fx:id="hits" text="Hit count:  0" textAlignment="CENTER" BorderPane.alignment="CENTER" />
+   </top>
 </BorderPane>
diff --git a/src/main/resources/lab/scenery.gif b/src/main/resources/lab/scenery.gif
deleted file mode 100644
index d65c8d3d921f20d9b41709b90e4c79e3a6bd6232..0000000000000000000000000000000000000000
Binary files a/src/main/resources/lab/scenery.gif and /dev/null differ
diff --git a/src/test/java/jez04/structure/test/ClassStructureTest.java b/src/test/java/jez04/structure/test/ClassStructureTest.java
index c545ff810230339a9776aeb920ce4afd9fd75d8a..820053648bea210a51013245bfb28d70910cce16 100644
--- a/src/test/java/jez04/structure/test/ClassStructureTest.java
+++ b/src/test/java/jez04/structure/test/ClassStructureTest.java
@@ -3,13 +3,23 @@ package jez04.structure.test;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
 import java.io.PrintStream;
-import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -24,191 +34,100 @@ import org.reflections.util.ConfigurationBuilder;
 
 import javafx.event.ActionEvent;
 import javafx.fxml.FXML;
-import javafx.geometry.Rectangle2D;
-import javafx.scene.canvas.GraphicsContext;
 
 class ClassStructureTest {
 
-	private static final String drawableSimulableName = "DrawableSimulable";
-	private static final String collisionableName = "Collisionable";
-
-	Set<String> allClasses = getNameOfAllClasses();
+	StructureHelper helper = new StructureHelper();
 
 	@Test
 	void gameControllerExistenceTest() {
-		classExist("GameController");
-		Class<?> c = getClass("GameController");
-		hasPropertyWithAnnotation(c, ".*", FXML.class);
-		hasMethodRegexp(c, ".*", void.class, ActionEvent.class);
+		helper.classExist("GameController");
 	}
+
 	@Test
 	void gameControllerFxmlTest() {
-		classExist("GameController");
-		Class<?> c = getClass("GameController");
-		hasPropertyWithAnnotation(c, ".*", FXML.class);
-	}
-	@Test
-	void gameControllerActionEventTest() {
-		classExist("GameController");
-		Class<?> c = getClass("GameController");
-		hasMethodRegexp(c, ".*", void.class, ActionEvent.class);
+		helper.classExist("GameController");
+		Class<?> c = helper.getClass("GameController");
+		helper.hasPropertyWithAnnotation(c, ".*", FXML.class);
 	}
 
 	@Test
-	void cannonExistenceTest() {
-		classExist("Cannon");
-	}
-	@Test
-	void cannonExistenceSetAngleTest() {
-		classExist("Cannon");
-		Class<?> c = getClass("Cannon");
-		hasMethod(c, "setAngle");
-	}
-
-
-	private void isInterface(Class<?> c) {
-		assertTrue(c.isInterface(), c.getName() + " have to be interface.");
-	}
-
-	private void classExist(String name) {
-		assertTrue(allClasses.stream().anyMatch(c -> c.endsWith(name)), "Interface " + name + " not found");
+	void gameControllerActionMethodTest() {
+		helper.classExist("GameController");
+		Class<?> c = helper.getClass("GameController");
+		helper.hasMethodRegexp(c, ".*", void.class, ActionEvent.class);
 	}
 
-	private Class<?> getClass(String name) {
-		String className = allClasses.stream().filter(c -> c.endsWith(name)).findAny().orElse(null);
-		if (className == null) {
-			Assertions.fail("Class " + name + " not found.");
-		}
-		try {
-			return Class.forName(className);
-		} catch (ClassNotFoundException e) {
-			final ByteArrayOutputStream baos = new ByteArrayOutputStream();
-			try (PrintStream ps = new PrintStream(baos, true)) {
-				e.printStackTrace(ps);
-			} catch (Exception e2) {
-				Assertions.fail(e2.getMessage());
-			}
-			String stackTrace = baos.toString();
-			Assertions.fail("Class " + name + " not found.\n" + stackTrace);
-			return null;
-		}
+	@Test
+	void gameControllerLambdasTest() {
+		helper.classExist("GameController");
+		Class<?> c = helper.getClass("GameController");
+		long lamdaCount = helper.countMethodRegexp(c, "lambda\\$.*");
+		long innerClasscount = helper.countClassesRegexp(".*GameController\\$.*");
+		assertTrue(lamdaCount + innerClasscount >= 2,
+				"At least 2 inner classes or lamdas required for GameController but only "
+						+ (lamdaCount + innerClasscount) + " found.");
 	}
 
-	private void hasProperty(Class<?> classDef, String propertyNameRegexp, Class<?> type, boolean array) {
-		List<Field> fields = Arrays.asList(classDef.getDeclaredFields());
-		assertTrue(fields.stream().anyMatch(f -> {
-			if (f.getName().matches(propertyNameRegexp)) {
-				if (array) {
-					return f.getType().isArray() && f.getType().getComponentType().equals(type);
-				} else {
-					return f.getType().equals(type);
-				}
-			}
-			return false;
-		}), "No field " + propertyNameRegexp + " of type " + type.getName() + " (is array " + array + ") in class "
-				+ classDef.getName());
+	@Test
+	void hitListenerExistenceTest() {
+		helper.classExist("HitListener");
 	}
 
-	private void hasPropertyWithAnnotation(Class<?> classDef, String propertyNameRegexp, Class<?> annotation) {
-		List<Field> fields = Arrays.asList(classDef.getDeclaredFields());
-		assertTrue(
-				fields.stream().filter(f -> f.getName().matches(propertyNameRegexp))
-						.flatMap(f -> Arrays.asList(
-								f.getAnnotations()).stream()).map(a -> a.annotationType()).anyMatch(a -> 
-								a.equals(annotation)),
-				"No field " + propertyNameRegexp + " with annotation " + annotation.getName() + " in class "
-						+ classDef.getName());
+	@Test
+	void hitListenerEventMethodTest() {
+		helper.classExist("HitListener");
+		Class<?> c = helper.getClass("HitListener");
+		helper.hasMethod(c, "ufoDestroyed");
 	}
 
-	private void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType) {
-		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
-		assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
-		assertTrue(
-				methods.stream().filter(m -> m.getName().contains(methodName))
-						.anyMatch(m -> m.getReturnType().equals(returnType)),
-				"Method " + methodName + " not return " + returnType.getName());
+	@Test
+	void sceneCollectionTest() {
+		helper.classExist("World");
+		Class<?> c = helper.getClass("World");
+		long collectionCount = Arrays.asList(c.getDeclaredFields()).stream()
+				.filter(f -> Collection.class.isAssignableFrom(f.getType())).count();
+		assertTrue(collectionCount >= 3, "lab.Scene require atleast 3 filed of type/subtype Collection, but only "
+				+ collectionCount + " found.");
 	}
 
-	private void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType, Class<?>... params) {
-		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
-		assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
-		assertTrue(
-				methods.stream().filter(m -> m.getName().contains(methodName))
-						.filter(m -> m.getReturnType().equals(returnType))
-						.anyMatch(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))),
-				"Method " + methodName + " has no all parrams:"
-						+ Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", ")));
+	@Test
+	void worldMethodAddTest() {
+		helper.classExist("World");
+		Class<?> c = helper.getClass("World");
+		helper.hasMethod(c, "add", void.class, helper.getClass("DrawableSimulable"));
+		;
 	}
 
-	private void hasMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, Class<?> returnType, Class<?>... params) {
-		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
-		assertTrue(methods.stream().anyMatch(m -> m.getName().matches(methodNameRegexp)), "No method " + methodNameRegexp);
-		assertTrue(
-				methods.stream().filter(m -> m.getName().matches(methodNameRegexp))
-						.filter(m -> m.getReturnType().equals(returnType))
-						.anyMatch(m -> 
-						Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))),
-				"Method " + methodNameRegexp + " has no all parrams:"
-						+ Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", ")));
-	}
-	private void hasMethod(Class<?> interfaceDef, boolean finalTag, boolean abstractTag, String methodName,
-			Class<?> returnType, Class<?>... params) {
-		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
-		assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
-		assertTrue(
-				methods.stream().filter(m -> m.getName().contains(methodName))
-						.filter(m -> m.getReturnType().equals(returnType)
-								&& (Modifier.isAbstract(m.getModifiers()) == abstractTag)
-								&& (Modifier.isFinal(m.getModifiers()) == finalTag))
-						.anyMatch(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))),
-				"Method " + methodName + " has no all params:"
-						+ Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", ")));
+	@Test
+	void worldMethodRemoveTest() {
+		helper.classExist("World");
+		Class<?> c = helper.getClass("World");
+		helper.hasMethod(c, "remove", void.class, helper.getClass("DrawableSimulable"));
+		;
 	}
 
-	private void hasImplements(Class<?> clazz, String... interfaceNames) {
-		List<Class<?>> interfaces = new ArrayList<>();
-		Arrays.asList(interfaceNames).stream().map(name -> getClass(name)).forEach(c -> interfaces.add(c));
-		assertTrue(Arrays.asList(clazz.getInterfaces()).containsAll(interfaces), "Class not implements all interfaces:"
-				+ interfaces.stream().map(Class::getName).collect(Collectors.joining(", ")));
+	@Test
+	void bulletAnimatedMethodAddTest() {
+		helper.classExist("BulletAnimated");
+		Class<?> c = helper.getClass("BulletAnimated");
+		Class<?> l = helper.getClass("HitListener");
+		helper.hasMethodRegexp(c, "add.*", List.of(void.class, boolean.class), l);
 	}
 
-	private void hasExtends(Class<?> clazz, String parentName) {
-		Class<?> parent = getClass(parentName);
-		assertTrue(clazz.getSuperclass().equals(parent),
-				"Class " + clazz.getName() + " not extends class " + parentName);
+	@Test
+	void bulletAnimatedMethodRemoveTest() {
+		helper.classExist("Ufo");
+		Class<?> c = helper.getClass("BulletAnimated");
+		Class<?> l = helper.getClass("HitListener");
+		helper.hasMethodRegexp(c, "remove.*", List.of(void.class, boolean.class), l);
 	}
 
-	private void hasMethod(Class<?> interfaceDef, String methodName) {
-		List<Method> methods = Arrays.asList(interfaceDef.getMethods());
-		assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
+	@Test
+	void bulletAnimatedMethodFireTest() {
+		helper.classExist("BulletAnimated");
+		Class<?> c = helper.getClass("BulletAnimated");
+		assertTrue(helper.countMethodRegexp(c, "fire.*") > 0, "Method fire.* in LochNess not found.");
 	}
 
-	private Set<String> getNameOfAllClasses() {
-		List<String> initClassesName = Arrays.asList("lab.Routines", "lab.App", "lab.DrawingThread");
-		for (String className : initClassesName) {
-			try {
-				Class.forName(className);
-			} catch (ClassNotFoundException e) {
-				System.out.println(String.format("Class '%s' cannot be loaded: %s", className, e.getMessage()));
-			}
-		}
-		Set<String> allClasses = new HashSet<>();
-		for (Package p : Package.getPackages()) {
-			if (p.getName().startsWith("java.") || p.getName().startsWith("com.") || p.getName().startsWith("jdk.")
-					|| p.getName().startsWith("javafx.") || p.getName().startsWith("org.")
-					|| p.getName().startsWith("sun.") || p.getName().startsWith("javax.")
-					|| p.getName().startsWith("javassist")) {
-				continue;
-			}
-			System.out.println(p.getName());
-			Configuration conf = new ConfigurationBuilder().addScanners(Scanners.SubTypes.filterResultsBy(pc -> true))
-					.forPackages(p.getName());
-			Reflections reflections = new Reflections(conf);
-			allClasses.addAll(reflections.getAll(Scanners.SubTypes.filterResultsBy(c -> {
-				System.out.println(c);
-				return true;
-			})));
-		}
-		return allClasses;
-	}
 }
diff --git a/src/test/java/jez04/structure/test/StructureHelper.java b/src/test/java/jez04/structure/test/StructureHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa10c81e0b4ccbc5d8bd405731e00501a30f8c27
--- /dev/null
+++ b/src/test/java/jez04/structure/test/StructureHelper.java
@@ -0,0 +1,272 @@
+package jez04.structure.test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.junit.jupiter.api.Assertions;
+import org.reflections.Configuration;
+import org.reflections.Reflections;
+import org.reflections.scanners.Scanners;
+import org.reflections.util.ConfigurationBuilder;
+
+class StructureHelper {
+
+	Set<String> allClasses = getNameOfAllClasses();
+
+	public void isInterface(Class<?> c) {
+		assertTrue(c.isInterface(), c.getName() + " have to be interface.");
+	}
+
+	public void classExist(String name) {
+		assertTrue(allClasses.stream().anyMatch(c -> c.endsWith(name)), "Class/Interface " + name + " not found");
+	}
+
+	public Class<?> getClass(String name) {
+		String className = allClasses.stream().filter(c -> c.endsWith(name)).findAny().orElse(null);
+		if (className == null) {
+			Assertions.fail("Class " + name + " not found.");
+		}
+		try {
+			return Class.forName(className);
+		} catch (ClassNotFoundException e) {
+			final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+			try (PrintStream ps = new PrintStream(baos, true)) {
+				e.printStackTrace(ps);
+			} catch (Exception e2) {
+				Assertions.fail(e2.getMessage());
+			}
+			String stackTrace = baos.toString();
+			Assertions.fail("Class " + name + " not found.\n" + stackTrace);
+			return null;
+		}
+	}
+
+	public void hasProperty(Class<?> classDef, String propertyNameRegexp, Class<?> type, boolean array) {
+		List<Field> fields = Arrays.asList(classDef.getDeclaredFields());
+		assertTrue(fields.stream().anyMatch(f -> {
+			if (f.getName().matches(propertyNameRegexp)) {
+				if (array) {
+					return f.getType().isArray() && f.getType().getComponentType().equals(type);
+				} else {
+					return f.getType().equals(type);
+				}
+			}
+			return false;
+		}), "No field " + propertyNameRegexp + " of type " + type.getName() + " (is array " + array + ") in class "
+				+ classDef.getName());
+	}
+
+	public void hasPropertyWithAnnotation(Class<?> classDef, String propertyNameRegexp, Class<?> annotation) {
+		List<Field> fields = Arrays.asList(classDef.getDeclaredFields());
+		assertTrue(
+				fields.stream().filter(f -> f.getName().matches(propertyNameRegexp))
+						.flatMap(f -> Arrays.asList(f.getAnnotations()).stream()).map(a -> a.annotationType())
+						.anyMatch(a -> a.equals(annotation)),
+				"No field " + propertyNameRegexp + " with annotation " + annotation.getName() + " in class "
+						+ classDef.getName());
+	}
+
+	public void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType) {
+		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
+		assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
+		assertTrue(
+				methods.stream().filter(m -> m.getName().contains(methodName))
+						.anyMatch(m -> m.getReturnType().equals(returnType)),
+				"Method " + methodName + " not return " + returnType.getName());
+	}
+
+	public void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType, Class<?>... params) {
+		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
+		assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
+		assertTrue(
+				methods.stream().filter(m -> m.getName().contains(methodName))
+						.filter(m -> m.getReturnType().equals(returnType))
+						.anyMatch(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))),
+				"Method " + methodName + " has no all parrams:"
+						+ Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", ")));
+	}
+
+	public long countMethodRegexp(Class<?> interfaceDef, String methodNameRegexp) {
+		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
+		return methods.stream().filter(m -> m.getName().matches(methodNameRegexp)).count();
+	}
+	public long countClassesRegexp(String classNameRegexp) {
+		return getNameOfAllClasses().stream().filter(className -> className.matches(classNameRegexp)).count();
+	}
+
+	public void hasMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, Class<?> returnType,
+			Class<?>... params) {
+		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
+		assertTrue(methods.stream().anyMatch(m -> m.getName().matches(methodNameRegexp)),
+				"No method " + methodNameRegexp);
+		assertTrue(
+				methods.stream().filter(
+						m -> 
+						m.getName().matches(methodNameRegexp))
+						.filter(
+								m -> 
+								m.getReturnType().equals(returnType))
+						.anyMatch(m ->
+						Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))),
+				"Method " + methodNameRegexp + " has no all parrams:"
+						+ Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", ")));
+	}
+
+	public void hasMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, Collection<Class<?>> returnTypeOnOf,
+			Class<?>... params) {
+		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
+		assertTrue(methods.stream().anyMatch(m -> m.getName().matches(methodNameRegexp)),
+				"No method " + methodNameRegexp);
+		assertTrue(
+				methods.stream().filter(
+						m -> 
+						m.getName().matches(methodNameRegexp))
+						.filter(
+								m -> 
+								returnTypeOnOf.contains(m.getReturnType()))
+						.anyMatch(m ->
+						Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))),
+				"Method " + methodNameRegexp + " has no all parrams:"
+						+ Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", ")));
+	}
+
+	public void hasMethod(Class<?> interfaceDef, boolean finalTag, boolean abstractTag, String methodName,
+			Class<?> returnType, Class<?>... params) {
+		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
+		assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
+		assertTrue(
+				methods.stream().filter(m -> m.getName().contains(methodName))
+						.filter(m -> m.getReturnType().equals(returnType)
+								&& (Modifier.isAbstract(m.getModifiers()) == abstractTag)
+								&& (Modifier.isFinal(m.getModifiers()) == finalTag))
+						.anyMatch(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))),
+				"Method " + methodName + " has no all params:"
+						+ Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", ")));
+	}
+
+	public void hasImplements(Class<?> clazz, String... interfaceNames) {
+		List<Class<?>> interfaces = new ArrayList<>();
+		Arrays.asList(interfaceNames).stream().map(name -> getClass(name)).forEach(c -> interfaces.add(c));
+		assertTrue(Arrays.asList(clazz.getInterfaces()).containsAll(interfaces), "Class not implements all interfaces:"
+				+ interfaces.stream().map(Class::getName).collect(Collectors.joining(", ")));
+	}
+
+	public void hasExtends(Class<?> clazz, String parentName) {
+		Class<?> parent = getClass(parentName);
+		assertTrue(clazz.getSuperclass().equals(parent),
+				"Class " + clazz.getName() + " not extends class " + parentName);
+	}
+
+	public void hasMethod(Class<?> interfaceDef, String methodName) {
+		List<Method> methods = Arrays.asList(interfaceDef.getMethods());
+		assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
+	}
+
+	public Set<String> getNameOfAllClasses() {
+		List<String> initClassesName = new ArrayList<>();
+		dynamicaliFoundSomeClass(initClassesName);
+		initClassesName.addAll(List.of("lab.Routines", "lab.App", "lab.DrawingThread"));
+		for (String className : initClassesName) {
+			try {
+				Class.forName(className);
+				break;
+			} catch (ClassNotFoundException e) {
+				System.out.println(String.format("Class '%s' cannot be loaded: %s", className, e.getMessage()));
+			}
+		}
+		Set<String> allClasses = new HashSet<>();
+		for (Package p : Package.getPackages()) {
+			if (p.getName().startsWith("java.") || p.getName().startsWith("com.") || p.getName().startsWith("jdk.")
+					|| p.getName().startsWith("javafx.") || p.getName().startsWith("org.")
+					|| p.getName().startsWith("sun.") || p.getName().startsWith("javax.")
+					|| p.getName().startsWith("javassist")) {
+				continue;
+			}
+			System.out.println(p.getName());
+			Configuration conf = new ConfigurationBuilder().addScanners(Scanners.SubTypes.filterResultsBy(pc -> true))
+					.forPackages(p.getName());
+			Reflections reflections = new Reflections(conf);
+			allClasses.addAll(reflections.getAll(Scanners.SubTypes.filterResultsBy(c -> {
+				System.out.println(c);
+				return true;
+			})));
+		}
+		for (String string : allClasses) {
+			System.out.println(string);
+		}
+		return allClasses;
+	}
+
+	public void dynamicaliFoundSomeClass(List<String> initClassesName) {
+		URL myClassUrl = StructureHelper.class.getResource("ClassStructureTest.class");
+		myClassUrl.getFile();
+		try {
+			Path classRoot = Paths.get(myClassUrl.toURI()).getParent().getParent().getParent().getParent();
+			if ("test-classes".equals(classRoot.getFileName().toString())) {
+				classRoot = classRoot.getParent().resolve("classes");
+			}
+			System.out.println("class root: " + classRoot);
+			final Path classRootFinal = classRoot;
+			Files.walkFileTree(classRoot, new FileVisitor<Path>() {
+
+				@Override
+				public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+					if (List.of("jez04", "META-INF").contains(dir.getFileName().toString())) {
+						return FileVisitResult.SKIP_SUBTREE;
+					}
+					return FileVisitResult.CONTINUE;
+				}
+
+				@Override
+				public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+					System.out.println("VISIT: " + file);
+					if ("module-info.class".equals(file.getFileName().toString())) {
+						return FileVisitResult.CONTINUE;
+					}
+					if (!file.getFileName().toString().endsWith(".class")) {
+						return FileVisitResult.CONTINUE;
+					}
+					String foundClassName = classRootFinal.relativize(file).toString();
+					foundClassName = foundClassName.substring(0, foundClassName.length() - 6)
+							.replace(File.separatorChar, '.');
+					initClassesName.add(foundClassName);
+					return FileVisitResult.TERMINATE;
+				}
+
+				@Override
+				public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
+					return FileVisitResult.CONTINUE;
+				}
+
+				@Override
+				public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+					return FileVisitResult.CONTINUE;
+				}
+			});
+		} catch (URISyntaxException | IOException e) {
+			e.printStackTrace();
+		}
+	}
+}