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

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

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

public Car {
    public float getSpeed();
}

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

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

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

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

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

Теперь допустим, я изменил внутреннюю деталь реализации моей машины:

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

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

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

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

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

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

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

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

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

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

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

См. также


Ради удовольствия, я могу сократить пример 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,
        ...
     }

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

32
задан Community 23 May 2017 в 12:01
поделиться