简体   繁体   English

Liskov替代原理-通用接口

[英]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 . 我们有两个孩子班, CarMotorcycle 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 . 根据逻辑,我们将返回新的CarMotorcycle 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 ,因为假设是不会有任何类不能继承IMotorcycleIVehicle的函数。 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,这意味着它仅期望IMotorcycleIVehicle对象( 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM