Почему интерфейсы?
Вы водите машину? Если нет, то я предполагаю, что вы знаете, что влечет за собой вождение автомобиля (руль, акселератор, тормоз). Остальная часть ответа предполагает, что вы водите машину и у вас есть машина, которая отличается от моей.
Вы когда-нибудь водили мою машину? Нет. Но если бы у вас был доступ, вы могли бы водить мою машину, не изучая, как водить мою машину? Да.
То же самое относится и ко мне. Я никогда не водил вашу машину, но я смог бы водить ее без необходимости учиться водить ее.
Почему это так? Поскольку все автомобили имеют один и тот же интерфейс . Руль, акселератор справа, тормоз посередине. Нет двух одинаковых автомобилей, но они построены таким образом, что взаимодействие между водителем и любой машиной абсолютно одинаково.
Сравните это с истребителем F16. Умение водить машину не дает вам возможности управлять самолетом , поскольку его интерфейс отличается . У него нет руля, нет педали акселератора / тормоза.
Главное преимущество очевидно: водителям не нужно учиться водить каждую машину индивидуально.
Теперь, чтобы завершить аналогию, общая концепция автомобиля - это интерфейс, тогда как интерфейсконкретные машины это классы. Главное преимущество очевидно: вам не нужно писать собственный код для каждого подобного класса.
Практический пример
public class BMW
{
public SteeringWheel SteeringWheel { get; set; }
public Pedal Accelerator { get; set; }
public Pedal Brake { get; set; }
}
public class BMWDriver
{
public void ParticipateInRace(BMW myBMW)
{
myBMW.Accelerator.Press();
myBMW.SteeringWheel.TurnLeft();
myBMW.SteeringWheel.TurnRight();
myBMW.Accelerator.Release();
myBMW.Brake.Press();
myBMW.Brake.Release();
}
}
Только для этого драйверазнает, как водить BMW.
public class Audi
{
public SteeringWheel SteeringWheel { get; set; }
public Pedal Accelerator { get; set; }
public Pedal Brake { get; set; }
}
public class AudiDriver
{
public void ParticipateInRace(Audi myAudi)
{
myAudi.Accelerator.Press();
myAudi.SteeringWheel.TurnLeft();
myAudi.SteeringWheel.TurnRight();
myAudi.Accelerator.Release();
myAudi.Brake.Press();
myAudi.Brake.Release();
}
}
Этот водитель знает, как водить Audi.
Но на самом деле, водитель сможет водить любой автомобиль (у которого есть рулевое колесо и две педали).
Итак, как нам сказать компилятору, что можно использовать любой автомобиль? Мы даем им общее, что они оба разделяют: интерфейс.
public interface ICar
{
SteeringWheel SteeringWheel { get; }
Pedal Accelerator { get; }
Pedal Brake { get; }
}
public class BMW : ICar { /* same as before */ }
public class Audi : ICar { /* same as before */ }
public class Driver
{
public void ParticipateInRace(ICar anyCar)
{
anyCar.Accelerator.Press();
anyCar.SteeringWheel.TurnLeft();
anyCar.SteeringWheel.TurnRight();
anyCar.Accelerator.Release();
anyCar.Brake.Press();
anyCar.Brake.Release();
}
}
Теперь у нас есть более обобщенный Driver
, который способен управлять любой машиной с рулевым колесом и двумяпедали.
Почему бы не наследовать?
Половина времени у меня все хорошо, если вы создаете класс и наследуете его, разве это не будет делать то же самое? что мне здесь не хватает?
В некоторых случаях наследование сработало бы. Однако наследование, как правило, является неполноценным решением, особенно когда вы получаете более сложные кодовые базы или более сложные архитектуры.
Не волнуйтесь, все разработчики когда-то любили наследование, а затем должны были научиться не использовать наследование как лекарство от всех болезней. Это часть обычного жизненного цикла разработчика:)
Одна из главных причин, по которой не может быть производным от нескольких классов, но вы можете реализовать несколько интерфейсов .
Допустим, у нас есть три вида спорта, которые можно делать
public class Runner
{
public void Run() { /* running logic */ }
}
public class Swimmer
{
public void Swim() { /* swimming logic */ }
}
public class Cyclist
{
public void Cycle() { /* cycling logic */ }
}
Теперь нам нужно создать специализированный вид спорта, который включает бег, например, баскетбол.
public class BasketBallPlayer : Runner
{
public void ThrowBall() { /* throwing logic */ }
// Running is already inherited from Runner
}
Отлично, проблем пока нет. Но теперь нам нужно создать класс триатлета, который включает в себя все три вида спорта (бег, плавание, езда на велосипеде)
public class Triathlete: Runner, Swimmer, Cyclist { ... }
И это то, где компилятор ломается. Он не позволяет вам наследовать от нескольких базовых классов одновременно. Причины гораздо глубже, чем можно вникнуть в этот ответ, Google, если вы хотите узнать больше.
Однако, если бы мы использовали интерфейсы:
public interface IRunner
{
void Run();
}
public interface ISwimmer
{
void Swim();
}
public interface ICyclist
{
void Cycle();
}
Тогда компилятор позволил быэто:
public class Triathlete: IRunner, ISwimmer, ICyclist
{
public void Run() { /* running logic */ }
public void Swim() { /* swimming logic */ }
public void Cycle() { /* cycling logic */ }
}
И именно поэтому интерфейсы обычно побеждают наследование (среди прочих причин).
Есть и другие причины, почему интерфейсы, как правило, лучше, но это самая большая из них. Если вы хотите получить более подробное объяснение, воспользуйтесь им, но я не могу вникать во все это для ответа StackOverflow.