Без дополнительного контекста я бы также сказал, интерфейсы. Учтите, что вы можете представить функцию или делегат как интерфейс с одним методом, а абстрактные классы - это просто интерфейсы с некоторыми методами, которые уже могут быть реализованы.
Тем не менее, это действительно зависит от того, чего вы пытаетесь достичь. Интерфейсы пригодны для случаев, когда у вас есть много объектов с общим интерфейсом, но потенциально различными реализациями. Если, например, вы разрабатываете очень простую систему обратного вызова для плагинов (т. Е. Пусть плагин перехватывает определенные события в хост-приложении), тогда делегаты, вероятно, проще и достаточны для ваших нужд.
Также имейте в виду, что если вы используете интерфейсы, вам, возможно, понадобится какой-нибудь способ для экземпляра экземпляра хоста. Самый простой способ сделать это - зарегистрировать делегата на хосте под уникальным именем.
Абстрактные классы полезны, только если вы хотите использовать интерфейсы и предоставлять реализацию некоторых вещей по умолчанию. Лучшее решение в этом случае - использовать реальный интерфейс и предоставить реализацию по умолчанию в виде миксина.