Зависимости внутри системы лучше строить от интерфейсов или абстрактных классов? Когда лучше использовать абстрактный класс и когда интерфейс? Например, у нас есть компонент логирования. Мы можем создать AbstractLogger с реализацией по-умолчанию и унаследовать все свои логгеры от него, в конструкторах или сеттерах других классов указывать что они принимают в параметре логгер типа AbstractLogger. На сколько это правильно? Может лучше использовать интерфейс? Например мы можем создать LoggerInterface, в конструкторах или сеттерах других классов указывать что они принимают в параметре логгер типа LoggerInterface, а например реализацию по-умолчанию реализовать в AbstractLogger который реализует несколько методов из LoggerInterface, таким образом я могу сам решать - унаследоваться от AbstractLogger который для меня уже реализовал несколько методов из интерфейса или если меня эта реализация не устраивает я могу написать свою реализовав LoggerInterface. По-моему зависимости внутри системы от интерфейсов более гибкие. Подскажите, пожалуйста, как правильно определить когда использовать интерфейс и когда абстрактный класс?