Как я могу выполнить внедрение зависимостей, не нарушая инкапсуляцию?
Используя Пример внедрения зависимостей из Википедии :
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();
}
Но введение зависимости открывает целую банку с червями: открывая всю банку с червями.Внедрение зависимостей требует раскрытия всех деталей моей реализации private .Потребитель моего 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();
}
Как я могу использовать достоинства Inpendency 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,
...
}
Это действительно детали реализации, на которые я не хочу, чтобы клиент обращал внимание.