[英]Liskov Substitution Principle - Common Interface
I've been trying to figure out the Liskov Substitution Principle and Interface Segregation Principle and I'm a little confused with the following example. 我一直在尝试弄清楚Liskov替换原理和接口隔离原理,但我对以下示例有些困惑。
Assume that we have a base class called Vehicle
with a couple of properties and a interface IVehicle
which is implemented in the IVehicle
class. 假设我们有一个名为Vehicle
的基类,它具有几个属性和一个在IVehicle
类中实现的接口IVehicle
。
We have two child classes, Car
& Motorcycle
. 我们有两个孩子班, Car
和Motorcycle
。 Car
inherits from Vehicle and implements the IVehicle
interface. Car
继承自Vehicle,并实现了IVehicle
接口。 Motorcycle
inherits from Vehicle and implements the IVehicle
as well, but Motorcycle
has an extra property, which is also added in a new Interface IMotorcycle
that is implemented in the Motorcycle
class. Motorcycle
继承自Vehicle,并且也实现了IVehicle
,但是Motorcycle
具有额外的属性,该属性也添加到在Motorcycle
类中实现的新Interface IMotorcycle
中。
Let me clarify it by writing it down in code: 让我通过编写代码来澄清它:
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();
}
}
My questions: If we have a class say Start
which has a method that returns IVehicle
( public IVehicle DoSomeStuff()
). 我的问题:如果我们有一个说Start
的类,它有一个返回IVehicle
的方法( public IVehicle DoSomeStuff()
)。 Based on the logic we will either return a new Car
or Motorcycle
. 根据逻辑,我们将返回新的Car
或Motorcycle
。 If we return a new Car
we will be able to access all properties since it only implements the IVehicle
interface, but let's assume that we return a new Motorcycle
how will be able to access the .DateCreated
property without casting it, 如果我们返回新的Car
,则将能够访问所有属性,因为它仅实现了IVehicle
接口,但是我们假设我们返回了一个新的Motorcycle
,它将能够在不强制转换的情况下访问.DateCreated
属性,
Is there a way to implement this better to instead have a common interace or did I miss anything? 有没有一种方法可以更好地实现此目的,而不是拥有共同的接口,或者我错过了任何事情?
if you want to follow LSP, if you have a method that accepts an IVehicle parameter, it should not matter if you call it with a car or with a motorcycle. 如果要遵循LSP,如果您具有接受IVehicle参数的方法,则无论是用汽车还是摩托车来调用它都没关系。 if you need to cast or check if it is a motorcycle in any way, you did not design your interface(s) correctly. 如果您需要以任何方式投放或检查其是否为摩托车,则说明界面设计不正确。
So a few things that stand out with the idea behind this example. 因此,此示例背后的想法与众不同。 IMotorcycle
is not a IVehicle
which doesn't make sense in this example because the assumption is that there would be no class that would not inherit the functions from both IMotorcycle
and IVehicle
. IMotorcycle
不是在这个示例中没有意义的IVehicle
,因为假设是不会有任何类不能继承IMotorcycle
和IVehicle
的函数。 So changing IMotorcycle
to inherit from IVehicle
would make things more logical. 因此,将IMotorcycle
更改为继承自IVehicle
会使事情变得更合逻辑。 Next, you need to understand that LSP and ISP in to work with the other SOLID principles to work. 接下来,您需要了解LSP和ISP可以与其他SOLID原理一起工作。 The example doesn't satisfy the Single Responsibablity and Open-Closed Principle. 该示例不满足单一责任和开放-封闭原则。
The class Start
should not be modified after the logic has been added. 添加逻辑后,不应修改Start
类。 So if you added a new class that implemented IVehicle
and some other class/interface (that doesn't inherit from IVehicle
) then the logic would have to be changed meaning it's not closed to modifications. 因此,如果您添加了一个实现IVehicle
的新类和其他一些类/接口(该类/接口未继承自IVehicle
),则必须更改逻辑,这意味着它不能进行修改。 There should be overriden or overloaded methods in the Start
class so that you can use method forwarding to handle the different types. Start
类中应该有重写或重载的方法,以便您可以使用方法转发来处理不同的类型。
But there's also the problem of Single Responsibility not be applied to the class. 但是,还有一个问题是“单一责任”不适用于该班级。 If you want to handle general interactions with vehicles than an object that extends IVehicle
should passed in, but if you are creating/building an instance of IVehicle
there should be a factory/builder pattern applied. 如果要处理与车辆的一般交互作用, IVehicle
不应传入扩展IVehicle
的对象,但是如果要创建/构建IVehicle
实例, IVehicle
应用工厂/构建者模式。 Either way Start
class should be creating, managing, or interacting with IVehicle
objects not creating, managing, AND interacting with the objects. 无论哪种方式, Start
类都应该创建,管理或与IVehicle
对象进行交互, IVehicle
不是创建,管理与对象进行交互。 Whatever function is calling on the Start
class should also implementing SOLID meaning it's only expecting either a IMotorcycle
or IVehicle
object (with IMotorcycle
inheriting from IVehicle
.) That way the function has a single functionality, does not need to be updated after you add more classes. 无论在Start
类上调用什么函数,都应该实现SOLID,这意味着它仅期望IMotorcycle
或IVehicle
对象( IMotorcycle
继承自IVehicle
。)这样,该函数具有单个功能,在添加更多类后无需更新。
There's more to this but I feel I've given a lot of information already. 还有更多的东西,但是我觉得我已经提供了很多信息。 The main thing you should think about is that you need to have the SOLID principles work together, not in isolation. 您应该考虑的主要问题是,您需要让SOLID原则协同工作,而不是孤立地工作。 If you want to use type specific functions from one object after getting it from a method that returns the a higher level interface, then you need to make the higher level interface have functions that when implemented will call on the type specific functions. 如果要在从返回更高级别接口的方法中获取一个对象后使用某个对象的特定于类型的函数,则需要使高层接口具有在实现时将调用特定于类型的函数的函数。 In your case you could have something like this: 您的情况可能是这样的:
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; }
}
You would then have the information as an object that expects an limited amount of information. 然后,您会将信息作为需要有限信息量的对象。 This shouldn't mirror the hierarchy IVehicle
. 这不应该反映层次结构IVehicle
。 It is just an object that all vehicle should have and all vehicles can interact with and other type specific code could interact with for handling information that is specific to that that type. 这只是所有车辆都应该拥有的对象,所有车辆都可以与之交互,并且其他类型特定的代码也可以与之交互,以处理该类型特定的信息。 It's not a great example and it's just to be used as an general look at how you could possibility improve your interfaces because there needs to be more done to highlight the functionality if you plan on using IVehicle
through out your system. 这不是一个很好的例子,它只是作为一种通用方法来研究如何改进界面,因为如果计划在IVehicle
系统中使用IVehicle
要做更多的工作来突出功能。 Getter and Setters aren't the best think to make an interface for. Getter和Setters并不是创建接口的最佳方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.