Yagona javobgarlik printsipi. Ko'rinadigan darajada oddiy emas

Yagona javobgarlik printsipi. Ko'rinadigan darajada oddiy emas Yagona javobgarlik printsipi, shuningdek, yagona javobgarlik printsipi,
aka yagona o'zgaruvchanlik printsipi - tushunish uchun juda silliq yigit va dasturchi intervyusida bunday asabiy savol.

Bu tamoyil bilan birinchi jiddiy tanishuvim birinchi kursimning boshida, yosh va yashil o'rmonga lichinkalardan haqiqiy talabalarni tayyorlash uchun olib borilganda sodir bo'ldi.

O'rmonda biz har birimiz 8-9 kishidan iborat guruhlarga bo'lindik va musobaqa o'tkazdik - qaysi guruh bir shisha aroqni tezroq ichishadi, agar guruhdan birinchi odam stakanga aroq quysa, ikkinchisi ichsa, uchinchisida esa gazak bor. Ishini tugatgan birlik guruh navbatining oxiriga o'tadi.

Navbat o'lchami uchga ko'p bo'lgan holat SRPning yaxshi amalga oshirilishi edi.

Ta'rif 1. Yagona javobgarlik.

Yagona javobgarlik printsipining (SRP) rasmiy ta'rifida har bir sub'ektning o'z mas'uliyati va mavjudligi uchun sabab borligi va u faqat bitta mas'uliyatga ega ekanligini ta'kidlaydi.

"Ichuvchi" ob'ektini ko'rib chiqing (Tippler).
SRP tamoyilini amalga oshirish uchun biz mas'uliyatni uchtaga ajratamiz:

  • Biri quyadi (PourOperation)
  • Bir ichimlik (DrinkUpOperation)
  • Birida gazak bor (TakeBiteOperation)

Jarayon ishtirokchilarining har biri jarayonning bir komponenti uchun javobgardir, ya'ni bitta atom mas'uliyatiga ega - ichish, quyish yoki gazak qilish.

Ichimlik teshigi, o'z navbatida, ushbu operatsiyalar uchun jabhadir:

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

Yagona javobgarlik printsipi. Ko'rinadigan darajada oddiy emas

Nima uchun?

Inson dasturchisi maymun odamga kod yozadi, maymun odam esa e'tiborsiz, ahmoq va doimo shoshqaloq. U bir vaqtning o'zida taxminan 3-7 atamani ushlab turishi va tushunishi mumkin.
Ichkilikbozlik holatida bu atamalarning uchtasi bor. Biroq, agar kodni bitta varaq bilan yozadigan bo'lsak, unda qo'llar, ko'zoynaklar, janjallar va siyosat haqidagi cheksiz bahslar bo'ladi. Va bularning barchasi bitta usulning tanasida bo'ladi. Ishonchim komilki, siz amaliyotingizda bunday kodni ko'rgansiz. Psixika uchun eng insoniy sinov emas.

Boshqa tomondan, maymun odam uning boshida haqiqiy dunyo ob'ektlarini taqlid qilish uchun yaratilgan. Tasavvurida u ularni bir-biriga itarib yuborishi, ulardan yangi narsalarni yig'ishi va ularni bir xil tarzda qismlarga ajratishi mumkin. Eski rusumdagi mashinani tasavvur qiling. O'zingizning tasavvuringizda siz eshikni ochishingiz, eshik trimini ochishingiz va u erda oynani ko'tarish mexanizmlarini ko'rishingiz mumkin, ularning ichida viteslar bo'ladi. Lekin mashinaning barcha qismlarini bir vaqtning o'zida, bitta "ro'yxat"da ko'ra olmaysiz. Hech bo'lmaganda "maymun odam" qila olmaydi.

Shuning uchun inson dasturchilari murakkab mexanizmlarni kamroq murakkab va ishlaydigan elementlar to'plamiga ajratadilar. Biroq, u turli yo'llar bilan parchalanishi mumkin: ko'plab eski avtomobillarda havo kanali eshikka kiradi va zamonaviy avtomashinalarda qulflash elektronikasidagi nosozlik dvigatelni ishga tushirishga to'sqinlik qiladi, bu ta'mirlash vaqtida muammo hisoblanadi.

