Вы можете использовать тот факт, что интерфейсы позволяют вложенным классам (автоматически общедоступным статическим) сохранять реализацию по умолчанию методов интерфейса, инкапсулированных в самом интерфейсе. То есть переместите класс BusinessLogic примера Алекса Б. в интерфейс.
Это похоже на способ, которым Scala генерирует код JVM для признаков, как описано здесь Как признаки Scala компилируются в байт-код Java?
При этом пример становится:
interface BusinessLogicInterface {
void method0();
class DefaultImpl {
private DefaultImpl() {
}
public static void method1(BusinessLogicInterface self) { ... }
public static void method2(BusinessLogicInterface self) { ... }
}
void method1();
void method2();
}
class User extends OtherClass implements BusinessLogicInterface {
@Override
void method0() { ... }
@Override
void method1() { BusinessLogic.defaultImpl.method1(this); }
@Override
void method2() { BusinessLogic.defaultImpl.method2(this); }
}
Обратите внимание, что мы передаем объект типа интерфейса в качестве параметра "self". Это означает, что бизнес-логика может использовать другие абстрактные методы (method0). Это может быть очень полезно для создания черты с абстрактными методами, которые все ортогональны друг другу, и полезными методами «расширения», которые могут быть реализованы в терминах этих ортогональных методов.
Недостатком является то, что каждый интерфейс должен копировать / вставлять код делегирования шаблонов. Другой часто используемый шаблон в Java без этого недостатка (но с меньшей согласованностью и меньшим количеством ОО-способов вызова методов) - создание класса с множественным именем в качестве интерфейса, содержащего статические методы, это используется в служебном классе Collections.