Я считаю, что в большинстве случаев лучше всего делать и то, и другое. Это не всегда возможно, когда вы полагаетесь на то, что происходит в базовом классе. Предоставление как абстрактного базового класса, так и интерфейса обеспечивает наибольшую широту для реализаторов вашей абстракции , опять же, до тех пор, пока разработчик интерфейса не требует, чтобы что-то произошло. Если вам требуется , чтобы что-то произошло, то вы не вообще хотите предоставить интерфейс - и вам также необходимо убедиться, что ваш базовый класс обеспечивает это требуемое действие для ВСЕГДА происходят ...
Отрицательно делать и то и другое: больше липучки.
пример псевдокода:
interface IVehicle
{
void PutCarInGear(Int speed);
}
abstract class Vehicle : IVehicle
{
public void PutCarInGear(Int speed)
{
//base implementation
}
}
Обратите внимание, что в этом примере кто-то может создать свой собственный класс, который реализует IVehicle
, а затем передать его любому объекту, который принимает IVehicle
. Этот объект НЕ ДОЛЖЕН быть потомком абстрактного класса Vehicle
. Следовательно, если вы ожидаете, что в методе PutCarInGear()
произойдет что-то конкретное, вполне вероятно, что новый класс НЕ будет соответствовать этому ожиданию. Однако до тех пор, пока не имеет значения, что делают реализации IVehicle
, это самая гибкая абстракция, имеющая как абстрактный базовый класс, так и интерфейс.