Overloaded Type Surface
ID |
java.max_methods |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Complexity |
Language |
Java |
Tags |
complexity |
Description
Reports classes, interfaces, enums and records that declare more methods than the configured limit allows. Methods of every visibility count, and so do constructors, including a record’s compact constructor.
Inherited methods, enum constants and record components are not counted. Nested and local types are measured on their own members, independently of the type that contains them.
Rationale
Past a certain size a type stops having a purpose that can be stated in one sentence. Its API is too wide to learn, unrelated callers end up depending on different slices of it, and every change has to be reasoned about against everything else the type happens to do. Large private surfaces count too: they are usually the internal machinery of two or three responsibilities that were never separated.
public class ReportService {
public Report load(long id) { ... }
public void save(Report r) { ... }
public byte[] renderPdf(Report r) { ... }
public byte[] renderCsv(Report r) { ... }
public void mail(Report r, String to) { ... }
public void schedule(Report r, Cron cron) { ... }
public Stats aggregate(List<Report> rs) { ... }
// ... forty more
}
Interfaces deserve the same scrutiny: a wide interface forces every implementation to carry methods it does not care about, which is exactly what the interface segregation principle warns against.
Remediation
Group the methods that operate on the same data or serve the same caller, and move each group into its own type. The original type keeps the coordination and delegates the rest.
public class ReportService {
private final ReportRepository repository;
private final ReportRenderer renderer;
private final ReportDelivery delivery;
public byte[] exportPdf(long id) {
return renderer.toPdf(repository.load(id));
}
}
For a wide interface, split it along the lines its clients actually use, so each implementation only has to satisfy the part it cares about.