Интерфейсы, как правило, делают код немного чище. Я чувствую, что его немного легче тестировать. Когда вы добавляете расширения, вы добавляете еще больше гибкости, сохраняя чистый тестируемый код.
Для меня абстрактные классы всегда казались неуклюжими, используя интерфейсы, у меня может быть фабрика объектов, которая возвращает объект, который является специфическим для того, что я пытаюсь выполнить (разделение задач).
Просто придумываю
У меня есть интерфейс с именем Math, который имеет сложение, вычитание, деление и умножение, а затем у меня есть класс с именем IntMAth, который реализует Math, который оптимизирован для целочисленной математики, и у меня есть еще один класс с именем FloatMath, который реализует Math, который оптимизирован для Floating Math, и у меня есть GeneralMath, который реализует Math, который обрабатывает все остальное.
Когда мне нужно добавить несколько чисел с плавающей точкой, я мог бы вызвать мою фабрику MathFactory.getMath (typeof (float)), и у нее есть некоторая логика, чтобы знать, что если тип, который я передаю, является float, то он возвращает класс FloatMath.
Таким образом, все мои классы меньше и удобнее в обслуживании, код, который вызывает классы, меньше и т. Д.