Imagine que você possui um módulo de software, que é um plug-in para algum programa (semelhante ao Eclipse) e deseja que ele tenha uma API que outros plug-ins podem chamar. Seu plugin não está disponível gratuitamente, assim que você quer ter um módulo API separado, que é livremente disponível e é a única coisa que outros plugins precisam diretamente link para - clientes da API pode compilar com apenas o módulo API, e não o módulo de aplicação, no caminho da construção. Se a API for forçada a evoluir de maneiras compatíveis, os plug-ins de clientes poderão até incluir o módulo da API em seus próprios jars (para evitar qualquer possibilidade de Error
s resultantes do acesso a classes inexistentes).
O licenciamento não é o único motivo para colocar a API e a implementação em módulos separados. Pode ser que o módulo de implementação seja complexo, com inúmeras dependências próprias. Os plug-ins do Eclipse geralmente têm pacotes internos e não internos, em que os pacotes não internos são semelhantes a um módulo de API (ambos estão incluídos no mesmo módulo, mas podem ser separados).
Eu já vi algumas alternativas diferentes para isso:
A API está em um pacote separado (ou grupo de pacotes) da implementação. As classes da API chamam diretamente nas classes de implementação. A API não pode ser compilada a partir da fonte (o que é desejável em alguns casos incomuns) sem a implementação. Não é fácil prever os efeitos exatos de chamar métodos de API quando a implementação não está instalada - portanto, os clientes geralmente evitam fazer isso.
package com.pluginx.api; import com.pluginx.internal.FooFactory; public class PluginXAPI { public static Foo getFoo() { return FooFactory.getFoo(); } }
A API está em um pacote separado e usa reflexão para acessar as classes de implementação. A API pode ser compilada sem a implementação. O uso da reflexão pode causar um impacto no desempenho (mas os objetos de reflexão podem ser armazenados em cache se houver um problema. É fácil controlar o que acontece se a implementação não estiver disponível.
package com.pluginx.api; public class PluginXAPI { public static Foo getFoo() { try { return (Foo)Class.forName("com.pluginx.internal.FooFactory").getMethod("getFoo").invoke(null); } catch(ReflectiveOperationException e) { return null; // or throw a RuntimeException, or add logging, or raise a fatal error in some global error handling system, etc } } }
A API consiste apenas em interfaces e classes abstratas, além de uma maneira de obter uma instância de uma classe.
package com.pluginx.api; public abstract class PluginXAPI { public abstract Foo getFoo(); private static PluginXAPI instance; public static PluginXAPI getInstance() {return instance;} public static void setInstance(PluginXAPI newInstance) { if(instance != null) throw new IllegalStateException("instance already set"); else instance = newInstance; } }
O mesmo que acima, mas o código do cliente precisa obter a referência inicial de outro lugar:
// API package com.pluginx.api; public interface PluginXAPI { Foo getFoo(); } // Implementation package com.pluginx.internal; public class PluginX extends Plugin implements PluginXAPI { @Override public Foo getFoo() { ... } } // Client code uses it like this PluginXAPI xapi = (PluginXAPI)PluginManager.getPlugin("com.pluginx"); Foo foo = xapi.getFoo();
Não. Faça com que os clientes se vinculem diretamente ao plug-in (mas ainda os impeçam de chamar métodos que não são da API). Isso tornaria difícil para muitos outros plugins (e a maioria dos plugins de código aberto) usar a API desse plug-in sem gravar seu próprio wrapper.