Prinsipyo ng Iisang Pananagutan. Hindi kasing simple ng tila

Prinsipyo ng Iisang Pananagutan. Hindi kasing simple ng tila Prinsipyo ng solong responsibilidad, na kilala rin bilang prinsipyo ng solong responsibilidad,
aka ang prinsipyo ng pare-parehong pagkakaiba-iba - isang lubhang madulas na tao upang maunawaan at tulad ng isang kinakabahan na tanong sa isang panayam ng programmer.

Ang aking unang seryosong pagkakakilala sa prinsipyong ito ay naganap sa simula ng unang taon, nang ang mga bata at berde ay dinala sa kagubatan upang gumawa ng mga mag-aaral mula sa larvae - mga tunay na estudyante.

Sa kagubatan, hinati kami sa mga grupo ng 8-9 na tao bawat isa at nagkaroon ng kumpetisyon - kung aling grupo ang pinakamabilis na uminom ng isang bote ng vodka, sa kondisyon na ang unang tao mula sa grupo ay nagbuhos ng vodka sa isang baso, ang pangalawa ay umiinom nito, at ang pangatlo ay may meryenda. Ang unit na nakakumpleto sa operasyon nito ay lilipat sa dulo ng pila ng grupo.

Ang kaso kung saan ang laki ng pila ay multiple ng tatlo ay isang magandang pagpapatupad ng SRP.

Kahulugan 1. Iisang responsibilidad.

Ang opisyal na kahulugan ng Single Responsibility Principle (SRP) ay nagsasaad na ang bawat entity ay may sariling responsibilidad at dahilan para sa pagkakaroon, at mayroon lamang itong isang responsibilidad.

Isaalang-alang ang bagay na "Drinker" (Tippler).
Upang ipatupad ang prinsipyo ng SRP, hahatiin natin ang mga responsibilidad sa tatlo:

  • Isang pagbubuhos (PourOperation)
  • Isang inumin (DrinkUpOperation)
  • Ang isa ay may meryenda (TakeBiteOperation)

Ang bawat isa sa mga kalahok sa proseso ay may pananagutan para sa isang bahagi ng proseso, iyon ay, ay may isang atomic na responsibilidad - uminom, magbuhos o meryenda.

Ang butas ng pag-inom, sa turn, ay isang harapan para sa mga operasyong ito:

сlass Tippler {
    //...
    void Act(){
        _pourOperation.Do() // Π½Π°Π»ΠΈΡ‚ΡŒ
        _drinkUpOperation.Do() // Π²Ρ‹ΠΏΠΈΡ‚ΡŒ
        _takeBiteOperation.Do() // Π·Π°ΠΊΡƒΡΠΈΡ‚ΡŒ
    }
}

Prinsipyo ng Iisang Pananagutan. Hindi kasing simple ng tila

Bakit?

Ang programmer ng tao ay nagsusulat ng code para sa taong unggoy, at ang taong unggoy ay walang pakialam, hangal at laging nagmamadali. Maaari niyang hawakan at maunawaan ang tungkol sa 3 - 7 termino sa isang pagkakataon.
Sa kaso ng isang lasenggo, mayroong tatlo sa mga terminong ito. Gayunpaman, kung isusulat natin ang code sa isang sheet, maglalaman ito ng mga kamay, baso, away at walang katapusang argumento tungkol sa pulitika. At ang lahat ng ito ay nasa katawan ng isang pamamaraan. Sigurado akong nakita mo ang ganoong code sa iyong pagsasanay. Hindi ang pinaka-makatao na pagsubok para sa psyche.

Sa kabilang banda, ang taong unggoy ay idinisenyo upang gayahin ang mga tunay na bagay sa mundo sa kanyang ulo. Sa kanyang imahinasyon, maaari niyang itulak ang mga ito nang magkasama, mag-ipon ng mga bagong bagay mula sa kanila, at i-disassemble ang mga ito sa parehong paraan. Isipin ang isang lumang modelo ng kotse. Sa iyong imahinasyon, maaari mong buksan ang pinto, i-unscrew ang trim ng pinto at makita doon ang mga mekanismo ng pag-angat ng bintana, sa loob kung saan magkakaroon ng mga gears. Ngunit hindi mo makikita ang lahat ng bahagi ng makina nang sabay-sabay, sa isang "listahan". Hindi bababa sa "lalaking unggoy" ang hindi.

Samakatuwid, ang mga programmer ng tao ay nabubulok ang mga kumplikadong mekanismo sa isang hanay ng mga hindi gaanong kumplikado at gumaganang mga elemento. Gayunpaman, maaari itong mabulok sa iba't ibang paraan: sa maraming mga lumang kotse, ang air duct ay pumapasok sa pinto, at sa mga modernong kotse, ang isang pagkabigo sa lock electronics ay pumipigil sa pagsisimula ng makina, na maaaring maging isang problema sa panahon ng pag-aayos.

Kaya, Ang SRP ay isang prinsipyo na nagpapaliwanag kung PAANO mag-decompose, ibig sabihin, kung saan iguguhit ang linya ng paghahati.

Sinabi niya na kinakailangan na mabulok ayon sa prinsipyo ng paghahati ng "responsibilidad," iyon ay, ayon sa mga gawain ng ilang mga bagay.

Prinsipyo ng Iisang Pananagutan. Hindi kasing simple ng tila

Bumalik tayo sa pag-inom at ang mga pakinabang na natatanggap ng taong unggoy sa panahon ng agnas:

  • Ang code ay naging napakalinaw sa bawat antas
  • Ang code ay maaaring isulat ng ilang programmer nang sabay-sabay (bawat isa ay nagsusulat ng hiwalay na elemento)
  • Ang automated na pagsubok ay pinasimple - mas simple ang elemento, mas madali itong subukan
  • Lumilitaw ang compositionality ng code - maaari mong palitan DrinkUpOperation sa isang operasyon kung saan ang isang lasenggo ay nagbubuhos ng likido sa ilalim ng mesa. O palitan ang operasyon ng pagbuhos ng isang operasyon kung saan pinaghalo mo ang alak at tubig o vodka at beer. Depende sa mga kinakailangan sa negosyo, magagawa mo ang lahat nang hindi hinahawakan ang code ng pamamaraan Tippler.Act.
  • Mula sa mga operasyong ito maaari mong tiklop ang matakaw (gamit lamang TakeBitOperation), Alcoholic (gamit lang DrinkUpOperation diretso mula sa bote) at matugunan ang maraming iba pang mga kinakailangan sa negosyo.

(Oh, tila ito ay isang prinsipyo ng OCP, at nilabag ko ang responsibilidad ng post na ito)

At, siyempre, ang kahinaan:

  • Kakailanganin nating lumikha ng higit pang mga uri.
  • Ang isang lasenggo ay umiinom sa unang pagkakataon makalipas ang ilang oras kaysa sa kung hindi man niya gagawin.

Kahulugan 2. Pinag-isang pagkakaiba-iba.

Payagan ako, mga ginoo! Ang klase ng pag-inom ay mayroon ding isang solong responsibilidad - ito ay umiinom! At sa pangkalahatan, ang salitang "responsibilidad" ay isang napakalabing konsepto. May responsable sa kapalaran ng sangkatauhan, at may responsable sa pagpapalaki ng mga penguin na nabaligtad sa poste.

Isaalang-alang natin ang dalawang pagpapatupad ng umiinom. Ang una, na nabanggit sa itaas, ay naglalaman ng tatlong klase - ibuhos, inumin at meryenda.

Ang pangalawa ay isinulat sa pamamagitan ng "Forward and Only Forward" methodology at naglalaman ng lahat ng logic sa method Kumilos:

//НС Ρ‚Ρ€Π°Ρ‚ΡŒΡ‚Π΅ врСмя  Π½Π° ΠΈΠ·ΡƒΡ‡Π΅Π½ΠΈΠ΅ этого класса. Π›ΡƒΡ‡ΡˆΠ΅ ΡΡŠΠ΅ΡˆΡŒΡ‚Π΅ ΠΏΠ΅Ρ‡Π΅Π½ΡŒΠΊΡƒ
сlass BrutTippler {
   //...
   void Act(){
        // Π½Π°Π»ΠΈΠ²Π°Π΅ΠΌ
    if(!_hand.TryDischarge(from:_bottle, to:_glass, size:_glass.Capacity))
        throw new OverdrunkException();

    // Π²Ρ‹ΠΏΠΈΠ²Π°Π΅ΠΌ
    if(!_hand.TryDrink(from: _glass,  size: _glass.Capacity))
        throw new OverdrunkException();

    //ЗакусываСм
    for(int i = 0; i< 3; i++){
        var food = _foodStore.TakeOrDefault();
        if(food==null)
            throw new FoodIsOverException();

        _hand.TryEat(food);
    }
   }
}

Pareho sa mga klase na ito, mula sa pananaw ng isang tagamasid sa labas, ay eksaktong magkapareho at may parehong responsibilidad ng "pag-inom".

Pagkalito!

Pagkatapos ay mag-online kami at alamin ang isa pang kahulugan ng SRP - ang Single Changeability Principle.

Sinasabi ng SCP na "Ang isang module ay may isa at isa lamang na dahilan para magbago". Iyon ay, "Ang responsibilidad ay isang dahilan para sa pagbabago."

(Mukhang ang mga taong nakabuo ng orihinal na kahulugan ay tiwala sa mga kakayahan ng telepatiko ng taong unggoy)

Ngayon ang lahat ay nahuhulog sa lugar. Hiwalay, maaari nating baguhin ang mga pamamaraan ng pagbuhos, pag-inom at pag-snack, ngunit sa mismong umiinom ay maaari lamang nating baguhin ang pagkakasunud-sunod at komposisyon ng mga operasyon, halimbawa, sa pamamagitan ng paglipat ng meryenda bago uminom o pagdaragdag ng pagbabasa ng isang toast.

Sa diskarteng "Forward and Only Forward", lahat ng maaaring baguhin ay mababago lamang sa pamamaraan Kumilos. Ito ay mababasa at mabisa kapag may kaunting lohika at bihira itong magbago, ngunit kadalasan ay nauuwi ito sa mga kakila-kilabot na pamamaraan ng 500 linya bawat isa, na may mas maraming if-statement kaysa sa kinakailangan para sa Russia na sumali sa NATO.

Kahulugan 3. Lokalisasyon ng mga pagbabago.

Madalas na hindi maintindihan ng mga umiinom kung bakit sila nagising sa apartment ng ibang tao, o kung nasaan ang kanilang mobile phone. Oras na para magdagdag ng detalyadong pag-log.

Magsimula tayo sa pag-log sa proseso ng pagbuhos:

class PourOperation: IOperation{
    PourOperation(ILogger log /*....*/){/*...*/}
    //...
    void Do(){
        _log.Log($"Before pour with {_hand} and {_bottle}");
        //Pour business logic ...
        _log.Log($"After pour with {_hand} and {_bottle}");
    }
}

Sa pamamagitan ng pag-encapsulate nito PourOperation, kami ay kumilos nang matalino mula sa punto ng view ng responsibilidad at encapsulation, ngunit ngayon kami ay nalilito sa prinsipyo ng pagkakaiba-iba. Bilang karagdagan sa mismong operasyon, na maaaring magbago, ang pag-log mismo ay nagiging nababago rin. Kakailanganin mong paghiwalayin at lumikha ng isang espesyal na logger para sa operasyon ng pagbuhos:

interface IPourLogger{
    void LogBefore(IHand, IBottle){}
    void LogAfter(IHand, IBottle){}
    void OnError(IHand, IBottle, Exception){}
}

