【问题标题】:How can I make a polymorphic method with different signatures如何制作具有不同签名的多态方法
【发布时间】:2023-04-14 10:23:02
【问题描述】:

考虑我有一些 抽象 Vehicle 类和汽车、卡车、摩托车抽象类,它们派生自 Vehicle。还想象一下,我必须能够为卡车和摩托车创造一辆基于燃料的汽车或电动汽车等等。 (具体类)

两个问题:

1.考虑我想在不知道它是什么的情况下以多态方式在车辆中填充能量。例如,如果车辆是基于燃料的,我想用燃料填充它,该方法应该使用 3 个参数:
void FillUpEnergy(EfuelType i_fuelType,int amounOfEnergy, int maxAmountOfEnergy)

但是对于基于电动的车辆,我需要几乎相同的功能签名,但这次当然没有燃料类型,例如(2 个参数):

void FillUpEnergy(int amounOfEnergy, int maxAmountOfEnergy) 

我可以在上述约束条件下使用多态FillUpEnergy 方法吗? (不同方法的签名)

2.在我的实现中,所有具体类都包含Engine(另一个抽象类)的引用,它代表FuelEngineElectricEngine(我拥有的其他从引擎派生的具体类)。例如,我有一个名为 ElectricCar 的具体类,它包含对 ElectricEngine 的引用。
这种架构足够好还是有更好的方法来实现车库系统? (在面向对象设计等方面。)

【问题讨论】:

  • 我不太明白 1) 至于 2),它被称为 Bridge,它是一种设计模式。
  • 桥接模式也是我的第一个想法。

标签: c# oop inheritance polymorphism


【解决方案1】:

关于问题1)

您可以将电动/汽油作为燃料类型的一部分,并在您的域逻辑中处理它。

C# 不提供具有不同签名的多态性。

2) 称为组合

【讨论】:

    【解决方案2】:
    1. 您不能这样做,因为这完全违反了封装。
    2. 我不明白你关于引擎的问题,但我可以肯定地说,可能有很多更好的方法来实现“车库系统”,因为有很多不同的“车库系统”。这实际上意味着您不应该尝试为您的系统建模(根据 OOP 或任何其他术语),直到您很好地掌握了您的需求。

    【讨论】:

      【解决方案3】:

      ElectricCarFueledCar 的区别是什么?只有引擎(概念上):

      interface IEngine
      {
          void FillUpFuel(int amountOfFuel, int maxAmountOfFuel);
      }
      
      class ElectricEngine : IEngine
      {
          public void FillUpFuel(int amountOfFuel, int maxAmountOfFuel) { ... }
      }
      
      abstract class Vehicle
      {
          public abstract IEngine Engine { get; }
      }
      
      class Car : Vehicle
      {
          public IEngine _engine;
          public override IEngine Engine { get { return _engine; } }
      
          public Car(IEngine engine)
          {
              _engine = engine;
          }
      }
      ...
      var electricCar = new Car(new ElectricEngine());
      electricCar.Engine.FillUpFuel(40, 70);
      

      典型的组合与继承示例。命名有点奇怪,因为 ElectricEngine 会加满燃料……但这不是重点。

      【讨论】:

      • 在这种情况下 Car 是聚合根。正如得墨忒耳法则所说,你永远不应该单独访问 Engine,只能通过 Car 对象。封装在这里完全被打破了。
      【解决方案4】:

      关于 1)
      拥有FillUpEnergy 多态(subtype polymorphism)的目的是当您唯一知道的是对象是Vehicle 时能够调用此方法。

      如果你需要知道确切的类型来选择正确的参数集,那么他们不需要这个函数是多态的。

      关于 2)
      没有什么令人震惊的

      【讨论】:

        【解决方案5】:

        你不能用不同的签名创建一个多态的“push-style”方法,但是你可以使用广为人知的Visitor Pattern来创建一个多态的“pull-style”方法。

        这个想法是颠倒交互顺序,让汽车对象决定做什么:不要调用FillUpEnergy汽车你认为它需要的东西,调用FillUpEnergy让汽车采取它知道自己需要什么,像这样:

        interface IEnergyProvider {
            void TakeFuel(EfuelType i_fuelType, int amounOfEnergy);
            void TakeElectricity(int amounOfEnergy);
        }
        interface ICar {
            void FillUpEnergy(IEnergyProvider provider);
        }
        

        现在你的多态方法的签名是固定的,但是方法的调度需要两条腿而不是一条腿:

        • 您拨打myCar.FillUpEnergy(myProvider)
        • 汽车呼叫myProvider.TakeFuelmyProvider.TakeElectricity

        【讨论】:

        • 不错的答案!想念旧头像
        • 但是这里的访客不是很复杂吗?关于取电和取任何其他燃料听起来像是同一个概念......
        • @Sebastian 您对访问者模式的看法是绝对正确的:它很容易失控,尤其是在您需要保持可扩展性的情况下。但是在可以预先指定层次结构的情况下,比如这个,这应该不是什么大问题:只需要几种方法,在你的脑海中遍历交互仍然很容易。至于加油和加油是同一个概念,我认为设计通过提供单一的FillUpEnergy 方法反映了这一点。