Single Responsibility Principle – SRP – aplikacja

Single Responsibility Principle – SRP – aplikacja

Dobrze tak jak już powiedziałem wystarczy już tych rozważań teoretycznych tego nudzenia i przejdźmy do praktycznego zastosowanie a wszystko stanie się jasne i proste, a najlepszy jest praktyczny, życiowy przykład !!!

I zamiast wykorzystywać przykłady, które nie mają żadnego związku z rzeczywistymi aplikacjami biznesowymi, ten kurs będzie pracować z aplikacją opartą na rzeczywistej domenie myjni samochodowej. Ten kod jest oczywiście bardzo prosty, tak prosty jak to tylko możliwe ale ma oczywiście wystarczającą złożoność, aby dobrze działać na potrzeby tego kursu i zrozumieć zasady SOLID.

Do tego celu, stworzyłem aplikację konsolową. Nasza aplikacja odczytuje dane wejściowe w postaci pliku tekstowego. Odczytuje szczegóły mycia z pliku i tworzy wycenę mycia na podstawie tych szczegółów stosując do nich niestandardową logikę biznesową.

Jak spojrzymy na klasę CarWash, jak myślicie, ile ma obowiązków?
Zastanówcie się, wrócimy do tego później !!!

/// <summary>
/// CarWash odczytuje szczegóły mycia z pliku i tworzy wycene mycia na podstawie szczegółów.
/// </summary>
public class CarWash
{
   public decimal WashingCost { get; set; } 

   public void Pricing()
   {
      Console.WriteLine("Starting pricing.");
      Console.WriteLine("Loading details.");

      string detailsJson = File.ReadAllText("details.json");

      var details = JsonConvert.DeserializeObject<Details>(detailsJson, new StringEnumConverter());

      switch (details.WashingType)
      {
         case WashingType.Standard:
            Console.WriteLine("Valuation for a standartd program.");
            Console.WriteLine("Valuation rules.");
            if (String.IsNullOrEmpty(details.Make))
            {
               Console.WriteLine("Car make must be stated.");
               return;
            }
            decimal baseWashingCost = 20 ;
            if (details.Make == "Ferrari")
            {
               baseWashingCost = baseWashingCost * 3;
            }
            baseWashingCost += details.Rinsing;
            baseWashingCost += details.Drying;
            WashingCost = baseWashingCost;                   
            break;

         case WashingType.StandardPlus:
            Console.WriteLine("Valuation for a standartd plus program.");
            Console.WriteLine("Valuation rules.");
            if (String.IsNullOrEmpty(details.Make))
            {
               Console.WriteLine("Car make must be stated");
               return;
            }
            if (details.VacuumingInside == 0 || details.WashingInside == 0)
            {
               Console.WriteLine("Standard Plus must specify Vacuuming Inside and Washing Inside.");
               return;
            }
            baseWashingCost = 25;
            if (details.Make == "Ferrari")
            {
               baseWashingCost = baseWashingCost * 3;
            }
            if (details.Make == "Ford")
            {
               baseWashingCost = baseWashingCost * 1.5m;
            }
            baseWashingCost += details.VacuumingInside;
            baseWashingCost += details.WashingInside;
            WashingCost = baseWashingCost;
            break;

         case WashingType.Waxing:
            Console.WriteLine("Valuation for a waxing program.");
            Console.WriteLine("Valuation rules.");
            baseWashingCost = 40;
            if (details.Double)
            {
               baseWashingCost = baseWashingCost * 3;
            }
            WashingCost = baseWashingCost;
            break;

            default:
               Console.WriteLine("Unknown type of Washing.");
               break;
      }

      Console.WriteLine("Pricing completed.");
   }
}

Zaczniemy od przyjrzenia się klasie Program i metodzie Main.

class Program
{
   static void Main(string[] args)
   {
      Console.WriteLine("Car Wash Starting...");

      var carWash = new CarWash();
      carWash.Pricing();

      if (carWash.WashingCost > 0)
      {
         Console.WriteLine($"Washing Cost: {carWash.WashingCost}");
      }
      else
      {
         Console.WriteLine("No cost.");
      }

      Console.WriteLine("Car Wash Ending...");
      Console.WriteLine("See you soon!");
   }
}

Co tutaj robimy. Tworzy nowy typ CarWash / Myjnia i wywołujemy metodę Pricing / Wycena. Wynik metody Pricing / Wycena ustawia właściwości WashingCost / Koszt Mycia. I Jeśli WashingCost/ Koszt Mycia jest większy niż 0, wyświetlamy ten koszt, w przeciwnym razie wyświetlamy że nie ma kosztu mycia.

Patrząc na klasę CarWash / Myjnia, ma ona tylko jedną metodę Pricing / Wycena. Co robi ta metoda zaczyna od wyświetlenia kilku rzeczy na konsoli. Następnie wczytuje szczegóły wyceny mycia z pliku json, a następnie deserializuje ten plik. Następnie w instrukcji switch określa, jaki jest rodzaj mycia. Aplikacja obecnie obsługuje 3 rodzaje mycia.

Możemy tutaj zobaczyć takie rodzaje mycia jak: Standard ,StandartPlus, Waxing / Woskowanie.

W przypadku, gdy jest to Standard, zaczynamy od przeprowadzenia weryfikacji czy została podana Marka samochodu gdyż jest ona niezbędna… Następnie wykonywane są określone reguły biznesowe, aby określić wycenę mycia. Oczywiście w prawdziwej aplikacji spodziewalibyśmy się, że będzie trochę więcej szczegółów niż tutaj ale nam tutaj chodzi o prostotę i o wyjaśnienie zasad SOLID.
Oczywiście ta logika się znacznie rozbuduje w trakcie kursu…

