{"id":34828,"date":"2019-10-31T22:00:38","date_gmt":"2019-10-31T19:00:38","guid":{"rendered":"https:\/\/prohoster.info\/blog\/single-responsibility-principle-ne-takoj-prostoj-kak-kazhetsya\/"},"modified":"2019-10-31T22:00:38","modified_gmt":"2019-10-31T19:00:38","slug":"single-responsibility-principle-ne-takoj-prostoj-kak-kazhetsya","status":"publish","type":"post","link":"https:\/\/prohoster.info\/pl\/blog\/news\/single-responsibility-principle-ne-takoj-prostoj-kak-kazhetsya","title":{"rendered":"Zasada pojedynczej odpowiedzialno\u015bci. Nie tak prosta, jak si\u0119 wydaje","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Zasada pojedynczej odpowiedzialno\u015bci. Nie tak prosta, jak si\u0119 wydaje\" src=\"\/wp-content\/uploads\/2019\/06\/5b253ff55e3c97b6baf5f7a7ae7f3b98.JPG\" style=\"display:block;margin: 0 auto;\" \/> Zasada pojedynczej odpowiedzialno\u015bci, znana r\u00f3wnie\u017c jako zasada jedynej odpowiedzialno\u015bci,<br \/>\nznana r\u00f3wnie\u017c jako zasada jedynej zmienno\u015bci \u2014 to niezwykle \u015bliski temat zrozumienia i bardzo dra\u017cliwa kwestia na rozmowie kwalifikacyjnej dla programist\u00f3w. <\/p>\n<p><\/p>\n<p>Moje pierwsze powa\u017cne spotkanie z t\u0105 zasad\u0105 mia\u0142o miejsce na pocz\u0105tku pierwszego roku studi\u00f3w, kiedy to m\u0142odych i zielonych wywie\u017ali nas do lasu, aby ze student\u00f3w larw uczyni\u0107 prawdziwych student\u00f3w.<\/p>\n<p><\/p>\n<p>W lesie podzielono nas na grupy po 8-9 os\u00f3b w ka\u017cdej i zorganizowano zawody \u2014 kt\u00f3ra grupa szybciej wypije butelk\u0119 w\u00f3dki, pod warunkiem, \u017ce pierwszy cz\u0142owiek z grupy nalewa w\u00f3dk\u0119 do kieliszka, drugi pije, a trzeci zak\u0105sza. Wykonuj\u0105cy swoj\u0105 operacj\u0119 uczestnik ustawia si\u0119 na ko\u0144cu kolejki grupy. <\/p>\n<p><\/p>\n<p>Sytuacja, gdy rozmiar kolejki by\u0142 wielokrotno\u015bci\u0105 trzech, by\u0142a dobrym odwzorowaniem SRP.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex> <\/p>\n<h2 id=\"opredelenie-1-edinaya-otvetstvennost\">Definicja 1. Pojedyncza odpowiedzialno\u015b\u0107.<\/h2>\n<p><\/p>\n<p>Oficjalna definicja zasady pojedynczej odpowiedzialno\u015bci (SRP) m\u00f3wi, \u017ce ka\u017cdy obiekt ma swoj\u0105 odpowiedzialno\u015b\u0107 i pow\u00f3d istnienia, a ta odpowiedzialno\u015b\u0107 jest tylko jedna.<\/p>\n<p><\/p>\n<p>Rozwa\u017cmy obiekt &#171;Pijak&#187; (<strong>Tippler<\/strong>).<br \/>\nAby wype\u0142ni\u0107 zasad\u0119 SRP, podzielmy obowi\u0105zki pomi\u0119dzy trzech: <\/p>\n<p><\/p>\n<ul>\n<li>Jeden nalewa (<strong>PourOperation<\/strong>)<\/li>\n<li>Jeden pije (<strong>DrinkUpOperation<\/strong>)<\/li>\n<li>Jeden zak\u0105sza (<strong>TakeBiteOperation<\/strong>)<\/li>\n<\/ul>\n<p><\/p>\n<p>Ka\u017cdy z uczestnik\u00f3w procesu jest odpowiedzialny za jeden komponent procesu, to znaczy ma jedn\u0105 atomow\u0105 odpowiedzialno\u015b\u0107 \u2014 wypi\u0107, nala\u0107 lub zak\u0105si\u0107. <\/p>\n<p><\/p>\n<p>Wypijacz, z kolei, jest fasad\u0105 dla tych operacji:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">class Tippler {\n    \/\/...\n    void Act() {\n        _pourOperation.Do() \/\/ nala\u0107\n        _drinkUpOperation.Do() \/\/ wypi\u0107\n        _takeBiteOperation.Do() \/\/ zak\u0105si\u0107\n    }\n}<\/code><\/pre>\n<p>\n<img decoding=\"async\" alt=\"Zasada pojedynczej odpowiedzialno\u015bci. Nie tak prosta, jak si\u0119 wydaje\" src=\"\/wp-content\/uploads\/2019\/06\/8d591a474e3555edfee3ffebc28d079b.JPG\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<h4 id=\"zachem\">Dlaczego?<\/h4>\n<p><\/p>\n<p>Programista tworzy kod dla ma\u0142py, a ma\u0142pa jest nieuwa\u017cna, g\u0142upia i wiecznie gdzie\u015b si\u0119 spieszy. Mo\u017ce zrozumie\u0107 i utrzyma\u0107 oko\u0142o 3 \u2014 7 termin\u00f3w w jednym momencie.<br \/>\nW przypadku wypijacza tych termin\u00f3w jest trzy. Jednak je\u015bli napiszemy kod w jednym kawa\u0142ku, pojawi\u0105 si\u0119 w nim r\u0119ce, kieliszki, b\u00f3jki i niesko\u0144czone spory o polityk\u0119. A wszystko to b\u0119dzie w ciele jednej metody. Jestem pewien, \u017ce widzieli\u015bcie taki kod w swojej praktyce. To nie jest zbyt humanitarne wyzwanie dla psychiki.<\/p>\n<p><\/p>\n<p>Z drugiej strony, cz\u0142owiek-ma\u0142pa jest zaprogramowany na modelowanie obiekt\u00f3w rzeczywistego \u015bwiata w swojej g\u0142owie. W swojej wyobra\u017ani mo\u017ce je zderza\u0107, zbiera\u0107 z nich nowe obiekty i dok\u0142adnie tak samo je rozk\u0142ada\u0107. Wyobra\u017a sobie stary model samochodu. Mo\u017cesz w wyobra\u017ani otworzy\u0107 drzwi, odkr\u0119ci\u0107 tapicerk\u0119 i zobaczy\u0107 tam mechanizmy podnoszenia szyb, wewn\u0105trz kt\u00f3rych b\u0119d\u0105 z\u0119batki. Ale nie mo\u017cesz zobaczy\u0107 wszystkich komponent\u00f3w samochodu jednocze\u015bnie, w jednym &#171;wy\u015bwietleniu&#187;. Przynajmniej &#171;cz\u0142owiek-ma\u0142pa&#187; nie mo\u017ce.<\/p>\n<p><\/p>\n<p>Dlatego programi\u015bci-deweloperzy dekomponuj\u0105 skomplikowane mechanizmy na zestaw mniej z\u0142o\u017conych i dzia\u0142aj\u0105cych element\u00f3w. Mo\u017cna to jednak robi\u0107 na r\u00f3\u017cne sposoby: w wielu starych samochodach \u2014 przew\u00f3d powietrzny wychodzi do drzwi, a w nowoczesnych \u2014 awaria elektroniki zamka uniemo\u017cliwia uruchomienie silnika, co sprawia problemy przy naprawie.<\/p>\n<p><\/p>\n<p>Przechodz\u0105c do sedna, <strong>SRP to zasada wyja\u015bniaj\u0105ca, JAK dekomponowa\u0107, to znaczy, gdzie przeprowadzi\u0107 lini\u0119 podzia\u0142u.<\/strong>. <\/p>\n<p><\/p>\n<p>M\u00f3wi, \u017ce dekomponowa\u0107 nale\u017cy zgodnie z zasad\u0105 podzia\u0142u &#171;odpowiedzialno\u015bci&#187;, czyli wed\u0142ug zada\u0144 poszczeg\u00f3lnych obiekt\u00f3w.<\/p>\n<p>\n<img decoding=\"async\" alt=\"Zasada pojedynczej odpowiedzialno\u015bci. Nie tak prosta, jak si\u0119 wydaje\" src=\"\/wp-content\/uploads\/2019\/06\/b1980c3dc9742b921eb818f14fe3527e.JPG\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>Wr\u00f3\u0107my do pijaka i korzy\u015bci, jakie uzyskuje cz\u0142owiek-ma\u0142pa przy dekompozycji: <\/p>\n<p><\/p>\n<ul>\n<li>Kod sta\u0142 si\u0119 przejrzysty na ka\u017cdym poziomie.<\/li>\n<li>Kod mog\u0105 pisa\u0107 jednocze\u015bnie kilku programist\u00f3w (ka\u017cdy pisze oddzielny element).<\/li>\n<li>U\u0142atwia to automatyczne testowanie \u2014 im prostszy element, tym \u0142atwiej go testowa\u0107.<\/li>\n<li>Pojawia si\u0119 kompozycyjno\u015b\u0107 kodu \u2014 mo\u017cna zast\u0105pi\u0107 <strong>DrinkUpOperation<\/strong> operacj\u0105, w kt\u00f3rej pijak wylewa p\u0142yn pod st\u00f3\u0142. Lub zast\u0105pi\u0107 operacj\u0119 nalewania operacj\u0105, w kt\u00f3rej mieszasz wino i wod\u0119 lub w\u00f3dk\u0119 i piwo. W zale\u017cno\u015bci od wymaga\u0144 biznesowych mo\u017cna wszystko, nie dotykaj\u0105c kodu metody. <strong>Tippler.Act<\/strong>. <\/li>\n<li>Z tych operacji mo\u017cesz z\u0142o\u017cy\u0107 ob\u017cartucha (u\u017cywaj\u0105c tylko <strong>TakeBitOperation<\/strong>), alkoholika (u\u017cywaj\u0105c tylko <strong>DrinkUpOperation<\/strong> bezpo\u015brednio z butelki) i spe\u0142ni\u0107 wiele innych wymaga\u0144 biznesowych.<\/li>\n<\/ul>\n<p><\/p>\n<p><em>(Ojej, wydaje mi si\u0119, \u017ce to ju\u017c zasada OCP, i z\u0142ama\u0142em odpowiedzialno\u015b\u0107 tego posta.) <\/em><\/p>\n<p><\/p>\n<p>I oczywi\u015bcie minusy: <\/p>\n<p><\/p>\n<ul>\n<li>B\u0119dzie trzeba stworzy\u0107 wi\u0119cej typ\u00f3w. <\/li>\n<li>Pijak wypije po raz pierwszy o kilka godzin p\u00f3\u017aniej, ni\u017c m\u00f3g\u0142by.<\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"opredelenie-2-edinaya-izmenchivost\">Definicja 2. Jednolita zmienno\u015b\u0107.<\/h2>\n<p><\/p>\n<p>Pozw\u00f3lcie, panowie! Klasa pijaka r\u00f3wnie\u017c pe\u0142ni jedn\u0105 odpowiedzialno\u015b\u0107 \u2014 pije! I w og\u00f3le, s\u0142owo &#171;odpowiedzialno\u015b\u0107&#187; \u2014 jest poj\u0119ciem skrajnie rozmytym. Kto\u015b jest odpowiedzialny za losy ludzko\u015bci, a kto\u015b inny jest odpowiedzialny za podnoszenie przewr\u00f3conych na biegunie pingwin\u00f3w.<\/p>\n<p><\/p>\n<p>Rozwa\u017cmy dwie realizacje pijaka. Pierwsza, opisana powy\u017cej, zawiera trzy klasy - nala\u0107, wypi\u0107 i zak\u0105si\u0107.<\/p>\n<p><\/p>\n<p>Druga, napisana metodologi\u0105 &#171;Do przodu i tylko do przodu&#187; i zawiera ca\u0142\u0105 logik\u0119 w metodzie. <strong>Act<\/strong>:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">\/\/\u041d\u0435 \u0442\u0440\u0430\u0442\u044c\u0442\u0435 \u0432\u0440\u0435\u043c\u044f  \u043d\u0430 \u0438\u0437\u0443\u0447\u0435\u043d\u0438\u0435 \u044d\u0442\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430. \u041b\u0443\u0447\u0448\u0435 \u0441\u044a\u0435\u0448\u044c\u0442\u0435 \u043f\u0435\u0447\u0435\u043d\u044c\u043a\u0443\n\u0441lass BrutTippler {\n   \/\/...\n   void Act(){\n        \/\/ \u043d\u0430\u043b\u0438\u0432\u0430\u0435\u043c\n    if(!_hand.TryDischarge(from:_bottle, to:_glass, size:_glass.Capacity))\n        throw new OverdrunkException();\n\n    \/\/ \u0432\u044b\u043f\u0438\u0432\u0430\u0435\u043c\n    if(!_hand.TryDrink(from: _glass,  size: _glass.Capacity))\n        throw new OverdrunkException();\n\n    \/\/\u0417\u0430\u043a\u0443\u0441\u044b\u0432\u0430\u0435\u043c\n    for(int i = 0; i&lt; 3; i++){\n        var food = _foodStore.TakeOrDefault();\n        if(food==null)\n            throw new FoodIsOverException();\n\n        _hand.TryEat(food);\n    }\n   }\n}<\/code><\/pre>\n<p><\/p>\n<p>Obie te klasy, z perspektywy zewn\u0119trznego obserwatora, wygl\u0105daj\u0105 absolutnie identycznie i wykonuj\u0105 jedn\u0105 odpowiedzialno\u015b\u0107 &#171;wypi\u0107&#187;. <\/p>\n<p><\/p>\n<p>K\u0142opot!<\/p>\n<p><\/p>\n<p>Wtedy zagl\u0105damy do internetu i znajdujemy inne definicje SRP - Zasada Jednej Zmiany (Single Changeability Principle).<\/p>\n<p><\/p>\n<p>SCP stanowi, \u017ce &#171;<strong>Modu\u0142 ma jeden i tylko jeden pow\u00f3d do zmiany<\/strong>&#171;. To znaczy &#171;Odpowiedzialno\u015b\u0107 \u2014 to pow\u00f3d do zmiany&#187;. <\/p>\n<p><\/p>\n<p><em>(Wygl\u0105da na to, \u017ce go\u015bcie, kt\u00f3rzy wymy\u015blili pierwotn\u0105 definicj\u0119, byli pewni telepatycznych zdolno\u015bci cz\u0142owieka-ma\u0142py)<\/em><\/p>\n<p><\/p>\n<p>Teraz wszystko staje si\u0119 jasne. Mo\u017cemy oddzielnie zmienia\u0107 procedury nalewania, picia i zak\u0105szania, a w samym pijaku mo\u017cemy zmieni\u0107 tylko kolejno\u015b\u0107 i sk\u0142ad operacji, na przyk\u0142ad przesuwaj\u0105c zak\u0105sk\u0119 przed wypiciem lub dodaj\u0105c czytanie toastu.<\/p>\n<p><\/p>\n<p>W podej\u015bciu &#171;Do przodu i tylko do przodu&#187;, wszystko, co mo\u017cna zmieni\u0107 \u2014 zmienia si\u0119 tylko w metodzie. <strong>Act<\/strong>. Mo\u017ce to by\u0107 czytelne i efektywne, gdy logiki jest ma\u0142o i rzadko si\u0119 zmienia, ale cz\u0119sto ko\u0144czy si\u0119 okropnymi metodami po 500 linii w ka\u017cdej, z liczb\u0105 if-\u00f3w wi\u0119ksz\u0105, ni\u017c potrzeba do wst\u0105pienia Rosji do NATO. <\/p>\n<p><\/p>\n<h2 id=\"opredelenie-3-lokalizaciya-izmeneniy\">Definicja 3. Lokalizacja zmian.<\/h2>\n<p><\/p>\n<p>Pijacy cz\u0119sto nie rozumiej\u0105, dlaczego obudzili si\u0119 w obcym mieszkaniu, lub gdzie jest ich telefon kom\u00f3rkowy. Nadszed\u0142 czas, aby doda\u0107 szczeg\u00f3\u0142owe logowanie.<\/p>\n<p><\/p>\n<p>Zacznijmy logowanie od procesu nalewania:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">class PourOperation: IOperation{\n    PourOperation(ILogger log \/*....*\/){\/*...*\/}\n    \/\/...\n    void Do(){\n        _log.Log($\"Przed nalaniem z {_hand} i {_bottle}\");\n        \/\/Logika biznesowa nalewania ...\n        _log.Log($\"Po nalaniu z {_hand} i {_bottle}\");\n    }\n}<\/code><\/pre>\n<p><\/p>\n<p>Inkapshu\u0105c j\u0105 w <strong>PourOperation<\/strong>, post\u0105pili\u015bmy m\u0105drze z punktu widzenia odpowiedzialno\u015bci i enkapsulacji, ale teraz mamy zamieszanie z zasad\u0105 zmienno\u015bci. Opr\u00f3cz samej operacji, kt\u00f3ra mo\u017ce si\u0119 zmienia\u0107, zmienna staje si\u0119 r\u00f3wnie\u017c sama logika. B\u0119dziemy musieli podzieli\u0107 i stworzy\u0107 specjalny logger dla operacji nalewania:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">interface IPourLogger{\n    void LogBefore(IHand, IBottle){}\n    void LogAfter(IHand, IBottle){}\n    void OnError(IHand, IBottle, Exception){}\n}\n\nclass PourOperation: IOperation{\n    PourOperation(IPourLogger log \/*....*\/){\/*...*\/}\n    \/\/...\n    void Do(){\n        _log.LogBefore(_hand, _bottle);\n        try{\n             \/\/... logika biznesowa\n             _log.LogAfter(_hand, _bottle);\n        }\n        catch(exception e){\n            _log.OnError(_hand, _bottle, e)\n        }\n    }\n}<\/code><\/pre>\n<p><\/p>\n<p>Uwa\u017cny czytelnik zauwa\u017cy, \u017ce <strong>LogAfter<\/strong>, <strong>LogBefore<\/strong> i <strong>OnError<\/strong> r\u00f3wnie\u017c mog\u0105 zmienia\u0107 si\u0119 oddzielnie, a w analogii do poprzednich dzia\u0142a\u0144 stworzy trzy klasy: <strong>PourLoggerBefore<\/strong>, <strong>PourLoggerAfter<\/strong> i <strong>PourErrorLogger<\/strong>.<\/p>\n<p><\/p>\n<p>A przypominaj\u0105c, \u017ce mamy trzy operacje dla nalewania, otrzymujemy dziewi\u0119\u0107 klas logowania. W sumie ca\u0142y proces nalewania sk\u0142ada si\u0119 z 14 (!!!) klas.<\/p>\n<p><\/p>\n<p>Hipotetycznie? Niekoniecznie! Cz\u0142owiek-ma\u0142pa z granatem dekompozycyjnym zmieni \u201cnalewacza\u201d na karafk\u0119, szklank\u0119, operator\u00f3w nalewania, serwis podawania wody, fizyczny model kolizji cz\u0105steczek, a nast\u0119pny kwarta\u0142 b\u0119dzie pr\u00f3bowa\u0142 rozwi\u0105za\u0107 zale\u017cno\u015bci bez zmiennych globalnych. I uwierzcie \u2014 nie przestanie.<\/p>\n<p><\/p>\n<p>W\u0142a\u015bnie w tym momencie wielu dochodzi do wniosku, \u017ce SRP to ba\u015bnie z r\u00f3\u017cowych kr\u00f3lestw i odchodzi, by ple\u015b\u0107 bzdury&#8230;<\/p>\n<p><\/p>\n<p>\u2026 nie poznaj\u0105c istnienia trzeciej definicji SRP: <\/p>\n<p><\/p>\n<p>&#171;Zasada pojedynczej odpowiedzialno\u015bci g\u0142osi, \u017ce <strong>podobne rzeczy, kt\u00f3re mog\u0105 ulec zmianie, powinny by\u0107 przechowywane w jednym miejscu<\/strong>&#171;. lub \u201c<strong>To, co zmienia si\u0119 razem, powinno by\u0107 przechowywane w jednym miejscu.<\/strong>\u201d<\/p>\n<p><\/p>\n<p>A wi\u0119c, je\u015bli zmieniamy logowanie operacji, musimy to zmienia\u0107 w jednym miejscu.<\/p>\n<p><\/p>\n<p>To bardzo wa\u017cny moment \u2014 poniewa\u017c wszystkie wcze\u015bniejsze wyja\u015bnienia SRP m\u00f3wi\u0142y o tym, \u017ce nale\u017cy dzieli\u0107 typy, dop\u00f3ki s\u0105 podzielne, co nak\u0142ada\u0142o &#171;g\u00f3rne ograniczenie&#187; na rozmiar obiektu, a teraz m\u00f3wimy ju\u017c i o &#171;ograniczeniu dolnym&#187;. Innymi s\u0142owy, <strong>SRP nie tylko wymaga &#171;dzieli\u0107, dop\u00f3ki si\u0119 da&#187;, ale tak\u017ce nie przesadzi\u0107 \u2014 &#171;nie rozdrabnia\u0107 po\u0142\u0105czonych rzeczy&#187;.<\/strong>. To wielka bitwa brzytwy Ockhama z cz\u0142owiekiem-ma\u0142p\u0105!<\/p>\n<p>\n<img decoding=\"async\" alt=\"Zasada pojedynczej odpowiedzialno\u015bci. Nie tak prosta, jak si\u0119 wydaje\" src=\"\/wp-content\/uploads\/2019\/06\/2e349a5b119a511d716341f3f6725844.JPG\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>Teraz powinno by\u0107 \u0142atwiej. Opr\u00f3cz tego, \u017ce nie trzeba dzieli\u0107 logera IPourLogger na trzy klasy, mo\u017cemy r\u00f3wnie\u017c po\u0142\u0105czy\u0107 wszystkie loggery w jeden typ:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">class OperationLogger{\n    public OperationLogger(string operationName){\n    \/*..*\/}\n    public void LogBefore(object[] args){\n    \/*...*\/}\n    public void LogAfter(object[] args){\n    \/*..*\/}\n    public void LogError(object[] args, exception e){\n    \/*..*\/}\n}<\/code><\/pre>\n<p><\/p>\n<p>A je\u015bli dodamy czwarty typ operacji, to logowanie dla niego jest ju\u017c gotowe. A sam kod operacji jest czysty i wolny od szum\u00f3w infrastrukturalnych.<\/p>\n<p><\/p>\n<p>W rezultacie mamy 5 klas do rozwi\u0105zania zadania nalewania:<\/p>\n<p><\/p>\n<ul>\n<li>Operacja nalewania<\/li>\n<li>Operacja wypicia<\/li>\n<li>Operacja podjadania<\/li>\n<li>Logger<\/li>\n<li>Fasada pijaka<\/li>\n<\/ul>\n<p><\/p>\n<p>Ka\u017cda z nich odpowiada \u015bci\u015ble za jedn\u0105 funkcjonalno\u015b\u0107, ma jeden pow\u00f3d do zmiany. Wszystkie podobne zasady do zmiany s\u0105 obok siebie. <\/p>\n<p>\n<b class=\"spoiler_title\">Przyk\u0142ad z \u017cycia wzi\u0119ty<\/b><\/p>\n<p>Pewnego razu pisali\u015bmy serwis automatycznej rejestracji klienta B2B. I pojawi\u0142a si\u0119 metoda GOD \u2014 na 200 linijek podobnej tre\u015bci:<\/p>\n<p><\/p>\n<ul>\n<li>Id\u017a do 1C i za\u0142\u00f3\u017c konto<\/li>\n<li>Z tym kontem id\u017a do modu\u0142u p\u0142atno\u015bci i za\u0142\u00f3\u017c je tam<\/li>\n<li>Sprawd\u017a, czy konto z tym kontem nie zosta\u0142o stworzone w g\u0142\u00f3wnym <a class=\"wpil_keyword_link\" href=\"https:\/\/prohoster.info\/server\/dts-dronten\/\"   title=\"serwerze\" data-wpil-keyword-link=\"linked\"  data-wpil-monitor-id=\"2485\">serwerze<\/a><\/li>\n<li>Stw\u00f3rz nowe konto<\/li>\n<li>Dodaj wynik rejestracji w module p\u0142atno\u015bci oraz numer 1C do serwisu wynik\u00f3w rejestracji<\/li>\n<li>Dodaj do tej tabeli informacje o koncie<\/li>\n<li>Stw\u00f3rz numer punktu dla tego klienta w serwisie punkt\u00f3w. Przeka\u017c do tego serwisu numer konta 1C.<\/li>\n<\/ul>\n<p><\/p>\n<p>A w tej li\u015bcie by\u0142o jeszcze oko\u0142o 10 operacji biznesowych z przera\u017caj\u0105c\u0105 powi\u0105zaniem. Obiekt konta by\u0142 potrzebny niemal wszystkim. Identyfikator punktu i nazwa klienta by\u0142y potrzebne w po\u0142owie wywo\u0142a\u0144.<\/p>\n<p><\/p>\n<p>Po godzinnej refaktoryzacji uda\u0142o nam si\u0119 oddzieli\u0107 kod infrastrukturalny i pewne niuanse zwi\u0105zane z kontem w osobne metody\/klasy. Metoda God zosta\u0142a uproszczona, ale pozosta\u0142o 100 linijek kodu, kt\u00f3re wcale nie chcia\u0142y si\u0119 rozwi\u0105za\u0107. <\/p>\n<p><\/p>\n<p>Dopiero po kilku dniach przysz\u0142o zrozumienie, \u017ce istota tej &#171;ule\u017cenie&#187; metody \u2014 jest algorytmem biznesowym. I \u017ce pocz\u0105tkowy opis Tz by\u0142 do\u015b\u0107 z\u0142o\u017cony. I to w\u0142a\u015bnie pr\u00f3ba rozbicia tej metody na kawa\u0142ki b\u0119dzie naruszeniem SRP, a nie odwrotnie. <\/p>\n<p><\/p>\n<h2 id=\"formalizm\">Formalizm.<\/h2>\n<p><\/p>\n<p>Nadszed\u0142 czas, aby zostawi\u0107 naszego pijaka w spokoju. Wytrzyj \u0142zy \u2014 na pewno do niego wr\u00f3cimy kiedykolwiek. A teraz sformalizujmy wiedz\u0119 z tego artyku\u0142u. <\/p>\n<p><\/p>\n<h4 id=\"formalizm-1-opredelenie-srp\">Formalizm 1. Definicja SRP<\/h4>\n<p><\/p>\n<ol>\n<li>Dziel elementy tak, aby ka\u017cdy z nich by\u0142 odpowiedzialny za co\u015b jednego.<\/li>\n<li>Odpowiedzialno\u015b\u0107 jest rozumiana jako \u201epow\u00f3d do zmiany\u201d. Oznacza to, \u017ce ka\u017cdy element ma tylko jeden pow\u00f3d do zmiany, w terminach logiki biznesowej.<\/li>\n<li>Potencjalne zmiany logiki biznesowej powinny by\u0107 lokalizowane. Elementy, kt\u00f3re mog\u0105 by\u0107 zmieniane synchronnie, powinny by\u0107 blisko siebie.<\/li>\n<\/ol>\n<p><\/p>\n<h4 id=\"formalizm-2-neobhodimye-kriterii-samoproverki\">Formalizm 2. Wymagane kryteria samosprawdzania.<\/h4>\n<p><\/p>\n<p>Nie natkn\u0105\u0142em si\u0119 na wystarczaj\u0105ce kryteria spe\u0142nienia SRP. Ale s\u0105 niezb\u0119dne warunki:<\/p>\n<p><\/p>\n<p>1) Zadaj sobie pytanie \u2013 co robi ta klasa\/ metoda\/ modu\u0142\/ us\u0142uga. Powiniene\u015b odpowiedzie\u0107 na nie prost\u0105 definicj\u0105. (Dzi\u0119kuj\u0119) <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/users\/brightori\/\" class=\"user_link\">Brightori<\/a><\/noindex> )<\/p>\n<p>\n<b class=\"spoiler_title\">wyja\u015bnienia<\/b><\/p>\n<p>Jednak czasami zdefiniowanie prostego poj\u0119cia jest bardzo trudne. <\/p>\n<p><\/p>\n<p>2) Naprawa pewnego b\u0142\u0119du lub dodanie nowej funkcji wp\u0142ywa na minimaln\u0105 liczb\u0119 plik\u00f3w\/ klas. W idealnym przypadku \u2013 jeden. <\/p>\n<p>\n<b class=\"spoiler_title\">wyja\u015bnienia<\/b><\/p>\n<p>Poniewa\u017c odpowiedzialno\u015b\u0107 (za funkcj\u0119 lub b\u0142\u0105d) jest enkapsulowana w jednym pliku\/ klasie, wiesz dok\u0142adnie, gdzie szuka\u0107 i co poprawia\u0107. Na przyk\u0142ad: funkcja zmiany spos\u00f3b logowania operacji wymaga jedynie zmiany loggera. Nie ma potrzeby przeszukiwania ca\u0142ego pozosta\u0142ego kodu. <\/p>\n<p><\/p>\n<p>Inny przyk\u0142ad \u2013 dodanie nowego kontrolera UI, podobnego do poprzednich. Je\u015bli zmusza ci\u0119 to do dodania 10 r\u00f3\u017cnych byt\u00f3w i 15 r\u00f3\u017cnych konwerter\u00f3w \u2013 wydaje si\u0119, \u017ce 'przerobi\u0142e\u015b to'.<\/p>\n<p><\/p>\n<p>3) Je\u015bli kilku programist\u00f3w pracuje nad r\u00f3\u017cnymi funkcjami twojego projektu, prawdopodobie\u0144stwo konfliktu scalania, czyli prawdopodobie\u0144stwo, \u017ce ten sam plik\/ klasa zostanie zmieniona przez kilku programist\u00f3w jednocze\u015bnie \u2013 jest minimalne.<\/p>\n<p>\n<b class=\"spoiler_title\">wyja\u015bnienia<\/b><\/p>\n<p>Je\u015bli przy dodawaniu nowej operacji \u201eWyla\u0107 w\u00f3dk\u0119 pod st\u00f3\u0142\u201d musisz dotkn\u0105\u0107 loggera, operacji picia i wylewania \u2014 to wydaje si\u0119, \u017ce odpowiedzialno\u015bci s\u0105 \u017ale podzielone. Oczywi\u015bcie nie zawsze jest to mo\u017cliwe, ale nale\u017cy stara\u0107 si\u0119 obni\u017cy\u0107 ten wska\u017anik. <\/p>\n<p><\/p>\n<p>4) Przy pytaniu wyja\u015bniaj\u0105cym o logik\u0119 biznesow\u0105 (od programisty lub mened\u017cera) si\u0119gasz \u015bci\u015ble do jednej klasy\/ pliku i uzyskujesz informacje tylko stamt\u0105d.<\/p>\n<p>\n<b class=\"spoiler_title\">wyja\u015bnienia<\/b><\/p>\n<p>Funkcje, zasady lub algorytmy s\u0105 zwi\u0119\u017ale napisane, ka\u017cdy w jednym miejscu, a nie rozrzucane flagami po ca\u0142ej przestrzeni kodu.<\/p>\n<p><\/p>\n<p>5) Nazewnictwo jest zrozumia\u0142e.<\/p>\n<p>\n<b class=\"spoiler_title\">wyja\u015bnienia<\/b><\/p>\n<p>Nasza klasa lub metoda jest odpowiedzialna za co\u015b jednego, a odpowiedzialno\u015b\u0107 jest odzwierciedlona w jej nazwie.<\/p>\n<p><\/p>\n<p>AllManagersManagerService \u2013 najprawdopodobniej klasa Boga.<br \/>\nLocalPayment \u2013 prawdopodobnie nie. <\/p>\n<p><\/p>\n<h4 id=\"formalizm-3-metodika-razrabotki-okkama-first\">Formalizm 3. Metodologia opracowania \u201eOckham-first\u201d.<\/h4>\n<p><\/p>\n<p>Na pocz\u0105tku projektowania, cz\u0142owiek-ma\u0142pa nie zna i nie czuje wszystkich niuans\u00f3w rozwi\u0105zywanego zadania i mo\u017ce pope\u0142ni\u0107 b\u0142\u0105d. Mo\u017cna pomyli\u0107 si\u0119 na r\u00f3\u017cne sposoby:<\/p>\n<p><\/p>\n<ul>\n<li>Tworzenie zbyt du\u017cych obiekt\u00f3w przez \u0142\u0105czenie r\u00f3\u017cnych odpowiedzialno\u015bci<\/li>\n<li>Rozdzielenie jednej odpowiedzialno\u015bci na wiele r\u00f3\u017cnych typ\u00f3w<\/li>\n<li>B\u0142\u0119dne okre\u015blenie granic odpowiedzialno\u015bci<\/li>\n<\/ul>\n<p><\/p>\n<p>Wa\u017cne jest, aby zapami\u0119ta\u0107 zasad\u0119: \u201elepiej pope\u0142nia\u0107 b\u0142\u0119dy w wi\u0119kszej skali\u201d, lub \u201enie pewny \u2014 nie dziel\u201d. Je\u015bli na przyk\u0142ad twoja klasa zbiera w sobie dwie odpowiedzialno\u015bci \u2014 to wci\u0105\u017c jest zrozumia\u0142a i mo\u017cna j\u0105 podzieli\u0107 na dwie z minimaln\u0105 zmian\u0105 kodu klienta. Zbieranie natomiast z od\u0142amk\u00f3w szk\u0142a szklanki jest zazwyczaj trudniejsze z powodu rozmytego kontekstu roz\u0142o\u017conego na kilka plik\u00f3w i braku niezb\u0119dnych zale\u017cno\u015bci w kodzie klienta. <\/p>\n<p><\/p>\n<h2 id=\"pora-zakruglyatsya\">Czas zako\u0144czy\u0107<\/h2>\n<p><\/p>\n<p>Zastosowanie SRP nie ogranicza si\u0119 do OOP i SOLID. Stosuje si\u0119 go do metod, funkcji, klas, modu\u0142\u00f3w, mikroserwis\u00f3w i us\u0142ug. Jest stosowany zar\u00f3wno w prostym, jak i w skomplikowanym rozwoju, wsz\u0119dzie sprawiaj\u0105c, \u017ce \u015bwiat staje si\u0119 nieco lepszy. Je\u015bli si\u0119 nad tym zastanowi\u0107, to jest to chyba jeden z fundament\u00f3w ca\u0142ej in\u017cynierii. In\u017cynieria mechaniczna, systemy kontroli i w og\u00f3le wszystkie z\u0142o\u017cone systemy \u2013 budowane s\u0105 z komponent\u00f3w, a \u201eniedostateczne podzielenie\u201d pozbawia konstruktor\u00f3w elastyczno\u015bci, \u201enadmierne podzielenie\u201d \u2013 efektywno\u015bci, a b\u0142\u0119dne granice \u2013 zdrowego rozs\u0105dku i spokoju ducha. <\/p>\n<p>\n<img decoding=\"async\" alt=\"Zasada pojedynczej odpowiedzialno\u015bci. Nie tak prosta, jak si\u0119 wydaje\" src=\"\/wp-content\/uploads\/2019\/06\/b70fb2b0b87cf90d41462263e839902b.JPG\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>SRP nie jest wymy\u015blone przez natur\u0119 i nie jest cz\u0119\u015bci\u0105 nauk \u015bcis\u0142ych. Wyno\u015bci si\u0119 z naszych biologicznych i psychologicznych ogranicze\u0144. To tylko spos\u00f3b na kontrolowanie i rozwijanie z\u0142o\u017conych system\u00f3w przy u\u017cyciu m\u00f3zgu cz\u0142owieka-ma\u0142py. M\u00f3wi nam, jak dekomponowa\u0107 system. Pocz\u0105tkowa formu\u0142a wymaga\u0142a znacznego talentu telepatycznego, ale mam nadziej\u0119, \u017ce ten artyku\u0142 nieco rozproszy\u0142 mg\u0142\u0119.<\/p>\n<p>\u0179r\u00f3d\u0142o: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/454290\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>Single responsibility principle, \u043e\u043d \u0436\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f \u0435\u0434\u0438\u043d\u043e\u0439 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438, \u043e\u043d \u0436\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f \u0435\u0434\u0438\u043d\u043e\u0439 \u0438\u0437\u043c\u0435\u043d\u0447\u0438\u0432\u043e\u0441\u0442\u0438 \u2014 \u043a\u0440\u0430\u0439\u043d\u0435 \u0441\u043a\u043e\u043b\u044c\u0437\u043a\u0438\u0439 \u0434\u043b\u044f \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u043f\u0430\u0440\u0435\u043d\u044c \u0438 \u0441\u0442\u043e\u043b\u044c \u043d\u0435\u0440\u0432\u043e\u0437\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441 \u043d\u0430 \u0441\u043e\u0431\u0435\u0441\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0438 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u0430. \u041f\u0435\u0440\u0432\u043e\u0435 \u0441\u0435\u0440\u044c\u0435\u0437\u043d\u043e\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u0441\u0442\u0432\u043e \u0441 \u044d\u0442\u0438\u043c \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u043e\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043b\u043e\u0441\u044c \u0434\u043b\u044f \u043c\u0435\u043d\u044f \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u043a\u0443\u0440\u0441\u0430, \u043a\u043e\u0433\u0434\u0430 \u043c\u043e\u043b\u043e\u0434\u044b\u0445 \u0438 \u0437\u0435\u043b\u0435\u043d\u044b\u0445 \u043d\u0430\u0441 \u0432\u044b\u0432\u0435\u0437\u043b\u0438 \u0432 \u043b\u0435\u0441, \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0438\u0437 \u043b\u0438\u0447\u0438\u043d\u043e\u043a \u0441\u0442\u0443\u0434\u0435\u043d\u0442\u043e\u0432 \u2014 \u0441\u0442\u0443\u0434\u0435\u043d\u0442\u043e\u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0445. [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":26229,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[702],"tags":[],"class_list":["post-34828","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-news"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/pl\/blog\/news\/single-responsibility-principle-ne-takoj-prostoj-kak-kazhetsya\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"pl_PL\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47Single Responsibility Principle. \u041d\u0435 \u0442\u0430\u043a\u043e\u0439 \u043f\u0440\u043e\u0441\u0442\u043e\u0439, \u043a\u0430\u043a \u043a\u0430\u0436\u0435\u0442\u0441\u044f | ProHoster\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/pl\/blog\/news\/single-responsibility-principle-ne-takoj-prostoj-kak-kazhetsya\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T19:00:38+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T19:00:38+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Zasada pojedynczej odpowiedzialno\u015bci. Nie jest tak prosta, jak si\u0119 wydaje | ProHoster","description":"","canonical_url":"https:\/\/prohoster.info\/pl\/blog\/news\/single-responsibility-principle-ne-takoj-prostoj-kak-kazhetsya","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pl_PL","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47Single Responsibility Principle. \u041d\u0435 \u0442\u0430\u043a\u043e\u0439 \u043f\u0440\u043e\u0441\u0442\u043e\u0439, \u043a\u0430\u043a \u043a\u0430\u0436\u0435\u0442\u0441\u044f | ProHoster","og:url":"https:\/\/prohoster.info\/pl\/blog\/news\/single-responsibility-principle-ne-takoj-prostoj-kak-kazhetsya","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T19:00:38+00:00","article:modified_time":"2019-10-31T19:00:38+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"34828","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-02-09 21:46:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 02:14:06","updated":"2026-02-09 21:46:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/34828","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/comments?post=34828"}],"version-history":[{"count":1,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/34828\/revisions"}],"predecessor-version":[{"id":159767,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/34828\/revisions\/159767"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media\/26229"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media?parent=34828"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/categories?post=34828"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/tags?post=34828"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}