In my Spring Boot app, I am trying to implement Template Method and in my concrete class, I am trying to use generic as shown below:
template interface: Not sure if I need to use it?
public interface PDFGenerator {
String createHtml(UUID uuid);
}
template abstract class:
public abstract class AbstractPDFGenerator<T> implements PDFGenerator {
@Override
public String createHtml(UUID uuid) {
T dto = getDTO(uuid);
Context context = new Context();
context.setVariable(getName(), dto.getName());
// ...
}
protected abstract T getDTO(UUID uuid);
protected abstract String getName();
// code omitted
}
concrete classes:
@Service
@RequiredArgsConstructor
public class BrandPDFGenerator extends AbstractPDFGenerator<BrandDTO> {
private static final String NAME = "brandName";
private final BrandService brandService;
@Override
protected String getName() {
return NAME;
}
@Override
protected BrandDTO getDTO(UUID uuid) {
return brandService.findByUuid(uuid);
}
// ...
}
@Service
@RequiredArgsConstructor
public class ProductPDFGenerator extends AbstractPDFGenerator<ProductDTO> {
private static final String NAME = "productName";
private final ProductService productService;
@Override
protected String getName() {
return NAME;
}
@Override
protected ProductDTO getDTO(UUID uuid) {
return productService.findByUuid(uuid);
}
// ...
}
I get "Cannot resolve method 'getName' in 'T'" at the dto.getName()
line in AbstractPDFGenerator
.
My questions are:
In order to fix the problem, I think of extending T from a base class from which BrandDTO
and ProductDTO
are inherit. However, I do not want to inherit them from a base class as they have not used similar purpose. So, how can I fix that problem ("Cannot resolve method 'getName' in 'T'")?
Do I need to use PDFGenerator
interface? Or should I remove it?
Option 1: Use interface, not abstract class. Abstract class will work, but dtos must have correct relationship with each other. If they don't have is a
relationship with the base class, it will be wrong.
public interface HasName {
String getName();
}
Then make BrandDTO
and ProductDTO
implement it, and introduce a bound for T
- only implementations of HasName
.
public abstract class AbstractPDFGenerator<T extends HasName> implements PDFGenerator
Option 2: Don't use generics. Keep in mind this option won't let you use template method pattern and will lead to code duplication.
public abstract class AbstractPDFGenerator implements PDFGenerator {
@Override
public String createHtml(UUID uuid) {
Context context = new Context();
this.setDataInContext(context, uuid);
//other operations
return ...;
}
protected abstract void setDataInContext(Context context, UUID uuid);
}
Concrete implementations of setDataInContext
will take care to set correct data, they will work with the actual dto, and generic is not needed.
public class ProductPDFGenerator extends AbstractPDFGenerator {
private static final String NAME = "productName";
private final ProductService productService;
@Override
protected void setDataInContext(Context context, UUID uuid) {
ProductDTO dto = productService.findByUuid(uuid);
context.setVariable(NAME, dto.getName());
//other operations
}
}
The implementation of BrandPDFGenerator
will be the same, except finding the dto.