【发布时间】:2018-04-25 17:58:21
【问题描述】:
我一直在试图弄清楚 Liskov 替换原则和接口隔离原则,我对下面的例子有点困惑。
假设我们有一个名为Vehicle 的基类,它有几个属性和一个在IVehicle 类中实现的接口IVehicle。
我们有两个子类,Car 和 Motorcycle。 Car 继承自 Vehicle 并实现 IVehicle 接口。 Motorcycle 继承自 Vehicle 并实现了 IVehicle,但 Motorcycle 有一个额外的属性,该属性也在 IMotorcycle 类中实现的新接口中添加。Motorcycle 类。
让我用代码写下来澄清一下:
public interface IVehicle
{
string Brand { get; set; }
string Model { get; set; }
int HorsePower { get; set; }
}
public interface IMotorcycle
{
DateTime DateCreated { get; set; }
}
public abstract class Vehicle : IVehicle
{
public string Brand { get; set; }
public string Model { get; set; }
public int HorsePower { get; set; }
}
public class Car : Vehicle, IVehicle
{ }
public class Motorcycle : Vehicle, IVehicle, IMotorcycle
{
public DateTime DateCreated { get; set; }
}
public class Start
{
public IVehicle DoSomeStuff()
{
//Does some stuff
//Based on logic we either return
//a new Car or Motorcycle
//but if I return a motorcycle how would I be able to
//access the DateCreated attribute since I'm returning IVehicle
//I guess I have to cast it but is it a good practice to do that
//or am I setting up everything incorrect?
return new Motorcycle();
}
}
我的问题:如果我们有一个类说Start,它有一个返回IVehicle(public IVehicle DoSomeStuff())的方法。根据逻辑,我们将返回一个新的Car 或Motorcycle。如果我们返回一个新的Car,我们将能够访问所有属性,因为它只实现了IVehicle 接口,但是假设我们返回一个新的Motorcycle,如何在不强制转换的情况下访问.DateCreated 属性它,
有没有办法更好地实现这一点,而不是有一个共同的交互,还是我错过了什么?
【问题讨论】:
-
“如何在不强制转换的情况下访问 DateCreated 属性”——你必须强制转换它。你可以说
if (rtnObj is IMotorcycle) { Console.WriteLine((rtnObj as IMotorcycle).DateCreated); }(C#7 中有一个更简单的习语)。这就是接口的用途:使用is或其他等效项来确定给定对象是否支持给定接口,如果支持,则进行转换。 -
一般的想法应该是调用者只关心车辆,而不是车辆的具体类型。如果他们需要了解更多信息,则可以选择使用多种方法来返回更具体的类型。或某种类型的通用方法。或者移动任何需要知道类内部类型的逻辑。
-
你的例子很糟糕。首先,因为你使用了一个人为的例子
DateCreated,所以不清楚为什么这个属性不能适用于所有IVehicles。因为如果确实如此,那么您可以简单地将属性移动到IVehicles。问题解决了。相反,如果您使用了NumberOfWheels,那么显然该属性属于IVehicle,并且可以在这两种情况下实现。也消除了问题。 -
然而,如果你想强制一些“不适合”的东西,那么你也不应该期望那些适合的东西和不适合的东西有多态性。您实际上是在寻求将“胖接口”强制转换为与 ISP 相反的多态行为的方法。
标签: c# oop interface design-principles