I will simplify the code to make my question easier to understand. The code is something like that:
import sample.backend.*;
import sample.frontend.*;
public class Game {
public void run(){
GameBackendImpl gameBackend = new GameBackendImpl();
GameUIImpl gameUI = new GameUIImpl();
}
}
And this next would be my main code, which I have separated for easy testing.
public class Main {
public static void main(String[] args) {
Game game = new Game();
game.run();
}
}
My question here is to know if it is possible to inject a backend and a frontend instance into the Game class from the Main class, using dependency injection.
Thanks in advance
CodePudding user response:
In this simplified case the dependency injection means just creating the instances of the classes you need in the main() method and passing them to the constructor of the Game class:
public class Game {
privat final GameBackend backend;
privat final GameUI ui;
public Game(GameBackend gameBackend, GameUI gameUI) {
this.backend = gameBackend;
this.ui = gameUI;
}
public void run(){
...
}
}
Main class:
public class Main {
public static void main(String[] args) {
GameBackend gameBackend = new GameBackendImpl();
GameUI gameUI = new GameUIImpl();
Game game = new Game(gameBackend, gameUI);
game.run();
}
}
But this approach, of course, doesn't bring the advantages of the Dependency Injection/Inversion of Control.
I guess you are looking for the possibility to inject your dependencies automatically and to be able to configure them according to your needs (different implementations for different environments etc.). In this case you can use a framework like Spring which will take control on the program flow, instantiating and injecting the dependencies into corresponding classes.
The simple implementation using Spring Boot would be the following:
GameBackend.java:
public interface GameBackend {
String getVersion();
}
GameUI.java:
public interface GameUI {
String getVersion();
}
GameBackendImpl.java:
@Component
public class GameBackendImpl implements GameBackend {
@Override
public String getVersion() {
return "Backend_v1";
}
}
GameUiImpl.java:
@Component
public class GameUiImpl implements GameUI {
@Override
public String getVersion() {
return "UI_v1";
}
}
Game.java:
@Component
public class Game {
private final GameBackend backend;
private final GameUI ui;
@Autowired
public Game (GameBackend backend, GameUI ui) {
this.backend = backend;
this.ui = ui;
}
public void run() {
System.out.println("Game bean instantiated with the UI '" this.ui.getVersion() "' and Backend '" this.backend.getVersion() "' versions");
}
}
SpringBootDemoApp.java:
@SpringBootApplication
public class SpringBootDemoApp implements CommandLineRunner {
@Autowired
ApplicationContext applicationContext;
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApp.class, args);
}
@Override
public void run(String... args) {
((Game)(applicationContext.getBean("game"))).run();
}
}
What happens here:
Spring scans classes in the package and finds Beans annotated with the @Component annotation. Upon instantiating the Game class, Spring considers its constructor, which is annotated with @Autowired and requires exactly 2 parameters (dependencies), and looks for the candidates to be injected. In this case there is only one candidate per each type (you can create multiple different implementations of the GameBackend and GameUI interfaces). Thus, all the preparation work is done by the framework. There is much more happening under the hood of course.
In the main class, we can check the result by getting the instance of the Game class (Bean with the name "game) from the ApplicationContext. Calling its run() method will print:
"Game bean instantiated with the UI 'UI_v1' and Backend 'Backend_v1' versions"
NOTE: getting and using the instance of the Game class from the main class doesn't bring any advantages and is done only for the demonstration.
There are more DI/IoC frameworks for Java (CDI in J2EE, Guice etc.)