I have a TemplateEngine
interface whose implementations would be MoustacheTemplateEngine
, FreemarkerTemplateEngine
etc.
public interface TemplateEngine<T> {
public T compileTemplate(String templateStr);
public void merge(T t, Map<String, Object> data);
public String getTemplateLang();
}
The purpose of compileTemplate
method is to enable caching of compiled templates.
Here is an implementation example:
import com.github.mustachejava.Mustache;
public class MoustacheTemplateEngine implements TemplateEngine<Mustache> {
@Override
public Mustache compileTemplate(String templateStr) {
// return compiled template;
}
@Override
public void merge(Mustache compiledTemplate, Map<String, Object> data) {
// merge
}
@Override
public String getTemplateLang() {
return "moustache";
}
}
I wish to create a factory that returns a TemplateEngine
depending on the template language supplied. The factory, and the client that would use the factory, do not know anything about TemplateEngine
implementations.
public class TemplateEngineFactory {
private Map<String, TemplateEngine<?>> TEMPLATE_ENGINE_REGISTRY = new HashMap<>();
@PostConstruct
public void init() {
// Scan all TemplateEngine impls in classpath and populate registry
}
public TemplateEngine<?> getTemplateEngine(String templateLang) {
return TEMPLATE_ENGINE_REGISTRY.get(templateLang);
}
}
A client would use the factory as below.
Map<String, Object> data = new HashMap<>();
data.put("name", "Tom");
TemplateEngine<?> templateEngine = factory.getTemplateEngine("moustache");
Object compiledTemplate = templateEngine.compileTemplate("Hi {{name}}");
templaeEngine.merge(compiledTemplate, data); // compile error
The error being The method merge(capture#3-of ?, Map<String,Object>) in the type TemplateEngine<capture#3-of ?> is not applicable for the arguments (Object, Map<String,Object>)
.
I understand the error and I know my API design is flawed due to the use of wildcard in factory. My question is how to design a factory for such use case and avoid unsafe casts?