Endi, SRP - bu QANDAY parchalanishni, ya'ni bo'linish chizig'ini qaerga chizish kerakligini tushuntiruvchi printsip..

Uning so'zlariga ko'ra, "mas'uliyat" ni taqsimlash printsipiga ko'ra, ya'ni muayyan ob'ektlarning vazifalariga ko'ra parchalanish kerak.

Yagona javobgarlik printsipi. Ko'rinadigan darajada oddiy emas

Keling, ichishga va maymun odamning parchalanish paytida oladigan afzalliklariga qaytaylik:

  • Kod har bir darajada juda aniq bo'ldi
  • Kod bir vaqtning o'zida bir nechta dasturchilar tomonidan yozilishi mumkin (har biri alohida element yozadi)
  • Avtomatlashtirilgan test soddalashtirilgan - element qanchalik sodda bo'lsa, uni tekshirish osonroq bo'ladi
  • Kodning kompozitsiyasi paydo bo'ladi - siz almashtirishingiz mumkin DrinkUpOperation ichkilikboz stol ostiga suyuqlik quyib yuboradigan operatsiyaga. Yoki quyish jarayonini sharob va suv yoki aroq va pivo aralashtiradigan operatsiya bilan almashtiring. Biznes talablariga qarab, siz usul kodiga tegmasdan hamma narsani qilishingiz mumkin Tippler.Act.
  • Ushbu operatsiyalardan siz ochko'zlikni katlay olasiz (faqat TakeBitOperation), Alkogolli (faqat foydalanish uchun DrinkUpOperation to'g'ridan-to'g'ri shishadan) va boshqa ko'plab biznes talablariga javob beradi.

(Oh, bu allaqachon OCP printsipi bo'lib tuyuladi va men ushbu postning javobgarligini buzganman)

Va, albatta, kamchiliklari:

  • Biz ko'proq turlarni yaratishimiz kerak.
  • Mast odam birinchi marta ichganidan bir necha soat keyin ichadi.

Ta'rif 2. Birlashtirilgan o'zgaruvchanlik.

Menga ruxsat bering, janoblar! Ichimlik sinfining ham bitta mas'uliyati bor - u ichadi! Va umuman olganda, "mas'uliyat" so'zi juda noaniq tushunchadir. Kimdir insoniyat taqdiri uchun, kimdir qutbda ag'darilgan pingvinlarni ko'tarish uchun javobgar.

Keling, ichuvchining ikkita tatbiqini ko'rib chiqaylik. Birinchisi, yuqorida aytib o'tilgan, uchta sinfni o'z ichiga oladi - quyish, ichish va gazak.

Ikkinchisi "Oldinga va faqat oldinga" metodologiyasi orqali yozilgan va usuldagi barcha mantiqni o'z ichiga oladi Act:

//Не тратьте время  на изучение этого класса. Лучше съешьте печеньку
с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 ikkala sinf, tashqi kuzatuvchi nuqtai nazaridan, bir xil ko'rinishga ega va "ichish" mas'uliyatini bir xil bo'ladi.

Chalkashlik!

Keyin biz Internetga kiramiz va SRP ning yana bir ta'rifini topamiz - Yagona o'zgaruvchanlik printsipi.

SCP ta'kidlaydi "Modulni o'zgartirish uchun faqat bitta sabab bor". Ya'ni, "Mas'uliyat - o'zgarish uchun sababdir".

(Aftidan, asl ta'rifni o'ylab topgan yigitlar maymun odamning telepatik qobiliyatiga ishonishgan)

Endi hammasi joyiga tushadi. Alohida-alohida, biz quyish, ichish va gazak qilish tartiblarini o'zgartirishimiz mumkin, ammo ichuvchining o'zida biz faqat operatsiyalar ketma-ketligi va tarkibini o'zgartirishimiz mumkin, masalan, ichishdan oldin gazakni siljitish yoki tushdi o'qishini qo'shish orqali.

"Oldinga va faqat oldinga" yondashuvida o'zgartirilishi mumkin bo'lgan hamma narsa faqat usulda o'zgartiriladi Act. Agar mantiq kam bo'lsa va u kamdan-kam o'zgaradi, bu o'qilishi mumkin va samarali bo'lishi mumkin, lekin ko'pincha u Rossiyaning NATOga qo'shilishi uchun talab qilinganidan ko'ra ko'proq 500 satrdan iborat dahshatli usullar bilan yakunlanadi.

Ta'rif 3. O'zgarishlarning lokalizatsiyasi.

Ichuvchilar ko'pincha nima uchun birovning kvartirasida uyg'onganini yoki mobil telefoni qaerdaligini tushunmaydi. Batafsil jurnalni qo'shish vaqti keldi.

Keling, quyish jarayoni bilan jurnalga kirishni boshlaylik:

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

Uni inkapsulatsiya qilish orqali PourOperation, biz mas'uliyat va inkapsulyatsiya nuqtai nazaridan oqilona harakat qildik, ammo hozir biz o'zgaruvchanlik printsipi bilan adashdik. O'zgarishi mumkin bo'lgan operatsiyadan tashqari, jurnalning o'zi ham o'zgaruvchan bo'ladi. Siz quyish uchun maxsus loggerni ajratib olishingiz va yaratishingiz kerak bo'ladi:

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

Ehtiyotkor o'quvchi buni sezadi LogAfter, LogBefore и OnXato shuningdek, individual ravishda o'zgartirilishi mumkin va oldingi bosqichlarga o'xshab, uchta sinf yaratiladi: PourLoggerBefore, PourLoggerAfter и PourErrorLogger.

Va ichuvchi uchun uchta operatsiya borligini eslab, biz to'qqizta daraxt kesish darsini olamiz. Natijada, butun ichimlik doirasi 14 (!!!) sinfdan iborat.

Giperbola? Zo'rg'a! Parchalanish granatasi bo'lgan maymun odam "quyma" ni dekanterga, stakanga, quyish operatorlariga, suv ta'minoti xizmatiga, molekulalar to'qnashuvining fizik modeliga bo'linadi va keyingi chorakda u bog'liqliklarni hech qanday muammosiz hal qilishga harakat qiladi. global o'zgaruvchilar. Va menga ishoning, u to'xtamaydi.

Aynan shu nuqtada ko'pchilik SRP pushti qirolliklarning ertaklari degan xulosaga keladi va noodle o'ynash uchun ketishadi ...

... hech qachon Srp ning uchinchi ta'rifi mavjudligini bilmasdan:

“Yagona javobgarlik tamoyilida shunday deyilgan o'zgarishlarga o'xshash narsalar bir joyda saqlanishi kerak". yoki "Qanday o'zgarishlar birgalikda bir joyda saqlanishi kerak"

Ya'ni, agar biz operatsiya jurnalini o'zgartirsak, uni bir joyda o'zgartirishimiz kerak.

Bu juda muhim nuqta - chunki yuqorida keltirilgan SRP ning barcha tushuntirishlarida turlarni maydalash paytida maydalash kerakligi aytilgan, ya'ni ular ob'ektning o'lchamiga "yuqoridan cheklov" qo'ygan va Endi biz "pastdan cheklash" haqida gapiramiz. Boshqa so'zlar bilan aytganda, SRP nafaqat "ezish paytida maydalashni", balki uni haddan tashqari oshirmaslikni ham talab qiladi - "bir-biriga bog'langan narsalarni ezib tashlamang". Bu Okkamning ustarasi va maymun odam o'rtasidagi buyuk jang!

Yagona javobgarlik printsipi. Ko'rinadigan darajada oddiy emas

Endi ichuvchi o'zini yaxshi his qilishi kerak. IPourLogger loggerini uchta sinfga bo'lishning hojati yo'qligidan tashqari, biz barcha loggerlarni bitta turga birlashtira olamiz:

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

Va agar biz to'rtinchi turdagi operatsiyani qo'shsak, u holda jurnallar allaqachon tayyor. Operatsiyalarning o'zi ham toza va infratuzilma shovqinidan xoli.

Natijada, ichimlik muammosini hal qilish uchun bizda 5 ta sinf mavjud:

  • To'kish operatsiyasi
  • Ichimlik operatsiyasi
  • Siqilish operatsiyasi
  • Logger
  • Ichimlik fasad

Ularning har biri bitta funksionallik uchun qat'iy javobgardir va o'zgartirish uchun bitta sababga ega. O'zgartirishga o'xshash barcha qoidalar yaqin joyda joylashgan.

Haqiqiy hayot misoli

Biz bir marta b2b mijozini avtomatik ro'yxatdan o'tkazish uchun xizmat yozgan edik. Va shunga o'xshash tarkibning 200 qatori uchun GOD usuli paydo bo'ldi:

  • 1C ga o'ting va hisob yarating
  • Ushbu hisob bilan to'lov moduliga o'ting va u erda yarating
  • Asosiy serverda bunday hisob qaydnomasi yaratilmaganligini tekshiring
  • Yangi hisob yarating
  • To'lov modulidagi ro'yxatga olish natijalarini va ro'yxatga olish natijalari xizmatiga 1c raqamini qo'shing
  • Ushbu jadvalga hisob ma'lumotlarini qo'shing
  • Nuqta xizmatida ushbu mijoz uchun nuqta raqamini yarating. 1c hisob raqamingizni ushbu xizmatga o'tkazing.

Va bu ro'yxatda dahshatli ulanishga ega yana 10 ga yaqin biznes operatsiyalari mavjud edi. Hisob ob'ekti deyarli hammaga kerak edi. Nuqta identifikatori va mijoz nomi qo'ng'iroqlarning yarmida kerak edi.

Bir soatlik refaktoringdan so'ng biz infratuzilma kodini va hisob bilan ishlashning ba'zi nuanslarini alohida usullar/sinflarga ajratishga muvaffaq bo'ldik. Xudo usuli buni osonlashtirdi, ammo 100 qator kod qoldi, ular shunchaki hal qilishni xohlamadi.

Faqat bir necha kundan keyin bu "engil" usulning mohiyati biznes algoritmi ekanligi ayon bo'ldi. Va texnik xususiyatlarning asl tavsifi juda murakkab edi. Va bu usulni SRPni buzadigan qismlarga ajratishga urinish, aksincha emas.

Formalizm.

Bizning mastimizni yolg'iz qoldirish vaqti keldi. Ko'z yoshlaringizni quriting - biz albatta bir kun kelib unga qaytamiz. Keling, ushbu maqoladagi bilimlarni rasmiylashtiramiz.

Formalizm 1. SRP ta'rifi

  1. Elementlarni har biri bitta narsa uchun javobgar bo'lishi uchun ajrating.
  2. Mas'uliyat "o'zgartirish uchun sabab" degan ma'noni anglatadi. Ya'ni, har bir element biznes mantig'i nuqtai nazaridan o'zgarish uchun faqat bitta sababga ega.
  3. Biznes mantiqidagi potentsial o'zgarishlar. mahalliylashtirilishi kerak. Sinxron ravishda o'zgarib turadigan elementlar yaqin atrofda bo'lishi kerak.

Formalizm 2. O'z-o'zini tekshirishning zaruriy mezonlari.

Men SRPni bajarish uchun etarli mezonlarni ko'rmadim. Ammo zarur shartlar mavjud:

1) O'zingizdan bu sinf/usul/modul/xizmat nima qilishini so'rang. oddiy ta'rif bilan javob berishingiz kerak. ( Rahmat Brightori )

tushuntirishlar

Biroq, ba'zida oddiy ta'rifni topish juda qiyin

2) Xatoni tuzatish yoki yangi xususiyatni qo'shish fayllar/sinflarning minimal soniga ta'sir qiladi. Ideal - bitta.

tushuntirishlar

Mas'uliyat (xususiyat yoki xato uchun) bitta fayl/sinfga kiritilganligi sababli, siz qaerga qarash va nimani tahrirlashni aniq bilasiz. Masalan: ro'yxatga olish operatsiyalarining chiqishini o'zgartirish xususiyati faqat loggerni o'zgartirishni talab qiladi. Kodning qolgan qismini ko'rib chiqishning hojati yo'q.

Yana bir misol - avvalgilariga o'xshash yangi UI boshqaruvini qo'shish. Agar bu sizni 10 xil ob'ekt va 15 xil konvertorni qo'shishga majbur qilsa, siz haddan tashqari oshirib yuborganga o'xshaysiz.

3) Agar bir nechta ishlab chiquvchilar loyihangizning turli funksiyalari ustida ishlayotgan bo‘lsa, u holda birlashma mojarosi ehtimoli, ya’ni bir xil fayl/sinf bir vaqtning o‘zida bir nechta ishlab chiquvchilar tomonidan o‘zgartirilishi ehtimoli minimaldir.

tushuntirishlar

Agar "Stol ostiga aroq quying" yangi operatsiyasini qo'shganda, siz loggerga, ichish va quyish jarayoniga ta'sir qilishingiz kerak bo'lsa, unda mas'uliyat egri bo'linganga o'xshaydi. Albatta, bu har doim ham mumkin emas, lekin biz bu ko'rsatkichni kamaytirishga harakat qilishimiz kerak.

4) Biznes mantig'i haqida aniq savol berilganda (ishlab chiquvchi yoki menejerdan), siz qat'iy ravishda bitta sinfga/faylga kirasiz va faqat u yerdan ma'lumot olasiz.

tushuntirishlar

Xususiyatlar, qoidalar yoki algoritmlar ixcham tarzda, har biri bitta joyda yozilgan va butun kod maydonida bayroqlar bilan tarqalmagan.

5) Nom aniq.

tushuntirishlar

Bizning sinfimiz yoki metodimiz bir narsa uchun javobgardir va mas'uliyat uning nomida aks etadi

AllManagersManagerService - ehtimol Xudo sinfi
LocalPayment - ehtimol yo'q

Formalizm 3. Okkam-birinchi rivojlanish metodologiyasi.

Dizaynning boshida maymun odam hal qilinayotgan muammoning barcha nozik tomonlarini bilmaydi va his qilmaydi va xato qilishi mumkin. Siz turli yo'llar bilan xato qilishingiz mumkin:

  • Turli mas'uliyatlarni birlashtirib, ob'ektlarni juda katta qiling
  • Yagona mas'uliyatni turli xil turlarga bo'lish orqali qayta tuzish
  • Mas'uliyat chegaralarini noto'g'ri belgilash

Qoidani eslab qolish muhim: "katta xatoga yo'l qo'yganingiz ma'qul" yoki "agar ishonchingiz komil bo'lmasa, uni ajratmang". Agar, masalan, sizning sinfingiz ikkita mas'uliyatni o'z ichiga olsa, u hali ham tushunarli va mijoz kodiga minimal o'zgarishlar bilan ikkiga bo'linishi mumkin. Shisha parchalaridan stakan yig'ish odatda kontekstning bir nechta fayllarga tarqalishi va mijoz kodida kerakli bog'liqliklar yo'qligi sababli qiyinroq.

Buni bir kunga chaqirish vaqti keldi

SRP doirasi OOP va SOLID bilan cheklanmaydi. U usullar, funksiyalar, sinflar, modullar, mikroservislar va xizmatlarga tegishli. Bu "figax-figax-and-prod" va "raketa-fan" rivojlanishiga ham tegishli bo'lib, dunyoni hamma joyda biroz yaxshiroq qiladi. Agar siz o'ylab ko'rsangiz, bu barcha muhandislikning deyarli asosiy printsipi. Mashinasozlik, boshqaruv tizimlari va haqiqatan ham barcha murakkab tizimlar tarkibiy qismlardan qurilgan va "pastki parchalanish" dizaynerlarni moslashuvchanlikdan mahrum qiladi, "haddan tashqari parchalanish" dizaynerlarni samaradorlikdan mahrum qiladi va noto'g'ri chegaralar ularni aql va xotirjamlikdan mahrum qiladi.

Yagona javobgarlik printsipi. Ko'rinadigan darajada oddiy emas

SRP tabiatan ixtiro qilinmagan va aniq fanning bir qismi emas. Bu bizning biologik va psixologik cheklovlarimizdan chiqib ketadi.Bu shunchaki maymun odam miyasidan foydalangan holda murakkab tizimlarni boshqarish va rivojlantirishning bir usuli. U bizga tizimni qanday parchalash kerakligini aytadi. Asl tuzilma juda ko'p telepatiyani talab qildi, ammo umid qilamanki, ushbu maqola tutun ekranining bir qismini tozalaydi.

Manba: www.habr.com

a Izoh qo'shish