SRP – czyli czemu coś co jest do wszystkiego jest do niczego

SRP jest oficjalnie pierwszą zasadą w SOLID – zestawie najważniejszych założeń programowania obiektowego. Są ku temu bardzo dobre powody. Moim zdaniem warto popatrzeć się temu zagadnieniu bliżej.

Czym jest Single-responsibility principle?

SRP jest fundamentem programowania obiektowego. Według tej zasady każda klasa powinna zajmować się tylko jedną, dobrze określoną funkcjonalnością.

Oryginalnie zostało to sformowane jako “klasa powinna mieć tylko jeden powód do zmiany”. Znaczy to miej więcej, że klasy posiadającej ściśle określoną funkcjonalność będą zmieniane rzadziej. Poprawnie zaprojektowane klasy mają jedną odpowiedzialność, przez co zmieniają się tylko jeśli aktualizacja dotknie tego konkretnego obszaru.

Zwykle definicja ta dotyczy tylko klas w programowaniu obiektowym. Moim zdaniem można ją spokojnie rozszerzyć na znacznie większe części systemu np. moduły. W tym kontekście należy dbać by moduły aplikacji tak samo jak klasy posiadały ściśle określoną odpowiedzialność.

Dlaczego SRP jest na pierwszym miejscu SOLID?

Kolejność zasad SOLID jest podyktowana literami w akronimie. Nie zmienia to faktu, że SRP można potraktować jako najważniejszą z nich. Jeśli klasy w naszym programie nie mają ściśle zdefiniowanej odpowiedzialności, to nie ma mowy o jakimkolwiek programowaniu obiektowym.

Zwykle łamanie tej zasady kończy się tworzeniem tzw. “blobów”. Są to wielkie klasy, które chcą robić wszystko na raz. Tego typu kod jest niesamowicie trudny w zrozumieniu i utrzymaniu.

Zasada ta zyskuje na wartości szczególnie w kontekście młodych programistów. Zwykle osoba zaczynająca swoją przygodę z programowaniem tworzy ogromne “bloby”. Zrozumienie, że taka praktyka jest tragiczna zwykle zajmuje trochę czasu. Jest to naturalna droga nauki, która może być przyśpieszona przez zrozumienie SRP.

Możliwe problemy z Single-responsibility principle.

Kiedy w końcu zaczynamy stosować SRP, to jesteśmy świadkami dziwnego zjawiska. Nasz kod nagle eksploduje na wiele małych klas. Jeśli każda z nich odpowiada za jedną funkcjonalność, to jest to naturalne zjawisko. Wydawać by się mogło wtedy, że kod stał się bardziej skomplikowany. Jest to wrażenie, które szybko minie. W praktyce wiele wyspecjalizowanych klas jest strukturą znacznie bardziej przejrzystą niż jedna wielka klasa. Jest to więc problem pozorny.

Zawsze też pozostaje pytanie na jakim dokładnie poziomie dzielić aplikację na klasy/moduły. Jest to głębsze zagadnienie, które nie ma jednej, uniwersalnej odpowiedzi. Jest to jeden z nierozwiązywalnych problemów architektury oprogramowania. Różni programiści mogą dzielić klasy w trochę różny sposób.

Przykłady zastosowania SRP.

Przykłady klas poprawnie zdefiniowanych:

  • UserService – klasa zajmująca się logiką związaną z obiektem User
  • ApplicationStarter – klasa która startuje całą aplikację
  • ProductValidator – klasa zawierająca logikę walidacji obiektu Product
  • DateUtil – klasa zawierająca statyczne metody do manipulacji datami
  • CustomerRepository – klasa dostarczająca obiekty typu Customer z bazy danych

Każde rozszerzenie odpowiedzialności powyższych klas będzie z pewnością złamaniem SRP.

Leave A Comment