Note that there are some explanatory texts on larger screens.

plurals
  1. POHow to use Dependency Injection without breaking encapsulation?
    primarykey
    data
    text
    <p>How can i perform dependency injection without breaking encapsulation?</p> <p>Using a <a href="http://en.wikipedia.org/wiki/Dependency_injection" rel="nofollow noreferrer">Dependency Injection example from Wikipedia</a>:</p> <pre><code>public Car { public float getSpeed(); } </code></pre> <blockquote> <p><strong>Note:</strong> Other methods and properties (e.g. PushBrake(), PushGas(), SetWheelPosition() ) omitted for clarity</p> </blockquote> <p>This works well; you don't know how my object implements <code>getSpeed</code> - it is "<em>encapsulated</em>".</p> <p>In reality my object implements <code>getSpeed</code> as:</p> <pre><code>public Car { private m_speed; public float getSpeed( return m_speed; ); } </code></pre> <p>And all is well. Someone constructs my <code>Car</code> object, mashes pedals, the horn, the steering wheel, and the car responds.</p> <p>Now lets say i change an internal implementation detail of my car:</p> <pre><code>public Car { private Engine m_engine; private float m_currentGearRatio; public float getSpeed( return m_engine.getRpm*m_currentGearRatio; ); } </code></pre> <p>All is well. The <code>Car</code> is following proper OO-principles, hiding details of <em>how</em> something is done. This frees the caller to solve his problems, rather than trying to understand how a car works. It also gives me the freedom to change my implementation as i see fit.</p> <p>But dependency injection would force me to expose my class to an <code>Engine</code> object that i didn't create or initialize. Even worse is that I've now exposed that my <code>Car</code> even <strong>has</strong> an engine:</p> <pre><code>public Car { public constructor(Engine engine); public float getSpeed(); } </code></pre> <p>And now the outside word is aware that i use an <code>Engine</code>. I didn't always use an engine, i may want to not use an <code>Engine</code> in the future, but i can no longer change my internal implementation:</p> <pre><code>public Car { private Gps m_gps; public float getSpeed( return m_gps.CurrentVelocity.Speed; ) } </code></pre> <p>without breaking the caller:</p> <pre><code>public Car { public constructor(Gps gps); public float getSpeed(); } </code></pre> <p>But dependency injection opens a whole can of worms: by opening the whole can of worms. Dependency Injection requires that all my objects <em>private</em> implementation details be exposed. The consumer of my <code>Car</code> class now has to understand, and deal with, all of the previously hidden internal intricacies of my class:</p> <pre><code>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(); } </code></pre> <p>How can i use the virtues of Dependency Injection to help unit testing, while not breaking the virtues of encapsulation to help usability?</p> <h2>See also</h2> <ul> <li><a href="https://stackoverflow.com/questions/1005473/must-dependency-injection-come-at-the-expense-of-encapsulationclarity">Must Dependency Injection come at the expense of Encapsulation?</a> (Must, rather than how)</li> </ul> <hr> <p>For the sake of fun, i can trim down the <code>getSpeed</code> example to just what is needed:</p> <pre><code>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); } } </code></pre> <hr> <p><strong>Update:</strong> Perhaps some answer can follow the question's lead, and give sample code?</p> <pre><code>public Car { public float getSpeed(); } </code></pre> <hr> <p>Another example is when my class depends on another object:</p> <pre><code>public Car { private float m_speed; } </code></pre> <p>In this case <code>float</code> is a class that is used to represent a floating-point value. From what i read, every dependant class should be injected - in case i want to mock the <code>float</code> class. This raises the spectre of having to inject every private member, since everything is fundamentally an object:</p> <pre><code>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, ... } </code></pre> <p>These really are implementation details that i don't want the customer to have to look at. </p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload