Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Choose class implementation at compile time

Tags:

java

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:

  • if I compile with choice == 0, the Dog class doesn't get compiled
  • if I compile with choice == 1, the Cat class doesn't get compiled

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?

like image 234
bgd223 Avatar asked Nov 22 '17 19:11

bgd223


3 Answers

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.

like image 173
dpr Avatar answered Oct 22 '22 01:10

dpr


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.

like image 28
Peter Avatar answered Oct 22 '22 03:10

Peter


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.

like image 2
9000 Avatar answered Oct 22 '22 03:10

9000