Tradycyjne podejście komponenty wysokopoziomowe zależą od niskopoziomowych. Przykładowy kod:
public class AutomaticWashingMachine { public void StartWashingCycle() { var engine = new Engine(); var heater = new Heater(); //all other required objects //code to process washing cycle } } public class Heater { private TemperatureSensor temperatureSensor; public Heater() { temperatureSensor = new TemperatureSensor(); } public void HeatWater(double requiredTemperature) { while(Math.Abs(requiredTemperature - temperatureSensor.GetCurrentTemperatur()) < 0.1) { Console.WriteLine("Rising water temperature..."); } } } public class TemperatureSensor { private double currentWaterTemperature = 10; public double GetCurrentTemperatur() { return currentWaterTemperature += 1; } } public class Engine { public void On() { Console.WriteLine("I turn the drum..."); } public void Off() { Console.WriteLine("Stoping engine..."); } }
Powyżej przykład kodu oprogramowującego działanie pralki automatycznej. Powyższy kod nie jest zgodny z zasadą DIP. Oprócz tego posiada następujące wady:
- trudno napisać do niego testy jednostkowe. Wynika to z faktu, że aby przetestować przycisk włączający pralkę należy stworzyć cały system - nie możemy zaślepić chwilowo grzałki i silnika (Mocki).
- stałe zależności w kodzie, trudno będzie wymienić czujnik temperatury na inny, gdyż obecny jest mocno powiązany z grzałką.
Powyższy kod można w bardzo łatwy sposób przerobić do kodu opartego o abstrakcję (interfejsy). Zobaczmy jak może to wyglądać:
public class AutomaticWashingMachine { private readonly IEngine engine; private readonly IHeater heater; public AutomaticWashingMachine(IEngine engine, IHeater heater) { this.engine = engine; this.heater = heater; } public void StartWashingCycle() { //all other required objects //code to process washing cycle } } public interface IHeater { void HeatWater(double requiredTemperature); } public class Heater : IHeater { private readonly ITemperatureSensor temperatureSensor; public Heater(ITemperatureSensor temperatureSensor) { this.temperatureSensor = temperatureSensor; } public void HeatWater(double requiredTemperature) { while (Math.Abs(requiredTemperature - temperatureSensor.GetCurrentTemperatur()) < 0.1) { Console.WriteLine("Rising water temperature..."); } } } public interface ITemperatureSensor { double GetCurrentTemperatur(); } public class TemperatureSensor : ITemperatureSensor { private double currentWaterTemperature = 10; public double GetCurrentTemperatur() { return currentWaterTemperature += 1; } } public interface IEngine { void On(); void Off(); } public class Engine : IEngine { public void On() { Console.WriteLine("I turn the drum..."); } public void Off() { Console.WriteLine("Stoping engine..."); } }
Powyższy kod zawiera sporo nowych interfejsów: IEngine, IHeater, ITemperatureSensor. Dzięki zastosowaniu interfejsów nie wiążemy poszczególnych modułów/klas z pralką automatyczną. Każdy komponent jest wymienny tak długo jak implementuje zadany interfejs. Takie podejście ułatwia implementowanie testów jednostkowych. W przypadku gdy chcemy wymienić jeden z komponentów na inny, także możemy to w prosty sposób zrobić bez konieczności przerabiania tony kodu.
Brak komentarzy:
Prześlij komentarz