为什么是接口?
你会开车吗?如果不是,我假设您知道驾驶汽车通常需要什么(方向盘、油门、刹车)。其余答案假设您驾驶汽车并且拥有与我不同品牌的汽车。
你开过我的车吗?不会。但是如果获得访问权限,您是否能够在无需学习如何驾驶我的汽车的情况下驾驶我的汽车?是的。
这同样适用于我。我从来没有开过你的车,但我可以驾驶它而无需学习如何驾驶它。
这是为什么呢? 因为所有汽车共享相同的界面。方向盘,油门在右边,刹车在中间。没有两辆汽车是完全相同的,但它们的构建方式是驾驶员与任何汽车之间的交互是完全相同的。
将此与 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();
}
}
这个司机只会开宝马。
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();
}
}
这个司机只会开奥迪。
但实际上,驾驶员可以驾驶任何汽车(有一个方向盘和两个踏板)。
那么我们如何告诉编译器可以使用任何汽车呢?我们赋予它们一个共同点:界面。
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 { ... }
这就是编译器崩溃的地方。它拒绝允许您同时从多个基类继承。原因比这个答案可以深入得多,如果你想了解更多,请谷歌。
但是,我们是否使用了接口:
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 答案。