Suppose I have an Animal
interface and particular classes implementing it Cat
and Dog
. Right at the entry to the program, there's this:
if (choice == 0) {
Animal A = new Cat();
}
else if (choice == 1) {
Animal A = new Dog();
}
I'd like to have the choice
parameter be populated at compile time (e.g., during Maven build), so that:
The reason for this is that 'Cat' and 'Dog' classes have separate/different dependencies.
Basically, the goal is to have one build for "A is a Cat" and another build for "A is a Dog". When I package the JAR, the use case for running the JAR will be either for 'Cat' or for 'Dog' (depending on the environment), never both 'Cat' and 'Dog' - so, there is no need for all dependencies to be in the project.
What is the best way to achieve this?
You could move the two implementations of Animal
to two separate projects. Each project can have its own dependencies. Then you can package both projects independently of each other (including the respective dependencies) and only one implementation of Animal
will be known at runtime.
You might end up with something like this:
base-animals
├ com.wang.animals.base.Animal
└ com.wang.animals.base.AnimalFactory
dog-application
├ com.wang.animals.dogs.Dog
└ application.properties // animalClass=com.wang.animals.dogs.Dog
cat-application
├ com.wang.animals.cats.Cat
└ application.properties // animalClass=com.wang.animals.cats.Cat
Both dog-application
and cat-application
depend on base-animals
and possibly other projects but don't need to know each other. Your build could then create a dog-application.jar
and a cat-application.jar
that could be shipped/handled as you like.
To determine which Animal
to use at runtime I'd implement an AnimalFactory
that looks up the Animal
implementation of choice by some properties file or system properties, or something similar (I omitted exception handling and stuff as this is an example only):
public class AnimalFactory {
public static Animal createAnimal() throws Exception {
final String animalClassName = getThisValueFromApplicationProperties();
return Class.forName(animalClassName).getConstructor().newInstance();
}
}
If you don't care about Cat
and Dog
being shipped together you could also move them to the base-animals
project and just keep the application.properties
file in the dog-application
and cat-application
. You would however need to ship both applications with all dependencies needed by Cat
and Dog.
If 'choice' is a static, final primitive or String it will be set at compile time.
static final int choice = 0;
I would question why this is necessary though? It seems like a lot of complexity to save what will likely be negligible JAR size. If there really are significant differences, then separate Maven projects with their own dependencies would be a better approach.
First I assume that you don't want to build Dog
when you use Cat
, and build Cat
when you use Dog
.
Next I assume that in your program you never ever mention Dog
and Cat
directly, else both of them became a compilation dependency. Anywhere but the Cat
and Dog
implementations, you should refer to the interface Animal
only.
Then you can use Class.forName
to look up a particular Cat
or Dog
, pass it through isSubclass
to make sure it implements Animal
, look up a constructor, invoke it, and cast the resulting object to Animal
.
The above makes most sense when you ship your app as a jar, but don't ship either cat.jar
or dog.jar
along with it, e.g. for licensing reasons, or because they use different native code dependencies, etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With