你开汽车吗?如果不是,我假设您知道驾驶汽车通常需要做什么(方向盘,加速器,制动器)。其余的答案假设您驾驶汽车,并且拥有与我的品牌不同的汽车。
你有没有开过我的车?否。但是如果获得访问权限,您将能够在不学习如何驾驶我的汽车的情况下驾驶我的汽车吗?是。
同样适用于我。我从未驾驶过您的汽车,但无需学习如何驾驶就可以驾驶它。
这是为什么?因为所有汽车都共享相同的接口。方向盘,右侧油门,中间刹车。没有两辆汽车是完全相同的,但是它们的构建方式使驾驶员与任何汽车之间的交互作用都完全相同。
将此与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();
}
}
Run Code Online (Sandbox Code Playgroud)
该驾驶员只知道如何驾驶宝马。
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();
}
}
Run Code Online (Sandbox Code Playgroud)
该驾驶员只知道如何驾驶奥迪。
但实际上,驾驶员将能够驾驶任何汽车(具有方向盘和两个踏板)。
那么我们如何告诉编译器可以使用任何汽车呢?我们给他们一个共同的共同点:界面。
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();
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我们可以更全面地介绍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 */ }
}
Run Code Online (Sandbox Code Playgroud)
现在,我们需要创建一项需要跑步的专门运动,例如篮球。
public class BasketBallPlayer : Runner
{
public void ThrowBall() { /* throwing logic */ }
// Running is already inherited from Runner
}
Run Code Online (Sandbox Code Playgroud)
很好,还没有问题。但是现在,我们需要创建一个铁人三项课程,其中涉及所有三种运动(跑步,游泳,骑自行车)
public class Triathlete: Runner, Swimmer, Cyclist { ... }
Run Code Online (Sandbox Code Playgroud)
这就是编译器崩溃的地方。它拒绝允许您同时从多个基类继承。原因要比这个答案要深入得多,如果您想了解更多,请谷歌搜索。
但是,是否使用过接口:
public interface IRunner
{
void Run();
}
public interface ISwimmer
{
void Swim();
}
public interface ICyclist
{
void Cycle();
}
Run Code Online (Sandbox Code Playgroud)
然后,编译器将允许这样做:
public class Triathlete: IRunner, ISwimmer, ICyclist
{
public void Run() { /* running logic */ }
public void Swim() { /* swimming logic */ }
public void Cycle() { /* cycling logic */ }
}
Run Code Online (Sandbox Code Playgroud)
这就是为什么接口通常击败继承的原因(还有其他原因)。
界面总体上更好的原因还有很多,但这是最大的原因。如果您需要更多说明,请使用Google,我无法完全了解StackOverflow的答案。