class PourOperation: IOperation{
    PourOperation(IPourLogger log /*....*/){/*...*/}
    //...
    void Do(){
        _log.LogBefore(_hand, _bottle);
        try{
             //... business logic
             _log.LogAfter(_hand, _bottle");
        }
        catch(exception e){
            _log.OnError(_hand, _bottle, e)
        }
    }
}

Mapapansin iyon ng maselang mambabasa LogAfter, LogBefore ΠΈ OnError ay maaari ding baguhin nang paisa-isa, at, sa pamamagitan ng pagkakatulad sa mga naunang hakbang, ay lilikha ng tatlong klase: PourLoggerBefore, PourLoggerAfter ΠΈ PourErrorLogger.

At pag-alala na mayroong tatlong operasyon para sa isang manginginom, nakakakuha tayo ng siyam na klase sa pag-log. Bilang resulta, ang buong bilog ng pag-inom ay binubuo ng 14 (!!!) na mga klase.

Hyperbola? Halos hindi! Ang isang taong unggoy na may decomposition grenade ay hahatiin ang "pourer" sa isang decanter, isang baso, mga operator ng pagbuhos, isang serbisyo sa supply ng tubig, isang pisikal na modelo ng banggaan ng mga molekula, at para sa susunod na quarter ay susubukan niyang alisin ang mga dependency nang walang pandaigdigang mga variable. At maniwala ka sa akin, hindi siya titigil.

Sa puntong ito marami ang nahuhuli na ang SRP ay mga fairy tales mula sa mga pink na kaharian, at lumalayo upang maglaro ng pansit...

... nang hindi nalalaman ang tungkol sa pagkakaroon ng ikatlong kahulugan ng Srp:

β€œAng Single Responsibility Principle ay nagsasaad na ang mga bagay na katulad ng pagbabago ay dapat na nakaimbak sa isang lugar". o"Ang mga pagbabagong magkakasama ay dapat itago sa isang lugar"

Iyon ay, kung babaguhin natin ang pag-log ng isang operasyon, dapat nating baguhin ito sa isang lugar.

Ito ay isang napakahalagang punto - dahil ang lahat ng mga paliwanag ng SRP na nasa itaas ay nagsabi na kinakailangang durugin ang mga uri habang sila ay dinudurog, iyon ay, nagpataw sila ng "itaas na limitasyon" sa laki ng bagay, at ngayon pinag-uusapan na natin ang tungkol sa "lower limit" . Sa ibang salita, Ang SRP ay hindi lamang nangangailangan ng "pagdurog habang dinudurog", kundi pati na rin na huwag lumampas - "huwag durugin ang mga magkakaugnay na bagay". Ito ang mahusay na labanan sa pagitan ng labaha ni Occam at ng taong unggoy!

Prinsipyo ng Iisang Pananagutan. Hindi kasing simple ng tila

Ngayon ang umiinom ay dapat na mas mabuti ang pakiramdam. Bilang karagdagan sa katotohanan na hindi na kailangang hatiin ang IPourLogger logger sa tatlong klase, maaari rin nating pagsamahin ang lahat ng mga logger sa isang uri:

class OperationLogger{
    public OperationLogger(string operationName){/*..*/}
    public void LogBefore(object[] args){/*...*/}       
    public void LogAfter(object[] args){/*..*/}
    public void LogError(object[] args, exception e){/*..*/}
}

At kung magdaragdag kami ng ikaapat na uri ng operasyon, kung gayon ang pag-log para dito ay handa na. At ang code ng mga operasyon mismo ay malinis at walang ingay sa imprastraktura.

Bilang resulta, mayroon kaming 5 klase para sa paglutas ng problema sa pag-inom:

  • Pagbuhos ng operasyon
  • Operasyon sa pag-inom
  • Operasyon ng jamming
  • Magtotroso
  • Facade ng inuman

Ang bawat isa sa kanila ay mahigpit na responsable para sa isang pag-andar at may isang dahilan para sa pagbabago. Ang lahat ng mga panuntunang katulad ng pagbabago ay matatagpuan sa malapit.

Halimbawa sa totoong buhay

Minsan kaming nagsulat ng isang serbisyo para sa awtomatikong pagrehistro ng isang b2b client. At lumitaw ang isang paraan ng DIYOS para sa 200 linya ng magkatulad na nilalaman:

