Wie wird Dependency Injection ohne Unterbrechung der Kapselung verwendet?

Wie kann ich eine Abhängigkeitsinjektion durchführen, ohne die Kapselung zu beschädigen?

Verwendung einerDependency Injection Beispiel aus Wikipedia:

public Car {
    public float getSpeed();
}

Hinweisndere Methoden und Eigenschaften (z. B. PushBrake (), PushGas (), SetWheelPosition ()) wurden aus Gründen der Übersichtlichkeit weggelasse

Das funktioniert gut; Sie wissen nicht, wie mein Objekt implementiertgetSpeed - es ist " gekapselt ".

In Wirklichkeit implementiert mein ObjektgetSpeed wie

public Car {
    private m_speed;
    public float getSpeed( return m_speed; );
}

nd alles ist gut. Jemand baut meinCar Objekt, zerdrückt Pedale, die Hupe, das Lenkrad und das Auto reagiert.

Jetzt können wir sagen, ich ändere ein internes Implementierungsdetail meines Autos:

public Car {
    private Engine m_engine;
    private float m_currentGearRatio;
    public float getSpeed( return m_engine.getRpm*m_currentGearRatio; );
}

Alles ist gut. DasCar folgt den richtigen OO-Prinzipien und verbirgt Details vonWi etwas ist erledigt. Dies gibt dem Anrufer die Freiheit, seine Probleme zu lösen, anstatt zu verstehen, wie ein Auto funktioniert. Es gibt mir auch die Freiheit, meine Implementierung nach Belieben zu ändern.

Aber Abhängigkeitsinjektion würde mich zwingen, meine Klasse einem @ auszusetzEngine Objekt, das ich nicht erstellt oder initialisiert habe. Noch schlimmer ist, dass ich jetzt ausgesetzt habe, dass meinCar sogarha ein Motor

public Car {
   public constructor(Engine engine);
   public float getSpeed();
}

Und jetzt ist dem äußeren Wort bewusst, dass ich ein @ benutEngine. Ich habe nicht immer einen Motor benutzt, ich möchte vielleicht kein @ benutzEngine in der Zukunft, aber ich kann meine interne Implementierung nicht mehr ändern:

public Car {
    private Gps m_gps;
    public float getSpeed( return m_gps.CurrentVelocity.Speed; )
}

ohne den Anrufer zu unterbrechen:

public Car {
   public constructor(Gps gps);
   public float getSpeed();
}

But Dependency Injection öffnet eine ganze Dose Würmer: Durch Öffnen der ganzen Dose Würmer. Für die Abhängigkeitsinjektion müssen alle meine ObjektePriva Implementierungsdetails werden offen gelegt. Der Verbraucher meinesCar class muss jetzt alle zuvor verborgenen internen Feinheiten meiner Klasse verstehen und damit umgehen:

public Car {
   public constructor(
       Gps gps, 
       Engine engine, 
       Transmission transmission,
       Tire frontLeftTire, Tire frontRightTire, Tire rearLeftTire, Tire rearRightTire, 
       Seat driversSeat, Seat passengersSeat, Seat rearBenchSeat,
       SeatbeltPretensioner seatBeltPretensioner,
       Alternator alternator, 
       Distributor distributor,
       Chime chime,
       ECM computer,
       TireMonitoringSystem tireMonitor
       );
   public float getSpeed();
}

Wie kann ich die Vorteile von Dependency Injection zum Testen von Einheiten nutzen, ohne die Vorteile der Kapselung zu beeinträchtigen, um die Benutzerfreundlichkeit zu verbessern?

Siehe auc Muss die Abhängigkeitsinjektion auf Kosten der Verkapselung gehen? (Muss, anstatt wie)

Für den Spaß kann ich das @ zuschneidgetSpeed Beispiel für genau das, was benötigt wird:

public Car {
   public constructor(
       Engine engine, 
       Transmission transmission,
       Tire frontLeftTire, Tire frontRightTire
       TireMonitoringSystem tireMonitor,
       UnitConverter unitsConverter
       );
   public float getSpeed()
   {
      float tireRpm = m_engine.CurrentRpm * 
              m_transmission.GetGearRatio( m_transmission.CurrentGear);

      float effectiveTireRadius = 
         (
            (m_frontLeftTire.RimSize + m_frontLeftTire.TireHeight / 25.4)
            +
            (m_frontRightTire.RimSize + m_frontRightTire.TireHeight / 25.4)
         ) / 2.0;

      //account for over/under inflated tires
      effectiveTireRadius = effectiveTireRadius * 
            ((m_tireMonitor.FrontLeftInflation + m_tireMontitor.FrontRightInflation) / 2.0);

      //speed in inches/minute
      float speed = tireRpm * effetiveTireRadius * 2 * Math.pi;

      //convert to mph
      return m_UnitConverter.InchesPerMinuteToMilesPerHour(speed);
   }
}

Aktualisieren Vielleicht kann eine Antwort dem Hinweis der Frage folgen und einen Beispielcode geben?

public Car {
    public float getSpeed();
}

Ein anderes Beispiel ist, wenn meine Klasse von einem anderen Objekt abhängt:

public Car {
    private float m_speed;
}

In diesem Fallfloat ist eine Klasse, die verwendet wird, um einen Gleitkommawert darzustellen. Nach dem, was ich gelesen habe, sollte jede abhängige Klasse injiziert werden - falls ich das @ verspotten möchfloat Klasse. Dies lässt das Gespenst aufkommen, jedes private Mitglied injizieren zu müssen, da im Grunde alles ein Objekt ist:

public Car {
    public Constructor(
        float speed,
        float weight,
        float wheelBase,
        float width,
        float length,
        float height,
        float headRoom,
        float legRoom,
        DateTime manufactureDate,
        DateTime designDate,
        DateTime carStarted,
        DateTime runningTime,
        Gps gps, 
        Engine engine, 
        Transmission transmission,
        Tire frontLeftTire, Tire frontRightTire, Tire rearLeftTire, Tire rearRightTire, 
        Seat driversSeat, Seat passengersSeat, Seat rearBenchSeat,
        SeatbeltPretensioner seatBeltPretensioner,
        Alternator alternator, 
        Distributor distributor,
        Chime chime,
        ECM computer,
        TireMonitoringSystem tireMonitor,
        ...
     }

Dies sind wirklich Implementierungsdetails, auf die der Kunde nicht achten muss.

Antworten auf die Frage(18)

Ihre Antwort auf die Frage