Как использовать Dependency Injection, не нарушая инкапсуляцию?

Как я могу выполнить внедрение зависимости без нарушения инкапсуляции?

ИспользуяПример внедрения зависимостей из Википедии:

public Car {
    public float getSpeed();
}

Замечания: Другие методы и свойства (например, PushBrake (), PushGas (), SetWheelPosition ()) опущены для ясности

Это хорошо работает; ты не знаешь, как реализуется мой объектgetSpeed - это "инкапсулированный».

На самом деле мой объект реализуетgetSpeed как:

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

И все хорошо. Кто-то конструирует мойCar объект, давит педали, клаксон, руль, и машина реагирует.

Теперь допустим, что я изменил внутреннюю деталь реализации моего автомобиля:

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

Все хорошо.Car придерживается надлежащих ОО-принципов, скрывая деталикак что-то сделано Это освобождает звонящего от решения его проблем, а не пытается понять, как работает автомобиль. Это также дает мне свободу изменять мою реализацию по своему усмотрению.

Но внедрение зависимости заставило бы меня выставить мой классEngine объект, который я не создал или не инициализировал. Еще хуже то, что я теперь показал, что мойCar четноеимеет двигатель:

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

И теперь внешнее слово знает, что я используюEngine, Я не всегда использовал двигатель, я могу не использоватьEngine в будущем, но я больше не могу изменить свою внутреннюю реализацию:

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

не нарушая звонящего:

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

Но инъекция зависимости открывает целую банку с червями: открывая целую банку с червями. Внедрение зависимостей требует, чтобы все мои объектычастный детали реализации будут раскрыты. Потребитель моегоCar Теперь класс должен понять и разобраться со всеми ранее скрытыми внутренними сложностями моего класса:

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();
}

Как я могу использовать преимущества Dependency Injection, чтобы помочь юнит-тестированию, не нарушая преимущества инкапсуляции для удобства использования?

Смотрите такжеНужно ли вводить зависимость за счет инкапсуляции? (Должен, а не как)

Ради забавы я могу урезатьgetSpeed пример того, что нужно:

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);
   }
}

Обновить: Возможно, какой-нибудь ответ может последовать за вопросом и дать пример кода?

public Car {
    public float getSpeed();
}

Другой пример, когда мой класс зависит от другого объекта:

public Car {
    private float m_speed;
}

В этом случаеfloat это класс, который используется для представления значения с плавающей точкой. Из того, что я прочитал, каждый зависимый класс должен быть введен - в случае, если я хочу издеваться надfloat учебный класс. Это порождает призрак необходимости вводить каждый закрытый член, поскольку все по сути является объектом:

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,
        ...
     }

Это действительно детали реализации, на которые я не хочу, чтобы клиент обращал внимание.

Ответы на вопрос(9)

Ваш ответ на вопрос