  • Pumunta sa 1C at gumawa ng account
  • Gamit ang account na ito, pumunta sa module ng pagbabayad at gawin ito doon
  • Suriin na ang isang account na may ganoong account ay hindi pa nagagawa sa pangunahing server
  • Gumawa ng bagong account
  • Idagdag ang mga resulta ng pagpaparehistro sa module ng pagbabayad at ang 1c na numero sa serbisyo ng mga resulta ng pagpaparehistro
  • Magdagdag ng impormasyon ng account sa talahanayang ito
  • Gumawa ng point number para sa client na ito sa point service. Ipasa ang iyong 1c account number sa serbisyong ito.

At mayroong humigit-kumulang 10 pang negosyo sa listahang ito na may kahila-hilakbot na koneksyon. Halos lahat ay nangangailangan ng object ng account. Ang point ID at pangalan ng kliyente ay kailangan sa kalahati ng mga tawag.

Pagkatapos ng isang oras ng refactoring, nagawa naming paghiwalayin ang code ng imprastraktura at ilan sa mga nuances ng pagtatrabaho sa isang account sa magkakahiwalay na pamamaraan/klase. Pinadali ng paraan ng Diyos, ngunit may natitira pang 100 linya ng code na ayaw lang maalis sa pagkakabuhol.

Pagkatapos lamang ng ilang araw ay naging malinaw na ang kakanyahan ng "magaan" na paraan na ito ay isang algorithm ng negosyo. At ang orihinal na paglalarawan ng mga teknikal na pagtutukoy ay medyo kumplikado. At ito ay ang pagtatangkang hatiin ang pamamaraang ito sa mga piraso na lalabag sa SRP, at hindi kabaliktaran.

Formalismo.

Oras na para iwanan ang ating lasing. Patuyuin ang iyong mga luha - tiyak na babalikan natin ito balang araw. Ngayon, gawing pormal natin ang kaalaman mula sa artikulong ito.

Pormalismo 1. Kahulugan ng SRP

  1. Paghiwalayin ang mga elemento upang ang bawat isa sa kanila ay may pananagutan sa isang bagay.
  2. Ang pananagutan ay nangangahulugang "dahilan para magbago." Iyon ay, ang bawat elemento ay may isang dahilan lamang para sa pagbabago, sa mga tuntunin ng lohika ng negosyo.
  3. Mga potensyal na pagbabago sa lohika ng negosyo. dapat i-localize. Ang mga elemento na nagbabago nang sabay-sabay ay dapat nasa malapit.

Formalismo 2. Kinakailangang pamantayan sa pagsusulit sa sarili.

Wala akong nakitang sapat na pamantayan para matupad ang SRP. Ngunit may mga kinakailangang kondisyon:

1) Tanungin ang iyong sarili kung ano ang ginagawa ng klase/paraan/modyul/serbisyong ito. kailangan mong sagutin ito ng isang simpleng kahulugan. ( Salamat Brightori )

mga paliwanag

Gayunpaman, kung minsan napakahirap na makahanap ng isang simpleng kahulugan

2) Ang pag-aayos ng bug o pagdaragdag ng bagong feature ay nakakaapekto sa pinakamababang bilang ng mga file/klase. Sa isip - isa.

mga paliwanag

Dahil ang responsibilidad (para sa isang tampok o bug) ay naka-encapsulated sa isang file/klase, alam mo kung saan eksaktong titingnan at kung ano ang ie-edit. Halimbawa: ang tampok ng pagbabago ng output ng mga pagpapatakbo ng pag-log ay mangangailangan ng pagbabago lamang ng logger. Hindi na kailangang tumakbo sa natitirang bahagi ng code.

Ang isa pang halimbawa ay ang pagdaragdag ng bagong kontrol sa UI, katulad ng mga nauna. Kung pipilitin ka nitong magdagdag ng 10 iba't ibang entity at 15 iba't ibang converter, mukhang nasobrahan mo na ito.

3) Kung ang ilang mga developer ay nagtatrabaho sa iba't ibang mga tampok ng iyong proyekto, kung gayon ang posibilidad ng isang pagsasanib ng pagsasanib, iyon ay, ang posibilidad na ang parehong file/klase ay mababago ng ilang mga developer sa parehong oras, ay minimal.

