很抱歉向sich提出一个通用的问题,但是我一直在研究这些问题,除了头部编程之外传达什么成员必须在课堂上,我只是看不到任何好处.
面向对象编程有两个(基本)部分,给新手带来麻烦; 第一个是inheritance,第二个是composition.这些是最难得到的; 一旦你理解了其他一切就更容易了.
你所指的是composition- 例如,一堂课做什么?如果你继承路由,它派生自一个抽象类(比如DogIS A Animal).如果您使用组合,那么您正在制定合同(A Car HAS A Driver/Loan/Insurance).任何实现您的接口的人都必须实现该接口的方法.
这允许松耦合; 并且不会将您绑定到不适合的继承模型中.
继承适合的地方,使用它; 但如果两个类之间的关系本质上是契约性的,或者是HAS-A对IS-A,那么使用接口来模拟该部分.
为什么使用接口?
举一个实际的例子,让我们跳进一个商业应用程序.如果你有一个存储库; 您将要在存储库上方创建接口的层.这样,如果您必须以存储库的工作方式更改任何内容,则不会影响任何内容,因为它们都遵循相同的合同.
这是我们的存储库:
public interface IUserRepository
{
public void Save();
public void Delete(int id);
public bool Create(User user);
public User GetUserById(int id);
}
Run Code Online (Sandbox Code Playgroud)
现在,我可以在类中实现该Repository:
public class UserRepository : IRepository
{
public void Save()
{
//Implement
}
public void Delete(int id)
{
//Implement
}
public bool Create(User user)
{
//Implement
}
public User GetUserById(int id)
{
//Implement
}
}
Run Code Online (Sandbox Code Playgroud)
这将接口与调用它分开.我可以将这个类从Linq-To-SQL更改为内联SQL或存储过程,只要我实现了IUserRepository接口,就没有人会更聪明; 最重要的是,没有任何课程来自我的课程,可能会对我的变化感到生气.
传承与构成:最好的朋友
继承和组合旨在解决不同的问题.在每个适合的地方使用它们,并且在使用它们时存在完整的问题子集.
我打算让乔治指出你现在可以使用界面而不是具体的类.这里的每个人似乎都明白界面是什么以及如何定义它们,但是大多数都未能以学生容易掌握的方式解释它们的关键点 - 大多数课程都没有指出,而是让你去掌握在吸管或为自己弄清楚所以我会尝试以一种不需要的方式拼出来.所以希望你不会想到"那么什么,它似乎浪费时间/精力/代码."
public interface ICar
{
public bool EngineIsRunning{ get; }
public void StartEngine();
public void StopEngine();
public int NumberOfWheels{ get; }
public void Drive(string direction);
}
public class SportsCar : ICar
{
public SportsCar
{
Console.WriteLine("New sports car ready for action!");
}
public bool EngineIsRunning{ get; protected set; }
public void StartEngine()
{
if(!EngineIsRunning)
{
EngineIsRunning = true;
Console.WriteLine("Engine is started.");
}
else
Console.WriteLine("Engine is already running.");
}
public void StopEngine()
{
if(EngineIsRunning)
{
EngineIsRunning = false;
Console.WriteLine("Engine is stopped.");
}
else
Console.WriteLine("Engine is already stopped.");
}
public int NumberOfWheels
{
get
{
return 4;
}
}
public void Drive(string direction)
{
if (EngineIsRunning)
Console.WriteLine("Driving {0}", direction);
else
Console.WriteLine("You can only drive when the engine is running.");
}
}
public class CarFactory
{
public ICar BuildCar(string car)
{
switch case(car)
case "SportsCar" :
return Activator.CreateInstance("SportsCar");
default :
/* Return some other concrete class that implements ICar */
}
}
public class Program
{
/* Your car type would be defined in your app.config or some other
* mechanism that is application agnostic - perhaps by implicit
* reference of an existing DLL or something else. My point is that
* while I've hard coded the CarType as "SportsCar" in this example,
* in a real world application, the CarType would not be known at
* design time - only at runtime. */
string CarType = "SportsCar";
/* Now we tell the CarFactory to build us a car of whatever type we
* found from our outside configuration */
ICar car = CarFactory.BuildCar(CarType);
/* And without knowing what type of car it was, we work to the
* interface. The CarFactory could have returned any type of car,
* our application doesn't care. We know that any class returned
* from the CarFactory has the StartEngine(), StopEngine() and Drive()
* methods as well as the NumberOfWheels and EngineIsRunning
* properties. */
if (car != null)
{
car.StartEngine();
Console.WriteLine("Engine is running: {0}", car.EngineIsRunning);
if (car.EngineIsRunning)
{
car.Drive("Forward");
car.StopEngine();
}
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,我们可以定义任何类型的汽车,只要该汽车实现接口ICar,它将具有我们可以从我们的主应用程序调用的预定义属性和方法.我们不需要知道什么类型的汽车 - 甚至是从CarFactory.BuildCar()方法返回的类的类型.它可以为我们所关心的所有人返回一个类型为"DragRacer"的实例,我们需要知道的是DragRacer实现了ICar,我们可以像往常一样继续生活.
在现实世界的应用程序中,想象一下IDataStore,我们的具体数据存储类提供对磁盘上数据存储的访问,或者在网络上,某些数据库,拇指驱动器,我们不关心什么 - 我们所关心的只是具体的从我们的类工厂返回的类实现了接口IDataStore,我们可以调用方法和属性,而无需了解类的底层架构.
另一个现实世界的含义(至少对于.NET)是,如果编写跑车类的人对包含跑车实施并重新编译的库进行更改,并且您已经对其库进行了硬性引用,则需要重新编译 - 如果你已经针对ICar编写了你的应用程序,你可以用他们的新版本替换DLL,你可以正常进行.