Во-первых, следует понимать, что чисто абстрактный класс - это просто интерфейс, который не может выполнять множественное наследование.
Напишите класс, извлеките интерфейс, это занятие для мозга. Настолько, что у нас есть рефакторинг для этого. Что жаль. Следуя этому шаблону «каждый класс получает интерфейс», он не только создает беспорядок, но и полностью теряет смысл.
Интерфейс не должен рассматриваться как просто формальное подтверждение того, что может сделать класс. Интерфейс должен рассматриваться как контракт, заключаемый с использованием клиентского кода, детализирующего его потребности.
У меня нет проблем с написанием интерфейса, который в настоящее время имеет только один класс, реализующий его. Мне действительно все равно, если ни один класс вообще не реализует его. Потому что я думаю о том, что нужно для моего кода. Интерфейс выражает то, что требует использование кода. Все, что приходит позже, может делать то, что ему нравится, при условии, что оно соответствует этим ожиданиям.
Теперь я не делаю это каждый раз, когда один объект использует другой. Я делаю это при пересечении границы. Я делаю это, когда не хочу, чтобы один объект точно знал, с каким другим объектом он разговаривает. Это единственный способ, которым полиморфизм будет работать. Я делаю это, когда ожидаю, что объект, о котором говорит мой клиентский код, может измениться. Я, конечно, не делаю этого, когда я использую класс String. Класс String хорош и стабилен, и я не чувствую необходимости защищать его от меня.
Когда вы решаете напрямую взаимодействовать с конкретной реализацией, а не через абстракцию, вы предсказываете, что реализация достаточно стабильна, чтобы доверять, чтобы не изменяться.
Именно так я и усмиряю принцип обращения зависимости . Вы не должны слепо фанатично применять это ко всему. Когда вы добавляете абстракцию, вы действительно говорите, что не доверяете выбору реализации класса, который будет стабильным в течение всего жизненного цикла проекта.
Все это предполагает, что вы пытаетесь следовать принципу открытого закрытого типа . Этот принцип важен только тогда, когда затраты, связанные с внесением прямых изменений в установленный код, значительны. Одна из главных причин, почему люди не согласны с тем, насколько важны развязки объектов, заключается в том, что не все испытывают одинаковые затраты при внесении прямых изменений. Если повторное тестирование, перекомпиляция и перераспределение всей вашей кодовой базы является тривиальным для вас, то решение необходимости изменений с помощью прямого изменения, вероятно, является очень привлекательным упрощением этой проблемы.
На этот вопрос просто нет мертвого ответа. Интерфейс или абстрактный класс - это не то, что вы должны добавить в каждый класс, и вы не можете просто посчитать количество реализующих классов и решить, что это не нужно. Это касается изменения. Что означает, что вы предвидите будущее. Не удивляйтесь, если вы ошибаетесь. Держите это простым, как вы можете, не заглядывая в угол.
Поэтому, пожалуйста, не пишите абстракции только для того, чтобы помочь нам прочитать код. У нас есть инструменты для этого. Используйте абстракции, чтобы отделить то, что нужно отделить.