mga paliwanag

Kung, kapag nagdaragdag ng isang bagong operasyon na "Ibuhos ang vodka sa ilalim ng mesa", kailangan mong maapektuhan ang logger, ang operasyon ng pag-inom at pagbuhos, kung gayon mukhang ang mga responsibilidad ay nahahati nang baluktot. Siyempre, hindi ito laging posible, ngunit dapat nating subukang bawasan ang figure na ito.

4) Kapag tinanong ng isang paglilinaw na tanong tungkol sa lohika ng negosyo (mula sa isang developer o manager), mahigpit kang pumunta sa isang klase/file at tumanggap lamang ng impormasyon mula doon.

mga paliwanag

Ang mga feature, panuntunan o algorithm ay nakasulat nang compact, bawat isa sa isang lugar, at hindi nakakalat ng mga flag sa buong espasyo ng code.

5) Malinaw ang pagpapangalan.

mga paliwanag

Ang aming klase o pamamaraan ay responsable para sa isang bagay, at ang responsibilidad ay makikita sa pangalan nito

AllManagersManagerService - malamang na isang klase ng Diyos
LocalPayment - malamang na hindi

Formalismo 3. Occam-first development methodology.

Sa simula ng disenyo, ang taong unggoy ay hindi alam at hindi nararamdaman ang lahat ng mga subtleties ng problema na nalutas at maaaring magkamali. Maaari kang magkamali sa iba't ibang paraan:

  • Gawing masyadong malaki ang mga bagay sa pamamagitan ng pagsasama-sama ng iba't ibang responsibilidad
  • Reframing sa pamamagitan ng paghahati ng isang responsibilidad sa maraming iba't ibang uri
  • Maling tukuyin ang mga hangganan ng responsibilidad

Mahalagang tandaan ang panuntunan: "mas mahusay na gumawa ng isang malaking pagkakamali," o "kung hindi ka sigurado, huwag paghiwalayin ito." Kung, halimbawa, ang iyong klase ay naglalaman ng dalawang responsibilidad, kung gayon ito ay mauunawaan pa rin at maaaring hatiin sa dalawa na may kaunting pagbabago sa code ng kliyente. Ang pag-assemble ng isang baso mula sa mga shards ng salamin ay kadalasang mas mahirap dahil sa konteksto na kumakalat sa ilang mga file at ang kakulangan ng mga kinakailangang dependency sa client code.

Oras na para tawagin itong isang araw

Ang saklaw ng SRP ay hindi limitado sa OOP at SOLID. Nalalapat ito sa mga pamamaraan, function, klase, module, microservice at serbisyo. Nalalapat ito sa parehong pag-unlad ng "figax-figax-and-prod" at "rocket-science", na ginagawang mas mahusay ang mundo sa lahat ng dako. Kung iisipin mo, ito ang halos pangunahing prinsipyo ng lahat ng engineering. Ang mekanikal na engineering, mga sistema ng kontrol, at sa katunayan ang lahat ng mga kumplikadong sistema ay binuo mula sa mga bahagi, at ang "underfragmentation" ay nag-aalis ng kakayahang umangkop sa mga taga-disenyo, ang "overfragmentation" ay nag-aalis sa mga taga-disenyo ng kahusayan, at ang mga maling hangganan ay nag-aalis sa kanila ng katwiran at kapayapaan ng isip.

Prinsipyo ng Iisang Pananagutan. Hindi kasing simple ng tila

Ang SRP ay hindi likas na naimbento at hindi bahagi ng eksaktong agham. Ito ay lumalabas sa ating biyolohikal at sikolohikal na mga limitasyon. Isa lamang itong paraan upang makontrol at bumuo ng mga kumplikadong sistema gamit ang utak ng taong unggoy. Sinasabi niya sa amin kung paano mabulok ang isang sistema. Ang orihinal na pormulasyon ay nangangailangan ng isang patas na halaga ng telepathy, ngunit umaasa ako na ang artikulong ito ay nililimas ang ilan sa mga smokescreen.

Pinagmulan: www.habr.com

Magdagdag ng komento