views:

52

answers:

4

In an effort to reduce duplication, I have my app's workspace split into 3 projects:

  • Main (A library project, where all of the common code lives)
  • Free (To make the free version)
  • Paid (To make the paid version)

Behavior in the free version and the paid version must differ sometimes. How can I "call into" the final projects from the library project?

Here is some sample psuedo-code to illustrate my question:

In the Main project:

private void makeADecision() {
    if (AppCode.isPaid()) {
        // do one thing
    } else {
        // do something else
    }
}

In the Free project:

public class AppCode {
    public static bool isPaid() {
        return false;
    }
}

In the Paid project:

public class AppCode {
    public static bool isPaid() {
        return true;
    }
}

That is basically what I have tried, but it won't compile because the Main project doesn't know about the AppCode class.

Bear in mind that this is only an example, so try not to focus on how an app can tell if it is the paid version or not. :) So far the best solution I have found is to put a string in the resources of all three projects and then make a decision based on its value but I don't like that method. Besides being ugly, I would prefer to keep functionality where it belongs. That way I can prevent "paid-only" functionality from being compiled into the free version at all and I can avoid having to include any "free-only" code in the paid version.

+2  A: 

I don't think it's a good idea to call the main App from the library, even if it's possible.

Instead I'd be adding a public static boolean to the library and set it from within your application once it starts for the first time.

public class MyLibrary {
    public static boolean IS_PAID = false;

    public void makeADecision() {
        if(IS_PAID) {
            // do one thing
        } else {
           // do something else
        }
    }
}

and in your main application you could do something like

com.yourname.yourlib.MyLibrary.IS_PAID = true;

to set it. Since it's not final, you can change it's state at any time. If it's more complicated behavior, you could use a public static listener or callback which you could assign from your full/free app and then call it from your library

Tseng
+1  A: 

You could use reflection to achieve that - take care that it is usually not a very good idea.

For example:

static private boolean isAppPaid;

static {
    try {
        Class c = Class.forName("your.package.AppCode");
        Method m = c.getMethod("isPaid");
        isAppPaid = (boolean) m.invoke(null);
    }
    catch (Exception e) {
        isAppPaid = false;
    }
}

There probably are mistakes in my code - I have never used Java reflection much.

Edit: I agree with Tseng that making a library invoke application code is debatable at best. (Except if said library is a framework that takes over the client application.)

jhominal
A: 

You could also make the free and paid versions make subclasses of whatever class makeADecision is in and implement the separate behavior that way.

so in main

public class BaseClass {    
    ...    
    public void makeADecision() {}
    ...
}

in free

public class FreeClass extends BaseClass {
...
    public void makeADecision() {
        //free implementation here
    }
...
}

in paid

public class PaidClass extends BaseClass {
...
    public void makeADecision() {
        //paid implementation here
    }
...
}
lathomas64
+1  A: 

Step #1: Define an interface in the library, so it is available to all three parties, whose methods are whatever operations you want the library to perform on the app

Step #2: Have the app supply an implementation of the interface to the library via some library-supplied API

Step #3: Have the library call methods on the supplied interface as needed

CommonsWare