基类A具有基类B的性质,但派生类A1具有派生类B1

本文关键字:基类 派生 B1 A1 | 更新日期: 2023-09-27 18:24:01

我得到了以下真实世界的模式,其中基类BCA持有基类类型BCB的公共属性BBCABCB分别有两个派生类——DCA1DCA2DCB1DCB2。在DCA1DCA2中的每一个中,真实世界中的属性实际上分别为DCB1DCB2类型。

如何最有效地将其转化为代码OOP设计?我是在BCA中保留B,还是在DCA1DCA2中分别具体键入?我应该使用仿制药吗?这个设计有什么问题吗?碰巧我在这个项目中已经遇到过几次了,不知怎么的,感觉不对劲;但我想知道我的想法是不是错了。

编辑:我举了一个更具体的例子,使它更清楚。我很抱歉,但我不允许张贴图片在类图上显示它,但它是这样的:

Pet具有PetFood类型的DailyMeal属性。Cat’s DailyMeal属性属于CatFood类型,Dog’s Daily Meal属于DogFood类型。

我希望这能使模式更加清晰。

第2版:这个问题被重新表达为:你如何用OOP术语,特别是C#来建模这样一个真实世界的场景,在这个场景中,你有一辆带发动机的汽车、一辆带电动发动机的电动汽车和一辆带柴油发动机的柴油车等,这样你就可以使用多态性:

Car myCar = myElectricCar;

myCar.Engine = myElectricEngine;

同时确保每种汽车类型都具有正确的发动机类型。我们的目标是根据类、接口和/或泛型或任何需要来建模这个真实世界的场景,并获得OOP的全部好处。

基类A具有基类B的性质,但派生类A1具有派生类B1

让我们使用以下示例:所有汽车都有发动机,但电动汽车有电动发动机。

你不能用类来设计它,但你可以用协变接口:

interface ICar<out TEngine>
{
    TEngine Engine { get; }
}
class Engine { }
class Car : ICar<Engine>
{
    private readonly Engine engine;
    public Car(Engine engine)
    {
        this.engine = engine;
    }
    public Engine Engine { get { return this.engine; } }
}
class ElectricEngine : Engine { }
class ElectricCar : ICar<ElectricEngine>
{
    private readonly ElectricEngine engine;
    public Car(ElectricEngine engine)
    {
        this.engine = engine;
    }
    public ElectricEngine Engine { get { return this.engine; } }
}

但是请注意,ElectricCar并不是从Car继承的。然而,ICar<ElectricEngine>可分配给ICar<Engine>,因此这是有效的:

ICar<Engine> myCar = new ElectricCar(new ElectricEngine());

当然,您也可以定义一个非通用接口ICar,它包含与引擎无关的所有属性和方法。

注意,ICar<out TEngine>是协变的。它也不能是相反的。这意味着Engine属性必须是只读的。

更新

您询问了为什么属性必须是只读的,以及它的可写性是否存在固有的问题。是的,会的。考虑这个场景:

ICar<Engine> myCar = new ElectricCar();
// assume ICar<Engine> has a writabel property Engine of type Engine
myCar.Engine = new DieselEngine();

Engine类型的可写属性必须允许最后一条语句。但在运行时,这当然不能工作,因为myCar恰好是一辆电动汽车,而这些汽车不使用DieselEngines。

我建议你读一下利斯科夫替代原则。

ElectricCar可以具有类型为ElectricEngine的可写Engine属性,其setter不能是ICar<TEngine>接口的一部分。因此,属性的setter不能是多态的。

使用泛型。使BCA在其属性类型中具有泛型,并将其约束为从BCB:派生的

class BCA<T> where T: BCB
{
    public T prop {get; set;}
}