Następnie mamy StandardPlus, zaczynamy od przeprowadzenia weryfikacji czy została podana Marka samochodu gdyż jest ona niezbędna i dodatkowe weryfikacje … Następnie wykonywane są określone reguły biznesowe, aby określić wycenę mycia.

Następnie mamy Waxing i tutaj nie jest wykonywana żadna weryfikacja tylko wykonywane są założone reguły biznesowe, aby określić wycenę mycia.

Na koniec. Jeśli aplikacja nie rozpozna typu mycia, po prostu zarejestruje ten fakt wyświetleniem komunikatu na konsolę. Przy przeprowadzeniu wyceny mycia system zapisuje wycenę w właściwości WashingCost / Koszt Mycia.

Jak widzimy jest to dość prosta aplikacja.

Mam jednak nadzieję, że posiada wystarczająco dużo logika biznesowej i łączy ją z prostotą, aby można było zrozumieć zasady SOLID. Na potrzeby tego kursu usunąłem całą dodatkową złożoność, która istniała by w prawdziwej aplikacji a zaciemniała by zrozumienie zasad SOLID, abyśmy mogli po prostu zobaczyć, jak zastosować te zasady w łatwy do zrozumienia sposób. Nie komplikować nie potrzebnie, chodzi przecież o dobre zrozumienie zasad SOLID!

Teraz, nawet z tak prostą aplikacją ​​mam nadzieję, że znalazłeś w niej więcej niż kilka obowiązków klasy RatingEngine.

Na początku tego wpisu zadałem pytanie. Jak myślicie, ile ma obowiązków klasa CarWash?

Zastanówcie się, wrócimy do tego w następnym wpisie gdzie zastosujemy Single Responsibility Principle – SRP!

W naszej aplikacji mamy jeszcze klasę Details / Szczegóły mycia, która jest bardzo prosta my tylko kilka właściwości które reprezentują szczegóły mycia.

public class Details
{
   public WashingType WashingType { get; set; }
   public string Make { get; set; } 

   #region Standart
   public decimal Rinsing { get; set; }
   public decimal Drying { get; set; }
   #endregion

   #region StandartPlus
   public decimal VacuumingInside { get; set; }
   public decimal WashingInside { get; set; }  
   #endregion

   #region Waxing
   public bool Double { get; set; }
   #endregion

}

Mamy również enum WashingType / Typ mycia, czyli jaki rodzaj mycia został wybrany.

public enum WashingType
{
   Standard = 0,
   StandardPlus = 1,
   Waxing = 2
}

I mamy jeszcze plik json ze szczegółami mycia do wczytania.

{
  "WashingType": "Standard",
  "Make": "Ferrari",
  "Rinsing": 7,
  "Drying": 10
}

I mamy jeszcze proste testy jednostkowe dla klasy CarWash.

public class CarWashTest
{
   [Test]
   public void ReturnsPricingOf77ForWashingTypeStandardOf20RinsingOf7DryingOf10MakeOfFerrari()     
   {
      var details = new Details
      {
         WashingType = WashingType.Standard,
         Make = "Ferrari",
         Rinsing = 7,
         Drying = 10
      };
      string json = JsonConvert.SerializeObject(details);
      File.WriteAllText("details.json", json);

      var carWash = new CarWash();
      carWash.Pricing();
      var result = carWash.WashingCost;

      Assert.AreEqual(77, result);
   }

   [Test]
   public void ReturnsPricingOf37ForWashingTypeStandardOf20RinsingOf7DryingOf10()
   {
      var details = new Details
      {
         WashingType = WashingType.Standard,
         Make = "Mazda",
         Rinsing = 7,
         Drying = 10
      };
      string json = JsonConvert.SerializeObject(details);
      File.WriteAllText("details.json", json);

      var carWash = new CarWash();
      carWash.Pricing();
      var result = carWash.WashingCost;

      Assert.AreEqual(37, result);
   }

   [Test]
   public void ReturnsPricingOf0ForMakeNull()     
   {
      var details = new Details
      {
         WashingType = WashingType.StandardPlus,
         VacuumingInside = 15,
         WashingInside = 20,
      };
      string json = JsonConvert.SerializeObject(details);
      File.WriteAllText("details.json", json);

      var carWash = new CarWash();
      carWash.Pricing();
      var result = carWash.WashingCost;

      Assert.AreEqual(0, result);
   }   
}

W następnym wpisie zastosujemy zasade Single Responsibility Principle – SRP!

5 comments

  1. Cześć,
    Dobry przykład na pokazanie SRP, tylko się tak zastanawiam czy ten przykład nie jest zbyt prosty 🙂

    1. Ten kod jest oczywiście bardzo prosty, tak prosty jak to tylko możliwe ale ma oczywiście wystarczającą złożoność, aby dobrze działać na potrzeby tego kursu i zrozumieć zasady SOLID. Pokazanie zastosowania zasad jest najważniejsze 🙂

  2. hej,
    Nie mogę się doczekać zastosowania SRP, wyczekuje już na kolejny post.

  3. Prawdziwy z Ciebie talent i mistrz pióra z ogromną łatwością przekładasz myśli na słowa… trzymaj tak dalej, dbaj i pięlęgnuj swego bloga… Czym się inspirujesz na codzień ? skad czerpiesz pomysły na wpisy ?

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *