Vahid Məsuliyyət Prinsipi. Göründüyü qədər sadə deyil

Vahid Məsuliyyət Prinsipi. Göründüyü qədər sadə deyil Vahid məsuliyyət prinsipi, eyni zamanda tək məsuliyyət prinsipi,
aka vahid dəyişkənlik prinsipi - başa düşmək üçün son dərəcə sürüşkən bir oğlan və proqramçı müsahibəsində belə bir əsəbi sual.

Bu prinsiplə ilk ciddi tanışlığım birinci kursun əvvəlində, balacaları və yaşılları sürfələrdən tələbələr etmək üçün meşəyə aparıldığı vaxt baş verdi - əsl tələbələr.

Meşədə hərəyə 8-9 nəfərlik qruplara bölündük və yarışdıq - hansı qrup bir şüşə arağı daha tez içəcək, bir şərtlə ki, qrupdan birinci olan stəkana araq töksün, ikincisi onu içsin, üçüncüdə isə qəlyanaltı var. Fəaliyyətini tamamlayan bölmə qrup növbəsinin sonuna keçir.

Növbə ölçüsünün üçə çox olması SRP-nin yaxşı tətbiqi idi.

Tərif 1. Vahid məsuliyyət.

Vahid Məsuliyyət Prinsipinin (SRP) rəsmi tərifində göstərilir ki, hər bir qurumun öz məsuliyyəti və mövcudluq səbəbi var və onun yalnız bir məsuliyyəti var.

“Sərxoş” obyektini nəzərdən keçirək (Tippler).
SRP prinsipini həyata keçirmək üçün biz vəzifələri üç yerə böləcəyik:

  • biri tökülür (PourOperation)
  • Bir içki (DrinkUpOperation)
  • Birində qəlyanaltı var (TakeBiteOperation)

Proses iştirakçılarının hər biri prosesin bir komponentinə cavabdehdir, yəni bir atom məsuliyyəti daşıyır - içmək, tökmək və ya qəlyanaltı etmək.

İçməli çuxur, öz növbəsində, bu əməliyyatlar üçün bir fasaddır:

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

Vahid Məsuliyyət Prinsipi. Göründüyü qədər sadə deyil

Niyə?

İnsan proqramçı meymun insan üçün kod yazır, meymun adam isə diqqətsiz, axmaq və həmişə tələskəndir. Eyni anda 3-7 termini tuta və başa düşə bilər.
Sərxoşun vəziyyətində bu terminlərdən üçü var. Ancaq kodu bir vərəqlə yazsaq, onda əllər, eynəklər, döyüşlər və siyasətlə bağlı sonsuz mübahisələr olacaq. Və bütün bunlar bir metodun bədənində olacaq. Əminəm ki, təcrübənizdə belə kodu görmüsünüz. Psixika üçün ən humanist test deyil.

Digər tərəfdən, meymun adam başındakı real dünya obyektlərini simulyasiya etmək üçün yaradılmışdır. Təxəyyülündə onları bir-birinə sıxışdıra, onlardan yeni əşyalar yığa və eyni şəkildə sökə bilər. Köhnə model avtomobili təsəvvür edin. Təsəvvürünüzə görə, qapını aça, qapının bəzəyini aça və orada pəncərə qaldırma mexanizmlərini görə bilərsiniz, içərisində dişlilər olacaq. Ancaq maşının bütün komponentlərini eyni anda, bir "siyahı"da görə bilməzsiniz. Ən azından “meymun adam” edə bilməz.

Buna görə də, insan proqramçıları mürəkkəb mexanizmləri daha az mürəkkəb və işləyən elementlər toplusuna parçalayırlar. Bununla belə, o, müxtəlif yollarla parçalana bilər: bir çox köhnə avtomobillərdə hava kanalı qapıya daxil olur, müasir avtomobillərdə isə kilid elektronikasında yaranan nasazlıq mühərrikin işə salınmasına mane olur, bu da təmir zamanı problem yarada bilər.

İndi, SRP NECƏ parçalanacağını, yəni bölücü xəttin harada çəkiləcəyini izah edən bir prinsipdir..

O deyir ki, “məsuliyyətin” bölünməsi prinsipinə, yəni müəyyən obyektlərin tapşırıqlarına görə parçalanmaq lazımdır.

Vahid Məsuliyyət Prinsipi. Göründüyü qədər sadə deyil

Gəlin içkiyə və meymunun parçalanma zamanı əldə etdiyi üstünlüklərə qayıdaq:

  • Kod hər səviyyədə son dərəcə aydın oldu
  • Kod eyni anda bir neçə proqramçı tərəfindən yazıla bilər (hər biri ayrıca element yazır)
  • Avtomatlaşdırılmış sınaq sadələşdirilmişdir - element nə qədər sadədirsə, sınaqdan keçirmək bir o qədər asandır
  • Kodun tərkibi görünür - əvəz edə bilərsiniz DrinkUpOperation sərxoşun masanın altına maye tökdüyü əməliyyata. Və ya tökmə əməliyyatını şərab və su və ya araq və pivə qarışdırdığınız əməliyyatla əvəz edin. Biznes tələblərindən asılı olaraq, metod koduna toxunmadan hər şeyi edə bilərsiniz Tippler.Act.
  • Bu əməliyyatlardan siz qarınqulu qatlaya bilərsiniz (yalnız TakeBitOperation), Alkoqollu (yalnız istifadə olunur DrinkUpOperation birbaşa şüşədən) və bir çox digər iş tələblərinə cavab verir.

(Oh, deyəsən, bu artıq bir OCP prinsipidir və mən bu yazının məsuliyyətini pozmuşam)

Və, əlbəttə ki, mənfi cəhətləri:

  • Biz daha çox növ yaratmalıyıq.
  • Sərxoş ilk dəfə içdiyi vaxtdan bir neçə saat gec içir.

Tərif 2. Vahid dəyişkənlik.

İcazə verin, cənablar! İçki sinfinin də bir məsuliyyəti var - o, içir! Və ümumiyyətlə, “məsuliyyət” sözü son dərəcə qeyri-müəyyən bir anlayışdır. Kimsə bəşəriyyətin taleyinə cavabdehdir, kimsə isə dirəkdə aşmış pinqvinlərin yetişdirilməsinə cavabdehdir.

Gəlin içənin iki tətbiqini nəzərdən keçirək. Yuxarıda qeyd olunan birincisi üç sinifdən ibarətdir - tökmək, içmək və qəlyanaltı.

İkincisi “İrəli və Yalnız İrəli” metodologiyası vasitəsilə yazılır və metoddakı bütün məntiqi ehtiva edir Akt:

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

Bu siniflərin hər ikisi, kənar müşahidəçinin nöqteyi-nəzərindən, tamamilə eyni görünür və eyni "içmək" məsuliyyətini bölüşür.

Qarışıqlıq!

Sonra internetə girib SRP-nin başqa tərifini - Vahid Dəyişkənlik Prinsipini tapırıq.

SCP bildirir ki, "Modulun dəyişmək üçün bir və yalnız bir səbəbi var". Yəni, “Məsuliyyət dəyişiklik üçün səbəbdir”.

(Görünür, orijinal tərifi tapan uşaqlar meymun adamın telepatik qabiliyyətlərinə arxayın idilər)

İndi hər şey öz yerinə düşür. Ayrı-ayrılıqda, biz tökmə, içmə və qəlyanaltı prosedurlarını dəyişdirə bilərik, ancaq içənin özündə biz yalnız əməliyyatların ardıcıllığını və tərkibini dəyişə bilərik, məsələn, qəlyanaltı içmədən əvvəl hərəkət etdirərək və ya tost oxunuşunu əlavə etməklə.

“İrəli və Yalnız İrəli” yanaşmasında dəyişdirilə bilən hər şey yalnız metodda dəyişdirilir Akt. Bu, az məntiq olduqda və nadir hallarda dəyişdikdə oxunaqlı və təsirli ola bilər, lakin çox vaxt Rusiyanın NATO-ya daxil olması üçün tələb olunandan daha çox if-ifadəsi ilə hər biri 500 sətirdən ibarət dəhşətli üsullarla başa çatır.

Tərif 3. Dəyişikliklərin lokallaşdırılması.

İçki içənlər çox vaxt başqasının mənzilində niyə oyandıqlarını, mobil telefonlarının harada olduğunu başa düşmürlər. Ətraflı giriş əlavə etməyin vaxtı gəldi.

Gəlin tökmə prosesi ilə girişə başlayaq:

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

Onu əhatə etməklə PourOperation, biz məsuliyyət və inkapsulyasiya nöqteyi-nəzərindən ağıllı hərəkət etdik, amma indi dəyişkənlik prinsipi ilə qarışdıq. Dəyişdirilə bilən əməliyyatın özündən əlavə, girişin özü də dəyişkən olur. Tökmə əməliyyatı üçün ayırıb xüsusi logger yaratmalı olacaqsınız:

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

Diqqətli oxucu bunu görəcək LogAfter, LogBefore и OnError fərdi olaraq dəyişdirilə bilər və əvvəlki addımlara bənzətməklə, üç sinif yaradacaqdır: PourLoggerBefore, PourLoggerAfter и PourErrorLogger.

Və bir içki içən üçün üç əməliyyat olduğunu xatırlasaq, biz doqquz ağac kəsmə dərsi alırıq. Nəticədə, bütün içmə dairəsi 14 (!!!) sinifdən ibarətdir.

Hiperbola? Çətinliklə! Parçalanma qumbarası olan meymun adam "tökən"i dekanterə, stəkana, tökmə operatorlarına, su təchizatı xidmətinə, molekulların toqquşmasının fiziki modelinə böləcək və növbəti rübdə asılılıqları heç bir problem olmadan həll etməyə çalışacaq. qlobal dəyişənlər. Və mənə inanın, o dayanmayacaq.

Məhz bu məqamda çoxları SRP-nin çəhrayı krallıqların nağılları olduğu qənaətinə gəlir və əriştə oynamağa gedirlər...

... Srp-nin üçüncü tərifinin varlığını heç vaxt öyrənmədən:

“Vahid Məsuliyyət Prinsipində deyilir ki dəyişməyə bənzər şeylər bir yerdə saxlanmalıdır". və ya "Nə dəyişikliklər birlikdə bir yerdə saxlanılmalıdır"

Yəni bir əməliyyatın qeydini dəyişdirsək, onu bir yerdə dəyişməliyik.

Bu, çox vacib bir məqamdır - çünki yuxarıda göstərilən SRP-nin bütün izahatları, növləri əzərkən əzmək lazım olduğunu söylədi, yəni obyektin ölçüsünə "yuxarı həddi" qoydular və indi biz artıq “aşağı hədd”dən danışırıq. Başqa sözlə, SRP yalnız "əzərkən əzmək" tələb etmir, həm də onu aşmamaq üçün - "bir-birinə bağlı olan şeyləri əzməyin". Bu, Okcamın ülgücü ilə meymun adam arasındakı böyük döyüşdür!

Vahid Məsuliyyət Prinsipi. Göründüyü qədər sadə deyil

İndi içki içən özünü daha yaxşı hiss etməlidir. IPourLogger qeydini üç sinfə bölməyə ehtiyac olmadığından əlavə, biz bütün loggerləri bir növdə birləşdirə bilərik:

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

Dördüncü növ əməliyyat əlavə etsək, onun üçün giriş artıq hazırdır. Və əməliyyatların kodu təmizdir və infrastruktur səs-küyündən azaddır.

Nəticədə, içmə problemini həll etmək üçün 5 sinifimiz var:

  • Tökmə əməliyyatı
  • İçki əməliyyatı
  • Sıxılma əməliyyatı
  • Logger
  • İçki fasadı

Onların hər biri ciddi şəkildə bir funksionallıq üçün məsuliyyət daşıyır və dəyişiklik üçün bir səbəbə malikdir. Dəyişikliyə bənzər bütün qaydalar yaxınlıqda yerləşir.

Real həyat nümunəsi

Biz bir dəfə b2b müştərisini avtomatik qeydiyyatdan keçirmək üçün xidmət yazmışdıq. Bənzər məzmunlu 200 sətir üçün GOD metodu ortaya çıxdı:

  • 1C-ə gedin və hesab yaradın
  • Bu hesabla ödəniş moduluna keçin və orada yaradın
  • Əsas serverdə belə hesaba malik hesabın yaradılmadığını yoxlayın
  • Yeni hesab yarat
  • Ödəniş modulunda qeydiyyat nəticələrini və 1c nömrəsini qeydiyyat nəticələri xidmətinə əlavə edin
  • Bu cədvələ hesab məlumatı əlavə edin
  • Nöqtə xidmətində bu müştəri üçün nöqtə nömrəsi yaradın. 1c hesab nömrənizi bu xidmətə ötürün.

Və bu siyahıda dəhşətli əlaqə ilə daha 10-a yaxın biznes əməliyyatı var idi. Hesab obyektinə demək olar ki, hər kəs lazım idi. Zənglərin yarısında nöqtə identifikatoru və müştəri adı tələb olunurdu.

Bir saatlıq refaktorinqdən sonra biz infrastruktur kodunu və hesabla işləməyin bəzi nüanslarını ayrıca metodlara/siniflərə ayıra bildik. Tanrı metodu bunu asanlaşdırdı, lakin 100 sətir kod qaldı ki, onları açmaq istəmədi.

Yalnız bir neçə gündən sonra məlum oldu ki, bu “yüngül” metodun mahiyyəti biznes alqoritmidir. Və texniki xüsusiyyətlərin orijinal təsviri olduqca mürəkkəb idi. Məhz bu üsulu parçalamaq cəhdi SRP-ni pozacaq, əksinə deyil.

Formalizm.

Sərxoşumuzu tək qoymağın vaxtı gəldi. Göz yaşlarınızı qurutun - bir gün mütləq ona qayıdacayıq. İndi bu məqalədəki bilikləri rəsmiləşdirək.

Formalizm 1. SRP-nin tərifi

  1. Elementləri ayırın ki, onların hər biri bir şeyə cavabdeh olsun.
  2. Məsuliyyət “dəyişiklik səbəbi” deməkdir. Yəni, hər bir elementin biznes məntiqi baxımından dəyişmək üçün yalnız bir səbəbi var.
  3. Biznes məntiqində potensial dəyişikliklər. lokallaşdırılmalıdır. Sinxron olaraq dəyişən elementlər yaxınlıqda olmalıdır.

Formalizm 2. Zəruri özünü sınamaq meyarları.

SRP-ni yerinə yetirmək üçün kifayət qədər meyar görməmişəm. Ancaq zəruri şərtlər var:

1) Özünüzdən soruşun ki, bu sinif/metod/modul/xidmət nə edir. sadə təriflə cavab verməlisiniz. ( Çox sağ ol Brightori )

izahatlar

Ancaq bəzən sadə bir tərif tapmaq çox çətindir

2) Bir səhvin düzəldilməsi və ya yeni funksiyanın əlavə edilməsi faylların/siniflərin minimum sayına təsir edir. İdeal - bir.

izahatlar

Məsuliyyət (xüsusiyyət və ya səhv üçün) bir fayl/sinifdə əhatə olunduğundan, siz hara baxacağınızı və nəyi redaktə edəcəyinizi dəqiq bilirsiniz. Məsələn: logging əməliyyatlarının çıxışını dəyişdirmək xüsusiyyəti yalnız loggerin dəyişdirilməsini tələb edəcəkdir. Kodun qalan hissəsindən keçməyə ehtiyac yoxdur.

Başqa bir nümunə, əvvəlkilərə bənzər yeni UI nəzarətinin əlavə edilməsidir. Əgər bu, sizi 10 fərqli obyekt və 15 fərqli çevirici əlavə etməyə məcbur edirsə, deyəsən, həddi aşırsınız.

3) Əgər bir neçə tərtibatçı layihənizin müxtəlif funksiyaları üzərində işləyirsə, onda birləşmə konfliktinin yaranma ehtimalı, yəni eyni faylın/sinfin eyni vaxtda bir neçə tərtibatçı tərəfindən dəyişdirilməsi ehtimalı minimaldır.

izahatlar

Əgər "Stol altına araq tök" əməliyyatını əlavə edərkən, loggerə, içmə və tökmə əməliyyatına təsir etmək lazımdırsa, o zaman vəzifələr əyri şəkildə bölünmüş kimi görünür. Təbii ki, bu, həmişə mümkün deyil, lakin biz bu rəqəmi azaltmağa çalışmalıyıq.

4) Biznes məntiqi ilə bağlı aydınlaşdırıcı sual verildikdə (bir tərtibatçıdan və ya menecerdən) siz ciddi şəkildə bir sinif/fayl daxil olursunuz və yalnız oradan məlumat alırsınız.

izahatlar

Xüsusiyyətlər, qaydalar və ya alqoritmlər hər biri bir yerdə yığcam şəkildə yazılır və bütün kod məkanında bayraqlarla səpələnməyib.

5) Adlandırma aydındır.

izahatlar

Sinifimiz və ya metodumuz bir şeyə görə məsuliyyət daşıyır və məsuliyyət onun adında əks olunur

AllManagersManagerService - çox güman ki, Tanrı sinfi
LocalPayment - yəqin ki, yox

Formalizm 3. Occam-birinci inkişaf metodologiyası.

Dizaynın başlanğıcında meymun adam həll olunan problemin bütün incəliklərini bilmir və hiss etmir və səhv edə bilər. Müxtəlif yollarla səhv edə bilərsiniz:

  • Müxtəlif məsuliyyətləri birləşdirərək obyektləri çox böyük edin
  • Tək bir məsuliyyəti bir çox müxtəlif növlərə bölmək yolu ilə yenidən çərçivələmə
  • Məsuliyyətin sərhədlərini səhv müəyyən etmək

Qaydanı xatırlamaq vacibdir: “böyük səhv etmək daha yaxşıdır” və ya “əmin deyilsinizsə, onu bölməyin”. Məsələn, sinifinizdə iki məsuliyyət varsa, o, hələ də başa düşüləndir və müştəri kodunda minimal dəyişikliklərlə ikiyə bölünə bilər. Şüşə qırıqlarından stəkanı yığmaq adətən kontekstin bir neçə fayla yayılması və müştəri kodunda lazımi asılılıqların olmaması səbəbindən daha çətindir.

Bunu bir gün adlandırmağın vaxtı gəldi

SRP-nin əhatə dairəsi OOP və SOLID ilə məhdudlaşmır. Bu üsullara, funksiyalara, siniflərə, modullara, mikroservislərə və xidmətlərə aiddir. Bu, həm “figax-figax-and-prod” və həm də “raket elmi” inkişafına aiddir və dünyanı hər yerdə bir az daha yaxşı edir. Fikir versəniz, bu, demək olar ki, bütün mühəndisliyin əsas prinsipidir. Maşınqayırma, idarəetmə sistemləri və həqiqətən də bütün mürəkkəb sistemlər komponentlərdən qurulur və “aşağı fraqmentasiya” konstruktorları çeviklikdən, “həddindən artıq parçalanma” dizaynerləri səmərəlilikdən, yanlış sərhədlər isə onları ağıldan və dinclikdən məhrum edir.

Vahid Məsuliyyət Prinsipi. Göründüyü qədər sadə deyil

SRP təbiət tərəfindən icad edilməmişdir və dəqiq elmin bir hissəsi deyil. Bu, bizim bioloji və psixoloji məhdudiyyətlərimizdən çıxır.Bu, sadəcə meymun insan beynindən istifadə edərək mürəkkəb sistemləri idarə etmək və inkişaf etdirmək üçün bir üsuldur. O, bizə bir sistemin necə parçalanacağını söyləyir. Orijinal tərtibat kifayət qədər telepatiya tələb edirdi, amma ümid edirəm ki, bu məqalə tüstüdən bir az təmizlənəcək.

Mənbə: www.habr.com

Добавить комментарий