Enkel verantwoordelikheid beginsel, ook bekend as die beginsel van enkel verantwoordelikheid,
aka die beginsel van eenvormige veranderlikheid - 'n uiters gladde ou om te verstaan en so 'n senuweeagtige vraag by 'n programmeerderonderhoud.
My eerste ernstige kennismaking met hierdie beginsel het aan die begin van die eerste jaar plaasgevind, toe die jong en groentjies bos toe geneem is om studente van larwes te maak – regte studente.
In die bos is ons in groepe van 8-9 mense elk opgedeel en het 'n kompetisie gehou - watter groep sou die vinnigste 'n bottel vodka drink, mits die eerste persoon van die groep vodka in 'n glas skink, die tweede dit drink, en die derde het 'n versnapering. Die eenheid wat sy operasie voltooi het, skuif na die einde van die groep se tou.
Die geval waar die tougrootte 'n veelvoud van drie was, was 'n goeie implementering van SRP.
Definisie 1. Enkel verantwoordelikheid.
Die amptelike definisie van die enkelverantwoordelikheidsbeginsel (SRP) bepaal dat elke entiteit sy eie verantwoordelikheid en bestaansrede het, en dit het net een verantwoordelikheid.
Oorweeg die voorwerp "drinker" (Tipper).
Om die SRP-beginsel te implementeer, sal ons die verantwoordelikhede in drie verdeel:
- Een skink (Giet Bedryf)
- Een drink (DrinkUp Operasie)
- Een het 'n versnapering (TakeBiteOperation)
Elkeen van die deelnemers aan die proses is verantwoordelik vir een komponent van die proses, dit wil sê, het een atoomverantwoordelikheid - om te drink, te skink of te peusel.
Die drinkgat is op sy beurt 'n fasade vir hierdie operasies:
сlass Tippler {
//...
void Act(){
_pourOperation.Do() // налить
_drinkUpOperation.Do() // выпить
_takeBiteOperation.Do() // закусить
}
}
Зачем?
Die menslike programmeerder skryf kode vir die aapmens, en die aapmens is onoplettend, dom en altyd haastig. Hy kan ongeveer 3 - 7 terme op een slag hou en verstaan.
In die geval van 'n dronkaard is daar drie van hierdie terme. As ons egter die kode met een vel skryf, sal dit hande, brille, gevegte en eindelose argumente oor politiek bevat. En dit alles sal in die liggaam van een metode wees. Ek is seker jy het sulke kode in jou praktyk gesien. Nie die mees menslike toets vir die psige nie.
Aan die ander kant is die aapmens ontwerp om werklike wêreldvoorwerpe in sy kop te simuleer. In sy verbeelding kan hy hulle bymekaar druk, nuwe voorwerpe daaruit aanmekaarsit en dit op dieselfde manier uitmekaar haal. Stel jou 'n ou modelmotor voor. In jou verbeelding kan jy die deur oopmaak, die deurbekleding losdraai en daar die vensterhysmeganismes sien, waarbinne daar ratte sal wees. Maar jy kan nie al die komponente van die masjien op dieselfde tyd, in een "lys" sien nie. Die “aapman” kan darem nie.
Daarom ontbind menslike programmeerders komplekse meganismes in 'n stel minder komplekse en werkende elemente. Dit kan egter op verskillende maniere ontbind word: in baie ou motors gaan die lugkanaal by die deur in, en in moderne motors verhoed 'n fout in die slotelektronika dat die enjin begin, wat tydens herstelwerk 'n probleem kan wees.
Dus, SRP is 'n beginsel wat verduidelik HOE om te ontbind, dit wil sê waar om die skeidslyn te trek.
Hy sê dat dit nodig is om te ontbind volgens die beginsel van verdeling van "verantwoordelikheid", dit wil sê volgens die take van sekere voorwerpe.
Kom ons keer terug na drink en die voordele wat die aapman tydens ontbinding ontvang:
- Die kode het op elke vlak uiters duidelik geword
- Die kode kan deur verskeie programmeerders gelyktydig geskryf word (elkeen skryf 'n aparte element)
- Outomatiese toetsing word vereenvoudig - hoe eenvoudiger die element is, hoe makliker is dit om te toets
- Die samestelling van die kode verskyn - jy kan vervang DrinkUp Operasie na 'n operasie waarin 'n dronkaard vloeistof onder die tafel gooi. Of vervang die skinkbewerking met ’n bewerking waarin jy wyn en water of vodka en bier meng. Afhangende van besigheidsvereistes, kan jy alles doen sonder om die metodekode aan te raak Tippler.Act.
- Van hierdie bewerkings kan jy die vraat vou (met slegs TakeBitOperation), Alkoholies (gebruik slegs DrinkUp Operasie direk uit die bottel) en voldoen aan baie ander besigheidsvereistes.
(O, dit blyk dat dit reeds 'n OCP-beginsel is, en ek het die verantwoordelikheid van hierdie pos oortree)
En natuurlik die nadele:
- Ons sal meer tipes moet skep.
- 'n Dronkman drink 'n paar uur later vir die eerste keer as wat hy andersins sou hê.
Definisie 2. Eenvormige veranderlikheid.
Laat my toe, here! Die drinkklas het ook 'n enkele verantwoordelikheid - dit drink! En oor die algemeen is die woord "verantwoordelikheid" 'n uiters vae konsep. Iemand is verantwoordelik vir die lot van die mensdom, en iemand is verantwoordelik vir die grootmaak van die pikkewyne wat by die paal omgeslaan is.
Kom ons kyk na twee implementerings van die drinker. Die eerste een, hierbo genoem, bevat drie klasse - skink, drink en peuselhappie.
Die tweede is geskryf deur die “Forward and Only Forward” metodologie en bevat al die logika in die metode Wet:
//Не тратьте время на изучение этого класса. Лучше съешьте печеньку
с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);
}
}
}Albei hierdie klasse lyk, vanuit die oogpunt van 'n buite-waarnemer, presies dieselfde en deel dieselfde verantwoordelikheid van "drink".
Verwarring!
Dan gaan ons aanlyn en vind 'n ander definisie van SRP uit - die enkele veranderlikheidsbeginsel.
SCP verklaar dat "'n Module het een en slegs een rede om te verander". Dit wil sê, "Verantwoordelikheid is 'n rede vir verandering."
(Dit blyk dat die ouens wat met die oorspronklike definisie vorendag gekom het, vol vertroue was in die telepatiese vermoëns van die aapmens)
Nou val alles in plek. Afsonderlik kan ons die skink-, drink- en peuselprosedures verander, maar in die drinker self kan ons net die volgorde en samestelling van bewerkings verander, byvoorbeeld deur die peuselhappie te skuif voordat dit gedrink word of die lees van 'n roosterbrood by te voeg.
In die “Forward and Only Forward”-benadering word alles wat verander kan word slegs in die metode verander Wet. Dit kan leesbaar en effektief wees wanneer daar min logika is en dit selde verander, maar dit eindig dikwels in verskriklike metodes van 500 reëls elk, met meer as-stellings as wat vereis word vir Rusland om by NAVO aan te sluit.
Definisie 3. Lokalisering van veranderinge.
Drinkers verstaan dikwels nie hoekom hulle wakker geword het in iemand anders se woonstel, of waar hul selfoon is nie. Dit is tyd om gedetailleerde logboeke by te voeg.
Kom ons begin aanteken met die gietproses:
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}");
}
}Deur dit in te kap Giet Bedryf, ons het wys opgetree vanuit die oogpunt van verantwoordelikheid en inkapseling, maar nou word ons verwar met die beginsel van veranderlikheid. Benewens die operasie self, wat kan verander, word die logging self ook veranderlik. Jy sal moet skei en 'n spesiale logger vir die gietbewerking moet skep:
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)
}
}
}Die noukeurige leser sal dit agterkom LogAfter, LogBefore и OpFout kan ook individueel verander word, en sal, na analogie van die vorige stappe, drie klasse skep: PourLoggerBefore, PourLoggerAfter и PourErrorLogger.
En as ons onthou dat daar drie operasies vir 'n drinker is, kry ons nege houtkapklasse. Gevolglik bestaan die hele drinkkring uit 14 (!!!) klasse.
Hiperbool? Skaars! 'n Aapman met 'n ontbindingsgranaat sal die "skinkbak" in 'n karaf, 'n glas, skinkoperateurs, 'n watervoorsieningdiens, 'n fisiese model van die botsing van molekules verdeel, en vir die volgende kwartaal sal hy probeer om die afhanklikhede te ontwrig sonder globale veranderlikes. En glo my, hy sal nie ophou nie.
Dit is op hierdie stadium dat baie tot die gevolgtrekking kom dat SRP sprokies uit pienk koninkryke is, en weggaan om noedels te speel...
... sonder om ooit te leer oor die bestaan van 'n derde definisie van Srp:
“Die enkelverantwoordelikheidsbeginsel stel dit dinge wat soortgelyk is aan verandering moet op een plek gestoor word". of "Wat verander saam moet op een plek gehou word"
Dit wil sê, as ons die aantekening van 'n operasie verander, dan moet ons dit op een plek verander.
Dit is 'n baie belangrike punt - aangesien al die verduidelikings van SRP hierbo gesê het dat dit nodig was om die tipes te verpletter terwyl hulle gebreek word, dit wil sê hulle het 'n "boonste limiet" op die grootte van die voorwerp opgelê, en nou ons praat reeds van 'n "onderste limiet" . Met ander woorde, SRP vereis nie net "verplettering terwyl dit vergruis word nie", maar ook om dit nie te oordoen nie - "moenie ineenlopende dinge vergruis nie". Dit is die groot stryd tussen Occam se skeermes en die aapmens!
Nou behoort die drinker beter te voel. Benewens die feit dat dit nie nodig is om die IPourLogger-logger in drie klasse te verdeel nie, kan ons ook alle loggers in een tipe kombineer:
class OperationLogger{
public OperationLogger(string operationName){/*..*/}
public void LogBefore(object[] args){/*...*/}
public void LogAfter(object[] args){/*..*/}
public void LogError(object[] args, exception e){/*..*/}
}En as ons 'n vierde tipe operasie byvoeg, dan is die aanteken daarvoor reeds gereed. En die kode van die bedrywighede self is skoon en vry van infrastruktuurgeraas.
As gevolg hiervan het ons 5 klasse om die drankprobleem op te los:
- Gietbewerking
- Drink operasie
- Stooroperasie
- Logger
- Drinker fasade
Elkeen van hulle is streng verantwoordelik vir een funksionaliteit en het een rede vir verandering. Alle reëls soortgelyk aan verandering is naby geleë.
Werklike voorbeeld
Ons het eenkeer 'n diens geskryf om 'n b2b-kliënt outomaties te registreer. En 'n GOD-metode het verskyn vir 200 reëls van soortgelyke inhoud:
- Gaan na 1C en skep 'n rekening
- Met hierdie rekening, gaan na die betalingsmodule en skep dit daar
- Kontroleer dat 'n rekening met hierdie rekeningnommer nie in die hoofrekening geskep is nie. bediener
- Skep 'n nuwe rekening
- Voeg die registrasieresultate in die betalingsmodule en die 1c-nommer by die registrasieuitslaediens
- Voeg rekeninginligting by hierdie tabel
- Skep 'n puntnommer vir hierdie kliënt in die puntdiens. Gee jou 1c rekeningnommer aan hierdie diens.
En daar was nog ongeveer 10 sakebedrywighede op hierdie lys met verskriklike konnektiwiteit. Byna almal het die rekeningobjek nodig gehad. Die punt-ID en kliëntnaam was in die helfte van die oproepe nodig.
Na 'n uur se herfaktorering kon ons die infrastruktuurkode en sommige van die nuanses van werk met 'n rekening in aparte metodes/klasse skei. Die God-metode het dit makliker gemaak, maar daar was 100 reëls kode oor wat net nie ontwrig wou word nie.
Eers na 'n paar dae het dit duidelik geword dat die essensie van hierdie "liggewig" metode 'n besigheidsalgoritme is. En dat die oorspronklike beskrywing van die tegniese spesifikasies redelik kompleks was. En dit is die poging om hierdie metode in stukke te breek wat die SRP sal oortree, en nie omgekeerd nie.
Formalisme.
Dit is tyd om ons dronk alleen te los. Droog jou trane af – ons sal beslis eendag daarna terugkeer. Kom ons formaliseer nou die kennis uit hierdie artikel.
Formalisme 1. Definisie van SRP
- Skei die elemente sodat elkeen van hulle vir een ding verantwoordelik is.
- Verantwoordelikheid staan vir "rede om te verander." Dit wil sê, elke element het net een rede vir verandering, in terme van besigheidslogika.
- Potensiële veranderinge aan besigheidslogika. gelokaliseer moet word. Elemente wat sinchronies verander moet naby wees.
Formalisme 2. Nodige selftoetskriteria.
Ek het nie voldoende kriteria gesien om aan die SRP te voldoen nie. Maar daar is die nodige voorwaardes:
1) Vra jouself af wat hierdie klas/metode/module/diens doen. jy moet dit met 'n eenvoudige definisie beantwoord. ( Dankie )
verduidelikings
Dit is egter soms baie moeilik om 'n eenvoudige definisie te vind
2) Om 'n fout reg te stel of 'n nuwe kenmerk by te voeg, beïnvloed 'n minimum aantal lêers/klasse. Ideaal gesproke - een.
verduidelikings
Aangesien verantwoordelikheid (vir 'n kenmerk of fout) in een lêer/klas ingekapsuleer is, weet jy presies waar om te kyk en wat om te redigeer. Byvoorbeeld: die kenmerk van die verandering van die uitvoer van logbewerkings sal vereis dat slegs die logger verander word. Dit is nie nodig om deur die res van die kode te hardloop nie.
Nog 'n voorbeeld is om 'n nuwe UI-beheer by te voeg, soortgelyk aan die voriges. As dit jou dwing om 10 verskillende entiteite en 15 verskillende omskakelaars by te voeg, lyk dit of jy dit oordoen.
3) As verskeie ontwikkelaars aan verskillende kenmerke van jou projek werk, dan is die waarskynlikheid van 'n samesmeltingskonflik, dit wil sê die waarskynlikheid dat dieselfde lêer/klas gelyktydig deur verskeie ontwikkelaars verander sal word, minimaal.
verduidelikings
As jy, wanneer jy 'n nuwe bewerking "Gooi vodka onder die tafel" byvoeg, die houtkapper, die werking van drink en skink moet beïnvloed, dan lyk dit of die verantwoordelikhede skeef verdeel is. Dit is natuurlik nie altyd moontlik nie, maar ons moet probeer om hierdie syfer te verminder.
4) Wanneer 'n verduidelikende vraag oor besigheidslogika (van 'n ontwikkelaar of bestuurder) gevra word, gaan jy streng in een klas/lêer in en ontvang slegs inligting van daar af.
verduidelikings
Kenmerke, reëls of algoritmes word kompak geskryf, elk op een plek, en nie met vlae deur die koderuimte gestrooi nie.
5) Die benaming is duidelik.
verduidelikings
Ons klas of metode is verantwoordelik vir een ding, en die verantwoordelikheid word in sy naam weerspieël
AllManagersManagerService - heel waarskynlik 'n God-klas
LocalPayment - waarskynlik nie
Formalisme 3. Occam-eerste ontwikkelingsmetodologie.
Aan die begin van ontwerp weet die aapman nie en voel nie al die subtiliteite van die probleem wat opgelos word nie en kan 'n fout maak. Jy kan op verskillende maniere foute maak:
- Maak voorwerpe te groot deur verskillende verantwoordelikhede saam te voeg
- Herraamwerk deur 'n enkele verantwoordelikheid in baie verskillende tipes te verdeel
- Definieer die grense van verantwoordelikheid verkeerd
Dit is belangrik om die reël te onthou: "dit is beter om 'n groot fout te maak," of "as jy nie seker is nie, moenie dit opdeel nie." As jou klas byvoorbeeld twee verantwoordelikhede bevat, is dit steeds verstaanbaar en kan dit in twee verdeel word met minimale veranderinge aan die kliëntkode. Die samestelling van 'n glas uit glasskerwe is gewoonlik moeiliker as gevolg van die konteks wat oor verskeie lêers versprei is en die gebrek aan nodige afhanklikhede in die kliëntkode.
Dit is tyd om dit 'n dag te noem
Die omvang van SRP is nie beperk tot OOP en SOLID nie. Dit is van toepassing op metodes, funksies, klasse, modules, mikrodienste en dienste. Dit is van toepassing op beide "figax-figax-and-prod" en "rocket-science" ontwikkeling, wat die wêreld oral 'n bietjie beter maak. As jy daaroor dink, is dit amper die fundamentele beginsel van alle ingenieurswese. Meganiese ingenieurswese, beheerstelsels, en inderdaad alle komplekse stelsels word uit komponente gebou, en "onderfragmentasie" ontneem ontwerpers van buigsaamheid, "oorfragmentasie" ontneem ontwerpers van doeltreffendheid, en verkeerde grense ontneem hulle van rede en gemoedsrus.
SRP word nie deur die natuur uitgevind nie en is nie deel van presiese wetenskap nie. Dit breek uit ons biologiese en psigologiese beperkings Dit is net 'n manier om komplekse stelsels te beheer en te ontwikkel deur die aapmensbrein te gebruik. Hy vertel ons hoe om 'n stelsel te ontbind. Die oorspronklike formulering het 'n redelike hoeveelheid telepatie vereis, maar ek hoop dat hierdie artikel 'n bietjie van die rookskerm skoonmaak.
Bron: will.com
