Clase-diseño es un subconjunto de diseño de software: Así que todo depende.
Como la pregunta es poco subjetiva (todo el mundo tiene un enfoque diferente), solo diré que utilizo este método, sin decir que es el mejor. Probablemente hay muchas formas diferentes.
public interface FuncX {
public void actionX(FuncP p, FuncQ q, FuncR r);
}
Y las clases implementan esta interfaz. Si dos clases son pequeñas pero están relacionadas, les permito implementar ambas interfaces.
Hace que cada implementación sea muy fácil de probar. Para iniciar el sistema, un método principal debe crear instancias de clases específicas. Esto puede ser configurable, por ejemplo.
public class MyFuncX implements FuncX, FuncP {
public void actionX(FuncP p, FuncQ q, FuncR r) {
...
}
public void actionP(...) {
...
}
}
// the caller:
FuncX x = new MyFuncX(); // dependency
FuncQ q = ...;
FuncR r = ...;
x.actionX(x, q, r);