[英]Liskov Substitution Principle - Common Interface
我一直在尝试弄清楚Liskov替换原理和接口隔离原理,但我对以下示例有些困惑。
假设我们有一个名为Vehicle
的基类,它具有几个属性和一个在IVehicle
类中实现的接口IVehicle
。
我们有两个孩子班, Car
和Motorcycle
。 Car
继承自Vehicle,并实现了IVehicle
接口。 Motorcycle
继承自Vehicle,并且也实现了IVehicle
,但是Motorcycle
具有额外的属性,该属性也添加到在Motorcycle
类中实现的新Interface IMotorcycle
中。
让我通过编写代码来澄清它:
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
属性,
有没有一种方法可以更好地实现此目的,而不是拥有共同的接口,或者我错过了任何事情?
如果要遵循LSP,如果您具有接受IVehicle参数的方法,则无论是用汽车还是摩托车来调用它都没关系。 如果您需要以任何方式投放或检查其是否为摩托车,则说明界面设计不正确。
因此,此示例背后的想法与众不同。 IMotorcycle
不是在这个示例中没有意义的IVehicle
,因为假设是不会有任何类不能继承IMotorcycle
和IVehicle
的函数。 因此,将IMotorcycle
更改为继承自IVehicle
会使事情变得更合逻辑。 接下来,您需要了解LSP和ISP可以与其他SOLID原理一起工作。 该示例不满足单一责任和开放-封闭原则。
添加逻辑后,不应修改Start
类。 因此,如果您添加了一个实现IVehicle
的新类和其他一些类/接口(该类/接口未继承自IVehicle
),则必须更改逻辑,这意味着它不能进行修改。 Start
类中应该有重写或重载的方法,以便您可以使用方法转发来处理不同的类型。
但是,还有一个问题是“单一责任”不适用于该班级。 如果要处理与车辆的一般交互作用, IVehicle
不应传入扩展IVehicle
的对象,但是如果要创建/构建IVehicle
实例, IVehicle
应用工厂/构建者模式。 无论哪种方式, Start
类都应该创建,管理或与IVehicle
对象进行交互, IVehicle
不是创建,管理与对象进行交互。 无论在Start
类上调用什么函数,都应该实现SOLID,这意味着它仅期望IMotorcycle
或IVehicle
对象( IMotorcycle
继承自IVehicle
。)这样,该函数具有单个功能,在添加更多类后无需更新。
还有更多的东西,但是我觉得我已经提供了很多信息。 您应该考虑的主要问题是,您需要让SOLID原则协同工作,而不是孤立地工作。 如果要在从返回更高级别接口的方法中获取一个对象后使用某个对象的特定于类型的函数,则需要使高层接口具有在实现时将调用特定于类型的函数的函数。 您的情况可能是这样的:
public interface IVehicle
{
string Brand { get; set; }
string Model { get; set; }
int HorsePower { get; set; }
void setInformation;
InformObject getInformation;
}
public interface IMotorcycle : IVehicle
{
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 void setInformation(string brand, string model, int hPower){
Brand = brand;
Model = model;
HorsePower = hPower;
}
public InfoObject getInformation(){
return new InfoObject(Brand, Model, HorsePower);
}
}
public class Car : Vehicle, IVehicle
{ }
public class Motorcycle : Vehicle, IVehicle, IMotorcycle
{
public DateTime DateCreated { get; set; }
}
public class InfoObject
{
public InfoObject(string brand, string model, int hPower){
Brand = brand;
Model = model;
HorsePower = hPower;
}
public InfoObject(string brand, string model, int hPower, DateTime timeCreated){
Brand = brand;
Model = model;
HorsePower = hPower;
DateCreated = timeCreated;
}
public string Brand { get; set; }
public string Model { get; set; }
public int HorsePower { get; set; }
DateTime DateCreated { get; set; }
}
然后,您会将信息作为需要有限信息量的对象。 这不应该反映层次结构IVehicle
。 这只是所有车辆都应该拥有的对象,所有车辆都可以与之交互,并且其他类型特定的代码也可以与之交互,以处理该类型特定的信息。 这不是一个很好的例子,它只是作为一种通用方法来研究如何改进界面,因为如果计划在IVehicle
系统中使用IVehicle
要做更多的工作来突出功能。 Getter和Setters并不是创建接口的最佳方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.