How to design such an API very much depends on how your API is intended to be used.
If it for example turns out, that your People
class is best implemented as a final class, and you want to make sure, that it is always checked
in the same consistent way, then providing a number of public static check...
methods is certainly a reasonable way to go.
If on the other hand you do not know in advance how your People
class should be checked, then I'd consider providing an ICheckingFunctions
interface that declares the necessary check...
methods. But if you go this route, you will perhaps also need to provide a way for the user to change the actually used implementation of ICheckingFunctions
.
You should also consider, that while using an interface is certainly much more flexible and extensible, it is also more work to maintain and it could also provide a possible security risk - e.g. if you allow users to change the used ICheckingFunction
, then you no longer have control of how your People
class is checked.
One possible way to implement such an API using an interface is allowing users to register/unregister the used ICheckingFunction
in your class. A very naive implementation could look like this:
public final class CheckingFunctions {
private static ICheckingFunctions checkFunction;
public static void registerCheckFunction(ICheckingFunctions checkFunction) {
CheckingFunctions.checkFunction = checkFunction;
}
public static boolean checkName(People ppl){
return checkFunction.checkName(ppl);
}
public static boolean checkAge(People ppl){
return checkFunction.checkAge(ppl);
}
}
This is of course just a minimal example. In an actual API you would have to decide quite a lot of additional details. For example:
- Is there only ever a single
ICheckingFunctions
instance available? If there may be more registered ICheckingFunctions
- how do you choose which of these functions are used?
- Who is allowed to register/unregister an
ICheckingFunctions
instance?
- May the
ICheckingFunctions
be called from different threads?
- etc.
You must also consider in which environment your API is going to be used. If you for example want to support usage of your API in an OSGI environment, then you could e.g. supply your ICheckingFunctions
as an OSGI service.
Last but not least I would consider the following: May your users subclass the People
class? If yes, then it would perhaps be a good idea to make the ICheckingFunctions
interface generic, and allow registrations of implementations for different classes. Here again a very naive example of this approach:
public final class CheckingFunctions {
public interface ICheckingFunctions<T extends People> {
boolean checkName(T p);
boolean checkAge(T p);
}
private static Map<Class<?>,ICheckingFunctions<?>> checkFunctions = new ConcurrentHashMap<>();
public static <T extends People> void registerCheckFunction(ICheckingFunctions<T> checkFunction, Class<T> c) {
checkFunctions.put(c, checkFunction);
}
private static <T extends People> ICheckingFunctions<T> getRegisteredCheckFunction(Class<T> c){
ICheckingFunctions<T> checkFunction = (ICheckingFunctions<T>) checkFunctions.get(c);
if (checkFunction == null) {
// provide some reasonable default?
throw new IllegalStateException();
}
return checkFunction;
}
public static <T extends People> boolean checkName(T ppl, Class<T> c){
return getRegisteredCheckFunction(c).checkName(ppl);
}
public static <T extends People> boolean checkAge(T ppl, Class<T> c){
return getRegisteredCheckFunction(c).checkAge(ppl);
}
}