I'm developing an application (Java) in a modular architecture. I have two approaches in mind and I'm not sure which one will be "better code" in case of maintenance and conventions.
I have two main modules that I want to wire them up:
CORE
- backend stuffUI
- frontend stuff
I can think of two approaches:
APPROACH 1:
CORE
module will exposeEngine
class,UI
will exposeUserInterface
class.UserInterface
will need anEngine
in its constructor. Creation of both objects will take place in the third module calledLAUNCHER
.APPROACH 2:
CORE
module will exposeEngine
class,UI
will exposeUserInterface
class. The difference is both modules with expose those classes ONLY toPRELAUNCHER
module that will create objects and pass them to theLAUNCHER
module as interfaces.
The second approach looks promising, because it won't allow any module create the instantion of UI/CORE module classes (as Java offers sealed interface - that means I can choose which class can implement it). That's kind of dependency injection - the needed items will be created in one place and passed as interfaces.
On the other hand - the approach introduces more code - one more module and at least two new interfaces.
Any ideas which one is better in terms of maintenance and conventions?
-
2Have you considered a standard dependency injection framework? How are the unit tests going to instantiate these things?pjc50– pjc5002/24/2022 14:37:59Commented Feb 24, 2022 at 14:37
-
What is the advantage of not being able to create new engines and new user interfaces?Stack Exchange Broke The Law– Stack Exchange Broke The Law03/23/2022 13:28:07Commented Mar 23, 2022 at 13:28
2 Answers 2
Going by the names you assigned to your classes it does not seem the engine depends on the UI in any way. If the dependency is indeed one way (UI uses engine) there is no point in abstracting away the UI for any client to address it through a interface. You may be better of by just having the UI instantiate the engine and by focusing on making the engine's interface useful to the UI.
Modularisation aims to decompose a software into replaceable and reusable components that are based on well-defined interfaces.
Approach 1 partially achieves this objective : UserInterface
depends on a Engine
via an implicit interface that encapsulates its implementation details. You could therefore replace the implementation of Engine
with another implementation or even a subclass of it. But you are cannot reuse UserInterface
independently of Engine
.
Approach 2 completely achieves the objectives of the modularization: according to your explanations UserInterface
no longer depends on Engine
but on an interface that abstracts away the engine and could easily be replaces with other implementations.
The use of a prelauncher and a launcher to further fine-tune accessibility control would of course enforce the proper use of the interface. But it might be an overkill in your case:
- Instead, you may consider to just modularize the interface, i.e having an
EngineInterface
defined independently of theEngine
, and that could safely be consumed by theUserInterface
and theLauncher
. This would be simpler. The launcher could still do the wiring as above since its goal is to make the glue between your modules and not be independent. - Adding an
EngineFactory
to the interfaces could make sense if engines are constructed in a lot of places but seem not absolutely required in your context.
Explore related questions
See similar questions with these tags.