Eenzeg Verantwortung Prinzip. Net esou einfach wéi et schéngt

Eenzeg Verantwortung Prinzip. Net esou einfach wéi et schéngt Eenzeg Verantwortungsprinzip, och bekannt als de Prinzip vun der eenzeger Verantwortung,
alias de Prinzip vun der eenheetlecher Verännerlechkeet - eng extrem rutscheg Guy ze verstoen an esou eng nervös Fro bei engem Programméierer Interview.

Meng éischt sérieux Bekannte mat dësem Prinzip huet am Ufank vum éischte Joer stattfonnt, wéi déi jonk a gréng an de Bësch geholl goufen, fir Studenten aus Larven ze maachen - richteg Studenten.

Am Bësch ware mir a Gruppe vun jeeweils 8-9 Leit opgedeelt an hunn e Concours gemaach - wéi eng Grupp am schnellsten eng Fläsch Wodka drénkt, virausgesat datt déi éischt Persoun aus dem Grupp Wodka an e Glas schëpt, déi zweet drénkt, an déi drëtt huet e Snack. D'Eenheet, déi seng Operatioun ofgeschloss huet, bewegt op d'Enn vun der Schlaang vun der Grupp.

De Fall wou d'Schlaanggréisst e Multiple vun dräi war, war eng gutt Implementatioun vu SRP.

Definitioun 1. Single Responsabilitéit.

Déi offiziell Definitioun vum Single Responsibility Principle (SRP) seet datt all Entitéit seng eege Verantwortung a Grond fir d'Existenz huet, an et huet nëmmen eng Verantwortung.

Betruecht den Objet "Drénker" (Tippler).
Fir de SRP Prinzip ëmzesetzen, wäerte mir d'Verantwortung an dräi opdeelen:

  • Een schenkt (PourOperation)
  • Een drénken (DrinkUpOperatioun)
  • Een huet e Snack (TakeBiteOperation)

Jidderee vun de Participanten am Prozess ass verantwortlech fir ee Bestanddeel vum Prozess, dat heescht, huet eng atomar Verantwortung - drénken, pour oder Snack.

D'Drénklach, am Tour, ass eng Fassad fir dës Operatiounen:

сlass Tippler {
    //...
    void Act(){
        _pourOperation.Do() // налить
        _drinkUpOperation.Do() // выпить
        _takeBiteOperation.Do() // закусить
    }
}

Eenzeg Verantwortung Prinzip. Net esou einfach wéi et schéngt

Firwat?

De mënschleche Programméierer schreift Code fir den Ape-Mann, an den Ape-Mann ass onopmerksam, domm an ëmmer presséiert. Hie kann ongeféier 3 - 7 Begrëffer gläichzäiteg halen a verstoen.
Am Fall vun engem Gedrénks ginn et dräi vun dëse Begrëffer. Wéi och ëmmer, wa mir de Code mat engem Blat schreiwen, da wäert et Hänn, Brëller, Kämpf an endlos Argumenter iwwer Politik enthalen. An all dat wäert am Kierper vun enger Method sinn. Ech sécher Dir esou Code an Ärer Praxis gesinn hunn. Net dee mënschlechsten Test fir d'Psyche.

Op der anerer Säit ass den Affemann entwéckelt fir real Weltobjekter a sengem Kapp ze simuléieren. A senger Fantasie kann hien se zesummen drécken, nei Objete doraus zesummesetzen an op déiselwecht Manéier ofbauen. Stellt Iech en ale Modellauto vir. An Ärer Fantasi kënnt Dir d'Dier opmaachen, d'Dier Trimm ofschrauwen an do d'Fensterliftmechanismen gesinn, an deenen et Gears gëtt. Awer Dir kënnt net all Komponente vun der Maschinn gläichzäiteg gesinn, an enger "Oplëschtung". Op d'mannst kann de "Affemann" net.

Dofir zerstéieren mënschlech Programméierer komplex Mechanismen an eng Rei vu manner komplexen a funktionnéierende Elementer. Wéi och ëmmer, et kann op verschidde Manéieren ofgebaut ginn: a villen alen Autoen geet d'Loftkanal an d'Dier, an an modernen Autoen verhënnert e Feeler an der Sperrelektronik de Motor auszeféieren, wat e Problem bei der Reparatur ka sinn.

Elo, SRP ass e Prinzip deen erkläert WEI ze zerstéieren, dat ass, wou d'Trennlinn ze zéien.

Hie seet, datt et néideg ass, no dem Prinzip vun der Divisioun vun der "Verantwortung" ze zerstéieren, dat heescht no den Aufgaben vu bestëmmten Objeten.

Eenzeg Verantwortung Prinzip. Net esou einfach wéi et schéngt

Komme mer zréck op d'Drénken an d'Virdeeler déi den Affemann während der Zersetzung kritt:

  • De Code ass extrem kloer op all Niveau ginn
  • De Code ka vu verschiddene Programméierer gläichzäiteg geschriwwe ginn (jidderee schreift en separaten Element)
  • Automatiséiert Testen ass vereinfacht - wat méi einfach d'Element ass, dest méi einfach ass et ze testen
  • D'Kompositioun vum Code erschéngt - Dir kënnt ersetzen DrinkUpOperatioun op eng Operatioun, an där en Drénken Flëssegkeet ënnert den Dësch schenkt. Oder d'Schéissoperatioun ersetzen duerch eng Operatioun, an där Dir Wäin a Waasser oder Wodka a Béier mëscht. Ofhängeg vun de Geschäftsbedéngungen, kënnt Dir alles maachen ouni de Methodcode ze beréieren Tippler.Act.
  • Vun dësen Operatiounen kënnt Dir de Glutton ausklappen (nëmmen TakeBitOperation), Alkohol (nëmmen benotzt DrinkUpOperatioun direkt aus der Fläsch) an treffen vill aner Geschäftsbedéngungen.

(Oh, et schéngt dat schonn en OCP Prinzip ass, an ech hunn d'Verantwortung vun dësem Post verletzt)

An, natierlech, d'Nodeeler:

  • Mir mussen méi Zorte kreéieren.
  • En Drénken drénkt fir d'éischt e puer Stonnen méi spéit wéi en soss hätt.

Definitioun 2. Vereenegt Verännerlechkeet.

Loosst mech, Hären! D'Drénkklass huet och eng eenzeg Verantwortung - et drénken! An allgemeng ass d'Wuert "Verantwortung" en extrem vague Konzept. Een ass verantwortlech fir d'Schicksal vun der Mënschheet, an een ass verantwortlech fir d'Pinguine z'erhéijen, déi um Pol ëmgedréit goufen.

Loosst eis zwou Implementatioune vum Drénken betruechten. Déi éischt, uewen ernimmt, enthält dräi Klassen - pour, drénken a Snack.

Déi zweet ass duerch d'Methodologie "Forward and Only Forward" geschriwwe ginn an enthält all Logik an der Method Gesetz:

//Не тратьте время  на изучение этого класса. Лучше съешьте печеньку
с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);
    }
   }
}

Béid vun dëse Klassen, aus der Siicht vun engem ausserhalb Beobachter, kucken genee d'selwecht an deelen déi selwecht Verantwortung vum "Drénken".

Duercherneen!

Da gi mir online an entdeckt eng aner Definitioun vu SRP - den Single Changeability Principle.

SCP seet datt "E Modul huet een an nëmmen ee Grond ze änneren". Dat ass, "Verantwortung ass e Grond fir Ännerung."

(Et schéngt, datt d'Kärelen, déi mat der ursprénglecher Definitioun komm sinn, zouversiichtlech an den telepathesche Fäegkeete vum Affemann waren)

Elo fällt alles op Plaz. Getrennt kënne mir d'Schëpfer, Drénken a Snackprozeduren änneren, awer am Drénken selwer kënne mir nëmmen d'Sequenz an d'Zesummesetzung vun den Operatiounen änneren, zum Beispill andeems Dir de Snack virum Drénken beweegt oder d'Liesen vun engem Toast addéieren.

An der Approche "Forward and Only Forward" gëtt alles wat geännert ka ginn nëmmen an der Method geännert Gesetz. Dëst kann liesbar an effektiv sinn wann et wéineg Logik ass an et selten ännert, awer dacks endet et a schreckleche Methode vu 500 Linnen all, mat méi Wann-Aussoen wéi néideg fir Russland fir an der NATO ze bäitrieden.

Definitioun 3. Lokalisatioun vun Ännerungen.

Drénken verstinn dacks net firwat se an engem aneren Appartement erwächt sinn oder wou hiren Handy ass. Et ass Zäit fir detailléiert Logbicher ze addéieren.

Loosst eis mam Logprozess ufänken:

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}");
    }
}

Andeems Dir et akapselt PourOperation, Mir hunn aus der Siicht vun der Verantwortung an der Verkapselung clever gehandelt, awer elo si mir verwiesselt mam Prinzip vun der Variabilitéit. Nieft der Operatioun selwer, déi ännere kann, gëtt de Logbuch selwer och verännerbar. Dir musst trennen an e spezielle Logger fir d'Schichtoperatioun erstellen:

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)
        }
    }
}

De virsiichtege Lieser wäert dat bemierken LogAfter, Log Virun и OnError kann och individuell geännert ginn, an, duerch Analogie mat de fréiere Schrëtt, wäerten dräi Klassen erstellen: PourLoggerBefore, PourLoggerAfter и PourErrorLogger.

An erënneren, datt et dräi Operatiounen fir engem drénken, mir kréien néng Logged Klassen. Als Resultat besteet de ganzen Drénkkrees aus 14 (!!!) Klassen.

Hyperbel? Kaum! En Affemann mat enger Zersetzungsgranat wäert den "Pourer" an en Dekanter, e Glas, Schéissbetreiber, e Waasserversuergungsdéngscht, e physikalesche Modell vun der Kollisioun vu Molekülen opdeelen, a fir den nächste Véierel probéiert hien d'Ofhängegkeeten z'entfalen ouni global Verännerlechen. A gleew mir, hie wäert net ophalen.

Et ass zu dësem Zäitpunkt datt vill zu der Conclusioun kommen datt SRP Mäerchen aus rosa Kinnekräicher sinn, a fort goen fir Nuddelen ze spillen ...

... ouni jeemools iwwer d'Existenz vun enger drëtter Definitioun vu Srp ze léieren:

"De Prinzip vun der eenzeger Verantwortung seet dat Saachen déi ähnlech wéi änneren sollen op enger Plaz gespäichert ginn". oder "Wat ännert zesummen soll op enger Plaz gehale ginn"

Dat ass, wa mir de Logbuch vun enger Operatioun änneren, da musse mir et op enger Plaz änneren.

Dëst ass e ganz wichtege Punkt - well all d'Erklärungen vum SRP, déi hei uewen waren, soten datt et noutwendeg wier d'Typen ze zerbriechen wärend se zerquetscht ginn, dat heescht, si hunn eng "iewescht Limit" op d'Gréisst vum Objet opgesat, an elo mir schwätze schonn vun enger "Ënnergrenz" . An anere Wierder, SRP erfuerdert net nëmmen "Krësselen beim Zerstéieren", awer och net ze iwwerdreiwen - "Krëschen keng interlocking Saachen". Dëst ass déi grouss Schluecht tëscht dem Occam sengem Raséierapparat an dem Affemann!

Eenzeg Verantwortung Prinzip. Net esou einfach wéi et schéngt

Elo soll den Drénken besser fillen. Zousätzlech zu der Tatsaach datt et net néideg ass den IPourLogger Logger an dräi Klassen opzedeelen, kënne mir och all Logger an eng Zort kombinéieren:

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

A wa mir eng véiert Aart vun Operatioun addéieren, dann ass de Logbuch fir et scho fäerdeg. An de Code vun den Operatiounen selwer ass propper a fräi vun Infrastrukturrauschen.

Als Resultat hu mir 5 Klassen fir den Drénkproblem ze léisen:

  • Schéiss Operatioun
  • Drénken Operatioun
  • Jamming Operatioun
  • Logger
  • Drénkfassad

Jidderee vun hinnen ass strikt verantwortlech fir eng Funktionalitéit an huet ee Grond fir ze änneren. All Regelen ähnlech ze änneren sinn an der Géigend etabléiert.

Real Liewen Beispill

Mir hunn eng Kéier e Service geschriwwen fir automatesch e b2b Client z'registréieren. An eng GOD Method erschéngt fir 200 Zeilen ähnlechen Inhalt:

  • Gitt op 1C a erstellt e Kont
  • Mat dësem Kont, gitt op de Bezuelungsmodul a erstellt et do
  • Kontrolléiert datt e Kont mat sou engem Kont net um Haaptserver erstallt gouf
  • Schafen en neie Kont
  • Füügt d'Registrierungsresultater am Bezuelmodul an d'Nummer 1c un den Aschreiwungsresultater Service
  • Füügt Kontinformatioun op dësen Dësch
  • Schafen eng Punkt Zuel fir dëse Client am Punkt Service. Gitt Är 1c Kontosnummer un dëse Service.

An et waren ongeféier 10 méi Geschäftsoperatiounen op dëser Lëscht mat schrecklecher Konnektivitéit. Bal jiddereen brauch de Kont Objet. D'Punkt ID an de Client Numm waren an der Halschent vun den Uruff gebraucht.

No enger Stonn Refactoring konnte mir den Infrastrukturcode an e puer vun den Nuancen vun der Aarbecht mat engem Kont an separat Methoden / Klassen trennen. D'Methode Gott huet et méi einfach gemaach, awer et waren 100 Zeilen Code lénks déi just net entwéckele wollten.

Eréischt no e puer Deeg gouf et kloer datt d'Essenz vun dëser "liichtgewiicht" Method e Geschäftsalgorithmus ass. An datt déi ursprénglech Beschreiwung vun den technesche Spezifikatioune zimlech komplex war. An et ass de Versuch dës Method a Stécker ze briechen, déi de SRP verletzen, an net vice versa.

Formalismus.

Et ass Zäit eis gedronk eleng ze loossen. Drëcht Är Tréinen - mir wäerten definitiv enges Daags drop zréckkommen. Loosst eis elo d'Wëssen aus dësem Artikel formaliséieren.

Formalismus 1. Definitioun vun SRP

  1. Trennt d'Elementer sou datt jidderee vun hinnen fir eng Saach verantwortlech ass.
  2. Verantwortung steet fir "Ursaach fir ze änneren." Dat ass, all Element huet nëmmen ee Grond fir Ännerung, am Sënn vun Affär Logik.
  3. Potenziell Ännerunge fir Geschäftslogik. muss lokaliséiert ginn. Elementer, déi synchron änneren, mussen an der Géigend sinn.

Formalismus 2. Néideg Self-Test Critèren.

Ech hunn net genuch Critèrë gesinn fir de SRP z'erfëllen. Mä et sinn néideg Konditiounen:

1) Frot Iech wat dës Klass / Method / Modul / Service mécht. Dir musst et mat enger einfacher Definitioun beäntweren. ( Merci Brightori )

Erklärungen

Wéi och ëmmer, heiansdo ass et ganz schwéier eng einfach Definitioun ze fannen

2) E Bugs ze fixéieren oder eng nei Feature bäizefügen beaflosst eng Minimum Unzuel u Dateien / Klassen. Idealfall - eent.

Erklärungen

Well d'Verantwortung (fir eng Feature oder Käfer) an enger Datei/Klass akapselt ass, wësst Dir genee wou Dir sicht a wat ze änneren. Zum Beispill: d'Fonktioun fir d'Ausgab vun de Logbicher z'änneren erfuerdert nëmmen de Logger z'änneren. Et ass net néideg duerch de Rescht vum Code ze lafen.

En anert Beispill ass eng nei UI Kontroll derbäi, ähnlech wéi déi virdrun. Wann dëst Iech zwéngt 10 verschidden Entitéiten a 15 verschidde Konverter ze addéieren, gesäit et aus wéi wann Dir et iwwerdréit.

3) Wann e puer Entwéckler op verschiddene Fonctiounen vun Ärem Projet schaffen, dann ass d'Wahrscheinlechkeet vun engem Fusiounskonflikt, dat heescht, d'Wahrscheinlechkeet datt déiselwecht Datei / Klass vun e puer Entwéckler zur selwechter Zäit geännert gëtt, minimal.

Erklärungen

Wann, wann Dir eng nei Operatioun "Pour Wodka ënner dem Dësch" bäigefüügt, musst Dir de Logger beaflossen, d'Operatioun vum Drénken a Gießen, da gesäit et aus wéi d'Verantwortung kromme opgedeelt ass. Natierlech ass dat net ëmmer méiglech, mä mir sollen probéieren dës Zuel ze reduzéieren.

4) Wann Dir eng Klärungsfro iwwer d'Geschäftslogik stellt (vun engem Entwéckler oder Manager), gitt Dir strikt an eng Klass/Datei a kritt Informatioun nëmme vun do.

Erklärungen

Features, Regelen oder Algorithmen gi kompakt geschriwwen, jidderee op enger Plaz, an net mat Fändelen am ganze Coderaum verspreet.

5) Den Numm ass kloer.

Erklärungen

Eis Klass oder Method ass fir eng Saach verantwortlech, an d'Verantwortung spigelt sech a sengem Numm

AllManagersManagerService - héchstwahrscheinlech e Gott Klass
LocalPayment - wahrscheinlech net

Formalismus 3. Occam-éischt Entwécklungsmethodologie.

Am Ufank vum Design weess den Affemann net a fillt net all d'Subtletien vum Problem geléist a kann e Feeler maachen. Dir kënnt Feeler op verschidde Manéiere maachen:

  • Maacht Objeten ze grouss andeems Dir verschidde Verantwortung fusionéiert
  • Reframing andeems Dir eng eenzeg Verantwortung a vill verschidden Aarte opdeelt
  • Falsch definéieren d'Grenze vun der Verantwortung

Et ass wichteg d'Regel ze erënneren: "et ass besser e grousse Feeler ze maachen", oder "wann Dir net sécher sidd, trennt et net op." Wann, zum Beispill, Är Klass zwou Verantwortung enthält, dann ass et nach ëmmer verständlech a kann an zwee opgedeelt ginn mat minimale Ännerunge vum Client Code. D'Assemblée vun engem Glas aus Glasscheren ass normalerweis méi schwéier wéinst dem Kontext iwwer e puer Dateien verbreet an dem Mangel un néideg Ofhängegkeeten am Client Code.

Et ass Zäit et en Dag ze nennen

Den Ëmfang vum SRP ass net limitéiert op OOP a SOLID. Et gëllt fir Methoden, Funktiounen, Klassen, Moduler, Mikroservicer a Servicer. Et gëllt souwuel fir "figax-figax-and-prod" wéi och "Rakéitwëssenschaft" Entwécklung, wat d'Welt iwwerall e bësse besser mécht. Wann Dir driwwer denkt, ass dëst bal de fundamentale Prinzip vun all Ingenieur. Mechanesch Ingenieuren, Kontrollsystemer, an zwar all komplex Systemer sinn aus Komponente gebaut, an "Ënnerfragmentéierung" entzu de Designer vu Flexibilitéit, "Iwwerfragmentatioun" enthëlt d'Designer Effizienz, a falsch Grenzen entzéien hinnen Vernunft a Fridden vum Geescht.

Eenzeg Verantwortung Prinzip. Net esou einfach wéi et schéngt

SRP ass net vun der Natur erfonnt an ass net Deel vun der exakter Wëssenschaft. Et brécht aus eise biologeschen a psychologeschen Aschränkungen eraus.Et ass just e Wee fir komplex Systemer mam Affe-Mënsch Gehir ze kontrolléieren an z'entwéckelen. Hie seet eis wéi een e System ofbaut. Déi ursprénglech Formuléierung erfuerdert eng fair Quantitéit un Telepathie, awer ech hoffen dësen Artikel läscht e puer vun der Rauchschirm.

Source: will.com

Setzt e Commentaire