Dependency inversion printsipi yordamida amalga oshiriladi. Bog'liqlik inversiyasi printsipi. Bog'liqlik inversiyasi bilan qatlamli arxitektura


Ogohlantirish: Ushbu maqola muallifi "amaki" Bob Martin kabi hurmatli o'rtoqni hokimiyatga putur etkazish yoki biron bir tarzda xafa qilish maqsadiga ega emas. Bu ko'proq qaramlikni inversiyalash tamoyili haqida ko'proq diqqat bilan o'ylash va uni tasvirlash uchun ishlatiladigan misollarni tahlil qilishdir.

Maqola davomida men yuqorida aytib o'tilgan manbalardan barcha kerakli iqtiboslar va misollarni keltiraman. Ammo "buzg'unchilar" dan qochish va sizning fikringiz ob'ektiv bo'lib qolishi uchun men 10-15 daqiqa vaqt sarflashni va maqola yoki kitobda ushbu tamoyilning asl tavsifini o'qishni tavsiya qilaman.

Bog'liqlik inversiyasi printsipi quyidagicha:

A. Yuqori darajadagi modullar quyi darajadagi modullarga bog'liq bo'lmasligi kerak. Ikkalasi ham abstraktsiyalarga bog'liq bo'lishi kerak.
B. Abstraktsiyalar tafsilotlarga bog'liq bo'lmasligi kerak. Tafsilotlar abstraktsiyalarga bog'liq bo'lishi kerak.
Birinchi nuqtadan boshlaylik.

Qatlamlash

Piyoz qatlamlarga ega, keklarda qatlamlar, kanniballarda qatlamlar va dasturiy ta'minot tizimlarida ham qatlamlar bor! - Shrek (c)
Har qanday murakkab tizim ierarxikdir: har bir qatlam quyi darajadagi tasdiqlangan va yaxshi ishlaydigan qatlam asosida qurilgan. Bu sizga asosiy qatlamlar qanday amalga oshirilishi haqida o'ylamasdan, bir vaqtning o'zida cheklangan tushunchalar to'plamiga e'tibor qaratish imkonini beradi.
Natijada biz quyidagi diagrammaga o'xshash narsani olamiz:

1-rasm - "Naive" qatlam sxemasi

Bob Martin nuqtai nazaridan, tizimni qatlamlarga bo'lishning bunday sxemasi sodda. Ushbu dizaynning nochorligi "hiyla xususiyati: qatlam Siyosat yo'lda barcha qatlamlardagi o'zgarishlarga bog'liq Qulaylik. Bu qaramlik tranzitivdir.» .

Hmm... Juda noodatiy gap. Agar biz .NET platformasi haqida gapiradigan bo'lsak, u holda qaramlik faqat joriy modul o'zining umumiy interfeysida quyi darajadagi modullarni "fosh etsa" tranzitiv bo'ladi. Boshqacha qilib aytganda, agar ichida MexanizmQatlam argument sifatida misolni oladigan ommaviy sinf mavjud StringUtil(dan QulaylikQatlam), keyin darajadagi barcha mijozlar MexanizmQatlam odatlanib qolish QulaylikQatlam. Aks holda, o'zgarishlarning o'tish qobiliyati yo'q: barcha quyi darajadagi o'zgarishlar joriy daraja bilan cheklangan va yuqorida tarqalmaydi.

Bob Martinning fikrini tushunish uchun qaramlikni inversiyalash printsipi birinchi marta 1996 yilda tasvirlanganligini va misol sifatida C++ tilidan foydalanilganini esga olishingiz kerak. Asl maqolada muallifning o'zi shunday yozadi tranzitivlik muammosi faqat sinf interfeysi amalga oshirishdan aniq ajratilmagan tillarda mavjud. C++ da tranzitiv bog'liqliklar muammosi haqiqatan ham dolzarbdir: agar fayl PolicyLayer. h"include" direktivasi orqali o'z ichiga oladi Mexanizm qatlami. h, bu o'z navbatida o'z ichiga oladi UtilityLayer. h, keyin sarlavha faylidagi har qanday o'zgarish uchun UtilityLayer. h(hatto ushbu faylda e'lon qilingan sinflarning "yopiq" bo'limida ham) biz barcha mijozlarni qayta kompilyatsiya qilishimiz va qayta joylashtirishimiz kerak. Biroq, C++ da bu muammo Herb Sutter tomonidan taklif qilingan va hozir unchalik dolzarb bo'lmagan PIml idiomasi yordamida hal qilinadi.

Bob Martin nuqtai nazaridan bu muammoning yechimi quyidagicha:

"Yuqori darajadagi qatlam o'ziga kerak bo'lgan xizmatlar uchun mavhum interfeysni e'lon qiladi. Keyin pastki qatlamlar ushbu interfeyslarni qondirish uchun amalga oshiriladi. Yuqori darajada joylashgan har qanday sinf mavhum interfeys orqali uning yonidagi qatlam qatlamiga kiradi. Shunday qilib, yuqori qatlamlar pastki qatlamlardan mustaqildir. Aksincha, pastki qatlamlar mavhum xizmatlar interfeysiga bog'liq, e'lon qildi yuqori darajada ... Shunday qilib, bog'liqliklarni bekor qilish orqali biz bir vaqtning o'zida yanada moslashuvchan, bardoshli va mobil tuzilmani yaratdik.



2-rasm - teskari qatlamlar

Qaysidir ma'noda, bu bo'linish mantiqiy. Shunday qilib, masalan, kuzatuvchi naqshidan foydalanganda, tashqi dunyo bilan o'zaro ta'sir qilish interfeysini aniqlaydigan kuzatilgan ob'ekt (kuzatiladi), shuning uchun unga hech qanday tashqi o'zgarishlar ta'sir qila olmaydi.

Ammo boshqa tomondan, odatda yig'ilishlar (yoki UML terminlarida paketlar) sifatida taqdim etiladigan qatlamlar haqida gap ketganda, taklif qilingan yondashuvni hayotiy deb atash qiyin. Ta'rifga ko'ra, quyi darajadagi yordamchi sinflar o'nlab turli yuqori darajadagi modullarda qo'llaniladi. Utility qatlami nafaqat ichida qo'llaniladi Mexanizm qatlami, balki ichida Ma'lumotlarga kirish qatlami, Transport qatlami, Boshqa qatlam. U barcha yuqori darajadagi modullarda belgilangan interfeyslarni amalga oshirishi kerakmi?

Shubhasiz, bu yechim deyarli ideal emas, ayniqsa biz .NET yoki Java kabi ko'plab platformalarda mavjud bo'lmagan muammoni hal qilyapmiz.

Abstraksiya tushunchasi

Ko'p atamalar miyamizga shunchalik singib ketganki, biz ularga e'tibor berishni to'xtatamiz. Aksariyat "obyektga yo'naltirilgan" dasturchilar uchun bu biz "abstraksiya", "polimorfizm", "kapsulyatsiya" kabi haddan tashqari ko'p ishlatiladigan atamalar haqida o'ylashni to'xtatganimizni anglatadi. Nega ular haqida o'ylash kerak, chunki hamma narsa allaqachon aniq? ;)

Biroq, qaramlik inversiyasi printsipi va ta'rifning ikkinchi qismining ma'nosini to'g'ri tushunish uchun biz ushbu asosiy tushunchalardan biriga qaytishimiz kerak. Keling, Gradi Bucha kitobidagi "abstraksiya" atamasining ta'rifini ko'rib chiqaylik:

Abstraktsiya ba'zi bir ob'ektni boshqa barcha turdagi ob'ektlardan ajratib turadigan muhim belgilarini aniqlaydi va shu bilan uning kontseptual chegaralarini kuzatuvchi nuqtai nazaridan aniq belgilaydi.

Boshqacha qilib aytganda, abstraktsiya ob'ektning ko'rinadigan xatti-harakatlarini belgilaydi, bu dasturlash tillarida ob'ektning umumiy (va himoyalangan) interfeysi bilan belgilanadi. Ko'pincha biz interfeyslar yoki mavhum sinflar yordamida abstraktsiyalarni modellashtiramiz, garchi OOP nuqtai nazaridan bu kerak emas.

Keling, ta'rifga qaytaylik: Abstraktsiyalar tafsilotlarga bog'liq bo'lmasligi kerak. Tafsilotlar abstraktsiyalarga bog'liq bo'lishi kerak.

Bu nima ekanligini eslaganimizdan keyin qanday misol esga tushadi? abstraksiya? Abstraktsiya qachon tafsilotlarga bog'liq bo'la boshlaydi? Bu tamoyilning buzilishiga misol sifatida mavhum sinfni keltirish mumkin GZipStream, oladi MemoryStream, mavhum sinf emas Oqim:

GZipStream mavhum klassi ( // Abstraktsiya GZipStream himoyalangan aniq oqimni qabul qiladi GZipStream(MemoryStream memoryStream) () )

Ushbu tamoyilning buzilishining yana bir misoli konstruktorda qabul qilingan ma'lumotlarga kirish qatlamidan mavhum ombor sinfi bo'lishi mumkin. PostgreSqlConnection yoki SQL Server uchun ulanish satri, bu ma'lum bir amalga oshirish bilan bog'langan bunday abstraktsiyaning har qanday amalga oshirilishini amalga oshiradi. Ammo bu Bob Martin nimani anglatadi? Maqolada yoki kitobda keltirilgan misollarga qaraganda, Bob Martin "abstraksiya" tushunchasi bilan butunlay boshqacha narsani tushunadi.

PrinsipDIPMartinga ko'ra

Uning ta'rifini tushuntirish uchun Bob Martin quyidagi tushuntirishni beradi.

DIP printsipining biroz soddalashtirilgan, ammo baribir juda samarali talqini oddiy evristik qoida bilan ifodalanadi: "Siz abstraktsiyalarga bog'lanishingiz kerak." Unda aytilishicha, muayyan sinflarga bog'liqlik bo'lmasligi kerak; dasturdagi barcha ulanishlar mavhum sinf yoki interfeysga olib kelishi kerak.

  • Muayyan sinflarga havolalarni saqlaydigan o'zgaruvchilar bo'lmasligi kerak.
  • Aniq sinflardan kelib chiqadigan sinflar bo'lmasligi kerak.
  • Asosiy sinflardan birida amalga oshirilgan usulni bekor qiladigan usullar bo'lmasligi kerak.

Umuman olganda, DIP printsipining buzilishini va birinchi "aniqlashtiruvchi" nuqtani, xususan, quyidagi misolni ko'rsatish uchun:

Umumiy sinf tugmasi ( shaxsiy Chiroq chiroq; umumiy bekor Anketa() ( agar (/* ba'zi shart */) chiroq.TurnOn(); ) )

Endi bu nima ekanligini yana bir bor eslaylik abstraksiya va savolga javob bering: bu erda tafsilotlarga bog'liq bo'lgan "abstraksiya" bormi? Siz bu haqda o'ylayotganingizda yoki ushbu savolga javobni o'z ichiga olgan paragrafni qidirayotganingizda, men kichik bir chekinishni xohlayman.

Kod bitta qiziqarli xususiyatga ega. Kamdan-kam holatlardan tashqari, kodning o'zi to'g'ri yoki noto'g'ri bo'lishi mumkin emas; Bu xato yoki xususiyat bo'ladimi, undan nima kutilayotganiga bog'liq. Rasmiy spetsifikatsiya bo'lmasa ham (bu me'yor), kod faqat talab qilingan yoki bajarish uchun mo'ljallangan narsadan boshqa narsani qilsagina noto'g'ri bo'ladi. Shartnoma dasturlashning asosi aynan shu tamoyil bo'lib, unda spetsifikatsiya (niyatlar) oldindan shartlar, keyingi shartlar va invariantlar shaklida bevosita kodda ifodalanadi.

Sinfga qarab Tugma Dizayn nuqsonli yoki yo'qligini ayta olmayman. Men aniq aytishim mumkinki, sinf nomi uning amalga oshirilishiga mos kelmaydi. Sinf nomini o'zgartirish kerak Chiroq tugmasi yoki sinfdan olib tashlang Tugma maydon Chiroq.

Bob Martinning ta'kidlashicha, bu dizayn noto'g'ri, chunki "yuqori darajadagi dastur strategiyasi past darajadagi amalga oshirishdan ajratilmagan. Abstraktsiyalar tafsilotlardan ajratilmaydi. Bunday ajratish bo'lmasa, yuqori darajadagi strategiya avtomatik ravishda quyi darajadagi modullarga bog'liq bo'ladi va abstraktsiya avtomatik ravishda tafsilotlarga bog'liq."

Birinchidan, Men bu misolda “yuqori darajadagi strategiyalar” va “pastki darajadagi modullar”ni ko‘rmayapman.: mening nuqtai nazarimdan, sinflar Tugma Va Chiroq mavhumlikning bir xil darajasida (hech bo'lmaganda men buning aksi uchun hech qanday dalil ko'rmayapman). Bu sinf Tugma Biror kishini nazorat qila olish uni yuqori darajaga olib kelmaydi. Ikkinchidan, bu yerda “tafsilotga bog‘liq abstraksiya” yo‘q, “tafsilotga bog‘liq abstraksiyani amalga oshirish” mavjud, bu umuman bir xil narsa emas.

Martinning yechimi:



3-rasm - "Bog'likni o'zgartirish"

Bu yechim yaxshiroqmi? Keling, ko'rib chiqaylik…

"Martinga ko'ra" qaramlikni teskari o'zgartirishning asosiy afzalligi - bu egalik huquqining inversiyasi. Asl dizaynda, sinfni o'zgartirganda Chiroq sinf o'zgarishi kerak edi Tugma. Endi sinf Tugma interfeysga "egadir" ButtonServer, lekin u "pastki darajadagi" o'zgarishlar tufayli o'zgarmaydi, masalan Chiroq. Bu aksincha: sinf o'zgarishi ButtonServer faqat Button sinfidagi o'zgarishlar ta'siri ostida mumkin, bu sinfning barcha avlodlarida o'zgarishlarga olib keladi ButonServer!

Bog'liqlik inversiyasi printsipini shakllantirish ikkita qoidadan iborat bo'lib, ularga rioya qilish kod tuzilishiga juda ijobiy ta'sir qiladi:

  • Yuqori darajadagi modullar quyi darajadagi modullarga bog'liq bo'lmasligi kerak. Ikkalasi ham mavhumlikka bog'liq bo'lishi kerak.
  • abstraktsiyalar tafsilotlarga bog'liq bo'lmasligi kerak. Tafsilotlar abstraktsiyalarga bog'liq bo'lishi kerak.

Avvaliga bu unchalik jozibali ko'rinmaydi va o'quvchi, ehtimol, juda ko'p atamalar, murakkab nutq shakllari va misollar bilan juda zerikarli maqolaga tayyor bo'lib, hech narsa aniq emas. Ammo behuda, chunki, printsipial jihatdan, qaramlikni inversiya qilish tamoyiliga binoan, hamma narsa yana to'g'ri foydalanishga va asosiy g'oya - kodni qayta ishlatishga to'g'ri keladi.

Bu erda dolzarb bo'lgan yana bir kontseptsiya - bu turlarning zaif bog'lanishi, ya'ni ularning bir-biriga bog'liqligini kamaytirish yoki yo'q qilish, bu aslida abstraktsiya va polimorfizm yordamida erishiladi. Bu, aslida, qaramlik inversiyasi tamoyilining mohiyatidir.

Keling, bo'shashmasdan bog'lanishning amalda qanday ko'rinishini aniq ko'rsatadigan misolni ko'rib chiqaylik.

Aytaylik, biz tug'ilgan kun tortiga buyurtma berishga qaror qildik. Buning uchun biz ko'chaning burchagidagi ajoyib nonvoyxonaga bordik. Ular biz uchun baland ovozda "Hashamatli" nomi bilan tort pishirishi mumkinligini bilib oldik va ijobiy javob olgach, biz buyurtma berdik. Hammasi oddiy.

Keling, ushbu kodga qanday abstraktsiyalarni kiritish kerakligini hal qilaylik. Buning uchun o'zingizga bir nechta savol bering:

  • Nima uchun tort? Bundan tashqari, pirog yoki keklarga buyurtma berishingiz mumkin
  • Nega aynan sizning tug'ilgan kuningiz uchun? To'y yoki bitiruv bo'lsa-chi?
  • Nima uchun "Hashamatli", agar menga "Anthill" yoki "Praga" ko'proq yoqsa-chi?
  • Nega aynan shu novvoyxona, shahar markazidagi yoki boshqa joyda qandolatxona emas?

Va ularning har biri "agar nima bo'lsa" va "agar nima bo'lsa" kodning kengaytirilishi talab qilinadigan nuqtalar va shunga mos ravishda abstraktsiyalar orqali turlarning bo'sh ulanishi va ta'rifi.

Endi turlarni o'zlari qurishni boshlaylik.

Biz buyurtma berishimiz mumkin bo'lgan har qanday qandolat asari uchun interfeysni aniqlaymiz.

IPastry interfeysi (string nomi (olish; o'rnatish; ))

Va bu erda o'ziga xos dastur, bizning holatlarimiz uchun - tug'ilgan kun keki. Ko'rib turganingizdek, an'anaviy ravishda tug'ilgan kun keki shamni o'z ichiga oladi)))

Class Tug'ilgan kun torti: IPastry ( public int NumberOfCandles ( get; set; ) public string Name ( get; set; ) public override string ToString() ( return String.Format("(0) with (1) candle nices", Name, NumberOfCandles ;)))

Agar bizga to'y yoki shunchaki choy uchun tort kerak bo'lsa yoki keks yoki kremli pirog kerak bo'lsa, bizda hamma uchun asosiy interfeys mavjud.

Keyingi savol qandolat mahsulotlari bir-biridan qanday farq qiladi (albatta, nomdan tashqari). Albatta, retsept bilan!

Va retsept tushunchasi ingredientlar ro'yxatini va pishirish jarayonining tavsifini o'z ichiga oladi. Shunday qilib, biz tarkibiy kontseptsiya uchun alohida interfeysga ega bo'lamiz:

Interfeys IIIngredient ( IngredientName satri (olish;to'siq;) ikki martalik Miqdor (olish; o'rnatish; ) qator birliklari (olish; o'rnatish; ) )

Va bizning tortimiz uchun ingredientlar: un, sariyog ', shakar va qaymoq:

Class Flour:IIIngredient ( umumiy string IngredientName (olish; set; ) umumiy juft Miqdor (ol; set; ) umumiy qator Birliklar (ol; set; ) umumiy qator Sifat (olish; belgilangan; ) ) sinf Sariyog ': IIIngredient ( umumiy qator IngredientName ( get; set; ) umumiy juft Miqdor (olish; o'rnatish; ) umumiy qator Birliklar (ol; to'siq; ) ) klass Shakar: IIIngredient ( umumiy qator IngredientName (ol; to'siq; ) umumiy juft Miqdor (olish; o'rnatish; ) umumiy qator Birliklar ( get; set; ) umumiy satr Turi (olish; to'siq; ) ) klass Krem: IIIngredient ( umumiy string IngredientName ( get; set; ) umumiy juft Miqdor (ol; set; ) umumiy qator Birliklar (ol; to'siq; ) umumiy er-xotin Yog '(oling; o'rnating;))

Ingredientlar ro'yxati turli retseptlar va turli xil qandolat mahsulotlarida farq qilishi mumkin, ammo bizning retseptimiz uchun bu ro'yxat etarli.

Endi retsept kontseptsiyasiga o'tish vaqti keldi. Biz nimani pishirsak ham, biz har qanday holatda uning nima deb ataladiganini, nima ekanligini, idishga qanday ingredientlar kiritilganligini va uni qanday pishirishni bilamiz.

Interfeys IRecipe ( Tur PastryType ( get; set; ) string Name ( get; set;) IList Ingredientlar ( get; set;) string tavsifi ( get; set;) )

Xususan, tug'ilgan kun keki retseptini ifodalovchi sinf quyidagicha ko'rinadi:

Class Tug‘ilgan kun keki retsepti: IRecipe ( umumiy turi PastryType (olish; o‘rnatish; ) umumiy qator nomi (olish; o‘rnatish;) ochiq IList Ingredientlar ( get; set; ) umumiy qator Tavsif ( get; set; ) public BirthdayCakeReipe() ( Ingredientlar = yangi roʻyxat (); } }

Keling, ko'cha burchagidagi ajoyib nonvoyxonamizga o'taylik.

Albatta, biz boshqa ko'plab novvoyxonalarga murojaat qilishimiz mumkin, shuning uchun biz buning uchun ham asosiy interfeysni aniqlaymiz. Nonvoyxona uchun eng muhim narsa nima? Mahsulotlarni pishirish qobiliyati.

IBakery interfeysi (IPastry Bake (IRecipe retsepti); )

Mana bizning nonvoyxonamizni ifodalovchi sinf:

NiceBakeryOnTheCornerOFMyStreet sinfi (lug'at menyu = yangi lug'at (); public void AddToMenu(IRecipe retsepti) ( if (!menu.ContainsKey(recipe.Name)) ( menu.Add(recipe.Name, retsept); ) else ( Console.WriteLine("U allaqachon menyuda"); ) ) ommaviy IRecipe FindInMenu(string nomi) ( if (menu.ContainsKey(name)) (qaytish menyusi; ) Console.WriteLine("Kechirasiz...hozirda bizda "+ism" yo'q); null qaytish; ) umumiy IPastry Bake (Irecipe retsepti) ( if (retsept != null) ( IPastry pastry = Activator.CreateInstance(recipe.PastryType) IPastry sifatida; if (pastry != null) ( pastry.Name = retsept.Name; pastry IPastry sifatida qaytariladi; ) ) nullni qaytaring;))

Qolgan narsa kodni sinab ko'rishdir:

Sinf dasturi ( static void Main() ( //nonvoyxona sinfining inctance var bakery = new NiceBakeryOnTheCornerOFMyStreet(); //retsept uchun ingredientlarni tayyorlash var un = new Flour() ( IngredientName = "Un", Miqdori = 1,5 , Birlik = "kg" ); var sariyog ' = yangi Sariyog '() ( IngredientName = "Sariq", Miqdori = 0,5, Birlik = "kg" ); var shakar = yangi Shakar() ( IngredientName = "Shakar", Miqdor = 0,7 , Birliklar = "kg" ); var krem ​​= yangi Krem() ( IngredientName = "Krem", Miqdori = 1,0, Birliklar = "litr" ); //va bu retseptning o'zi var weddingCakeRecipe = new BirthdayCakeRecipe() ( PastryType = typeof(BirthdayCake), Ism = "Birthday Cake Luxury", Tavsif = "Go'zal tug'ilgan kun tortini qanday qilish haqida tavsif" ); WeddingCakeRecipe.Ingredients.Add(un); WeddingCakeRecipe.Ingredients.Qo'shish(sariq); WeddingCakeRecipe.Ingredients .Add(shakar); WeddingCakeRecipe.Ingredients.Add(creme); //bizning tort retseptini novvoyxonaning novvoyxona menyusiga qo'shish.AddToMenu(weddingCakeRecipe); //Endi buyurtma beraylik!! Tug'ilgan kun torti = bakery.Bake(bakery.FindInMenu("Birthday Cake Luxury")) Tug'ilgan kun torti sifatida; //bir nechta sham qo'shamiz;) tort.NumberOfCandles = 10; //va biz shu yerdamiz !!! Console.WriteLine(tort); ) )

Endi yana butun kodni ko'rib chiqamiz va uni baholaymiz. Kod juda oddiy, turlari, ularning abstraktsiyalari, ma'lumotlari va funksionalligi aniq belgilangan, kod kengaytirish va qayta foydalanish imkonini beradi. Har bir segment og'riqsiz ravishda asosiy turga mos keladigan boshqasi bilan almashtirilishi mumkin va bu kodning qolgan qismini buzmaydi.

Siz cheksiz turdagi ingredientlarni, turli xil qandolat mahsulotlari uchun retseptlarni qo'shishingiz, nonvoyxonalar, qandolatchilik do'konlari va boshqa shunga o'xshash muassasalarni tavsiflovchi boshqa sinflarni yaratishingiz mumkin.

Yomon natija emas. Va barchasi biz darslarni bir-biri bilan minimal darajada bog'lashga harakat qilganimiz uchun rahmat.

Endi qaramlikni inversiya qilish tamoyilini buzish oqibatlari haqida o'ylab ko'raylik:

  1. qattiqlik (tizimga biron bir o'zgartirish kiritish juda qiyin bo'ladi, chunki har bir o'zgarish uning ko'plab turli qismlariga ta'sir qilgan).
  2. mo'rtlik (tizimning bir qismiga biron bir o'zgartirish kiritilganda, uning boshqa qismlari zaif bo'lib qoladi va ba'zida bu birinchi qarashda juda aniq emas).
  3. harakatsizlik (boshqa tizimlarda kodni qayta ishlatishni unutishingiz mumkin, chunki modullar bir-biriga kuchli bog'langan).

Xo'sh, endi qaramlikni inversiyalash printsipi kodda qanchalik foydali ekanligi haqida o'z xulosalaringizni chiqaring. Menimcha, javob aniq.

14 javob

Asosan shunday deydi:

  • Abstraktsiyalar hech qachon tafsilotlarga bog'liq bo'lmasligi kerak. Tafsilotlar abstraktsiyalarga bog'liq bo'lishi kerak.

Bu nima uchun muhimligiga kelsak, bir so'z bilan aytganda: o'zgarish xavfli va amalga oshirishdan ko'ra kontseptsiyaga qarab, siz qo'ng'iroq saytlarida o'zgarishlarga bo'lgan ehtiyojni kamaytirasiz.

Samarali ravishda DIP kodning turli qismlari orasidagi ulanishni kamaytiradi. G'oya shundan iboratki, masalan, loggerni amalga oshirishning ko'plab usullari mavjud bo'lsa-da, uni ishlatish usuli vaqt o'tishi bilan nisbatan barqaror bo'lishi kerak. Agar siz jurnalga yozish kontseptsiyasini ifodalovchi interfeysni ajratib olishingiz mumkin bo'lsa, u interfeys vaqt o'tishi bilan uni amalga oshirishga qaraganda ancha barqaror bo'lishi kerak va qo'ng'iroq qiluvchi saytlar ro'yxatga olish mexanizmini saqlab qolish yoki kengaytirish orqali amalga oshirishingiz mumkin bo'lgan o'zgarishlarga nisbatan kamroq sezgir bo'lishi kerak.

Amalga oshirish interfeysga xos bo'lganligi sababli, siz ish vaqtida qaysi dastur sizning maxsus muhitingizga eng mos kelishini tanlash imkoniyatiga egasiz. Vaziyatga qarab, bu ham qiziqarli bo'lishi mumkin.

Agile Software Development, Principles, Patterns and Practices hamda C# tilidagi Agile Principles, Patterns and Practices kitoblari Bog'liqlikni teskari o'zgartirish tamoyilining asl maqsadlari va motivlarini to'liq tushunish uchun eng yaxshi manbadir. "Bog'liqlikni qaytarish printsipi" maqolasi ham yaxshi manbadir, lekin u ilgari eslatib o'tilgan kitoblarda yakunlangan loyihaning ixchamlashtirilgan versiyasi bo'lganligi sababli, u paketga egalik tushunchasi haqida ba'zi muhim munozaralarni ortda qoldiradi. va asosiy bo'lgan interfeyslar Ushbu tamoyil "Dizayn naqshlari" (Gamma va boshqalar) kitobida keltirilgan "amalga oshirish uchun emas, balki interfeys uchun dastur" haqidagi umumiy tavsiyalardan farq qiladi.

Qisqacha xulosa qilish uchun, qaramlikni inversiya qilish printsipi birinchi navbatda o'zgartirish an'anaviy ravishda bog'liqliklarni "yuqori darajadagi" komponentlardan "pastki darajadagi" komponentlarga yo'naltirish, "pastki darajadagi" komponentlar interfeyslarga bog'liq bo'lishi uchun, tegishli“yuqori darajadagi” komponentlarga.(Izoh: “Yuqori darajali” komponent bu yerda tashqi bog‘liqliklarni/xizmatlarni talab qiluvchi komponentni nazarda tutadi, lekin uning qatlamli arxitekturadagi kontseptual pozitsiyasi shart emas.) kamayadi u kabi siljishlar nazariy jihatdan kamroq qimmatli bo'lgan komponentlardan nazariy jihatdan qimmatroq bo'lgan komponentlarga.

Bunga tashqi bog'liqliklari komponent iste'molchisi amalga oshirishni ta'minlashi kerak bo'lgan interfeys sifatida ifodalangan komponentlarni loyihalash orqali erishiladi. Boshqacha qilib aytganda, ba'zi interfeyslar komponentdan qanday foydalanishingizni emas, balki komponentga nima kerakligini bildiradi (masalan, "IDoSomething" emas, "INeedSomething").

Bog'liqlikni teskari o'zgartirish printsipi interfeyslardan foydalangan holda bog'liqliklarni mavhumlashning oddiy amaliyoti (masalan, MyService -> ) bilan bog'liq emas. Garchi bu komponentni bog'liqlikning maxsus amalga oshirish detalidan ajratsa ham, iste'molchi va qaramlik o'rtasidagi munosabatni o'zgartirmaydi (masalan, ⇐ Logger.

Bog'liqlikni o'zgartirish printsipining ahamiyatini bitta maqsadga - tashqi bog'liqliklarga tayanadigan dasturiy ta'minot komponentlarini qayta ishlatish qobiliyati (ro'yxatga olish, tekshirish va hk) bilan bog'lash mumkin.

Qayta foydalanishning ushbu umumiy maqsadi doirasida biz qayta foydalanishning ikkita kichik turini ajratib ko'rsatishimiz mumkin:

    Dasturiy ta'minot komponentidan bir nechta ilovalarga bog'liqlik ilovalaridan foydalanish (masalan, siz DI konteynerini ishlab chiqdingiz va jurnalni taqdim qilmoqchisiz, lekin konteyneringizni ma'lum bir loggerga bog'lashni xohlamaysiz, shuning uchun konteyneringizdan foydalanadigan har bir kishi jurnaldan foydalanishi kerak. Siz tanlagan kutubxona).

    Rivojlanayotgan kontekstda dasturiy ta'minot komponentlaridan foydalanish (masalan, siz dasturning turli versiyalarida bir xil bo'lib qoladigan biznes mantiqiy komponentlarini ishlab chiqdingiz, bu erda amalga oshirish tafsilotlari rivojlanadi).

Komponentlarni bir nechta ilovalarda, masalan, infratuzilma kutubxonasida qayta ishlatishning birinchi holatida, maqsad iste'molchilarni o'z kutubxonangizning bog'liqliklariga bog'lamasdan, iste'molchilarni asosiy infratuzilma bilan ta'minlashdir, chunki bunday bog'liqliklardan bog'liqliklarni olish iste'molchilardan ham talab qilishni talab qiladi. bir xil bog'liqliklar. Kutubxonangiz iste'molchilari bir xil infratuzilma ehtiyojlari uchun (masalan, NLog va log4net) boshqa kutubxonadan foydalanishga qaror qilganda yoki ular talab qilingan kutubxonaning talab qilingan versiya bilan orqaga mos kelmaydigan keyingi versiyasidan foydalanishga qaror qilganda, bu muammoli bo'lishi mumkin. kutubxonangiz tomonidan.

Biznes mantiqiy komponentlarini qayta ishlatishning ikkinchi holatida (masalan, "Yuqori darajadagi komponentlar") maqsad dasturni amalga oshirishni asosiy miqyosda amalga oshirish tafsilotlarining o'zgaruvchan ehtiyojlaridan ajratishdir (masalan, doimiy kutubxonalarni o'zgartirish/yangilash, kutubxonalar xabarlarini almashtirish) . shifrlash strategiyalari va boshqalar). Ideal holda, dasturni amalga oshirish tafsilotlarini o'zgartirish ilovaning biznes mantiqini qamrab oluvchi komponentlarni buzmasligi kerak.

Eslatma. Ba'zilar, bitta ishlab chiquvchi ilovada ishlatiladigan biznes mantiqiy komponentlari kabi komponentlar faqat bitta foydalanishni tashkil qiladi, deb hisoblab, ushbu ikkinchi holatni haqiqiy qayta foydalanish sifatida tasvirlashga e'tiroz bildirishi mumkin. Biroq, bu erda g'oya shundan iboratki, dasturni amalga oshirish tafsilotlaridagi har bir o'zgarish yangi kontekstni va shuning uchun boshqa foydalanish holatini ifodalaydi, ammo yakuniy maqsadlarni izolyatsiya va ko'chma sifatida ajratish mumkin.

Ikkinchi holda, qaramlikni inversiyalash tamoyiliga amal qilish qandaydir foyda keltirishi mumkin bo'lsa-da, shuni ta'kidlash kerakki, uning Java va C# kabi zamonaviy tillarga nisbatan ahamiyati sezilarli darajada kamaydi, ehtimol u ahamiyatsiz bo'lib qoladi. Yuqorida aytib o'tilganidek, DIP amalga oshirish tafsilotlarini alohida paketlarga butunlay ajratishni o'z ichiga oladi. Rivojlanayotgan dastur bo'lsa, shunchaki biznes domenida belgilangan interfeyslardan foydalanish, hatto amalga oshirish tafsilotlari oxir-oqibat bir xil paketda joylashgan bo'lsa ham, amalga oshirish tafsilotlari komponentlarining o'zgaruvchan ehtiyojlari tufayli yuqori darajadagi komponentlarni o'zgartirish zaruratidan himoya qiladi. . Printsipning bu qismi tilni kodlashtirish vaqtida tegishli bo‘lgan (masalan, C++) yangi tillarga tegishli bo‘lmagan jihatlarni aks ettiradi. Biroq, qaramlikni o'zgartirish printsipining ahamiyati birinchi navbatda qayta ishlatiladigan dasturiy ta'minot komponentlari/kutubxonalarni ishlab chiqish bilan bog'liq.

Ushbu printsipni batafsilroq muhokama qilish mumkin, chunki u interfeyslardan oddiy foydalanish, bog'liqlikni in'ektsiya qilish va bo'lingan interfeys naqshlari bilan bog'liq.

Dasturiy ta'minot dasturlarini ishlab chiqishda biz past darajadagi sinflarni - asosiy va asosiy operatsiyalarni amalga oshiradigan sinflarni (diskga kirish, tarmoq protokollari va boshqalar) va yuqori darajadagi sinflarni - murakkab mantiqni (biznes oqimlari,... ) qamrab oluvchi sinflarni ko'rib chiqishimiz mumkin. .

Ikkinchisi past darajadagi sinflarga tayanadi. Bunday tuzilmalarni amalga oshirishning tabiiy yo'li past darajadagi sinflarni yozish bo'ladi va biz har doim murakkab yuqori darajadagi sinflarni yozishga majbur bo'lamiz. Yuqori darajadagi sinflar boshqalar nuqtai nazaridan aniqlanganligi sababli, buni amalga oshirishning mantiqiy usuli kabi ko'rinadi. Ammo bu sezgir dizayn emas. Agar biz past darajadagi sinfni almashtirishimiz kerak bo'lsa nima bo'ladi?

Bog'liqlik inversiyasi printsipi quyidagilarni ta'kidlaydi:

  • Yuqori darajadagi modullar past darajadagi modullarga bog'liq bo'lmasligi kerak. Ikkalasi ham abstraktsiyalarga bog'liq bo'lishi kerak.

Ushbu tamoyil dasturiy ta'minotdagi yuqori darajadagi modullar quyi darajadagi modullarga bog'liq bo'lishi kerakligi haqidagi odatiy fikrni "teskari" qilishga qaratilgan. Bu erda yuqori darajadagi modullar quyi darajadagi modullar tomonidan amalga oshiriladigan abstraktsiyaga (masalan, interfeys usullarini tanlash) egalik qiladi. Shunday qilib, quyi darajadagi modullar yuqori darajadagi modullarga bog'liq.

Bog'liqlik inversiyasidan samarali foydalanish dastur arxitekturangizda moslashuvchanlik va barqarorlikni ta'minlaydi. Bu sizning ilovangiz xavfsizroq va barqaror rivojlanishiga imkon beradi.

An'anaviy qatlamli arxitektura

An'anaga ko'ra, qatlamli arxitekturaning foydalanuvchi interfeysi biznes darajasiga bog'liq bo'lib, u o'z navbatida ma'lumotlarga kirish darajasiga bog'liq edi.

Siz qatlam, paket yoki kutubxonani tushunishingiz kerak. Keling, kod qanday ishlashini ko'rib chiqaylik.

Bizda ma'lumotlarga kirish qatlami uchun kutubxona yoki paket bo'ladi.

// DataAccessLayer.dll umumiy klassi ProductDAO ( )

// DataAccessLayer yordamida BusinessLogicLayer.dll; ommaviy sinf ProductBO (xususiy ProductDAO productDAO; )

Bog'liqlik inversiyasi bilan qatlamli arxitektura

Bog'liqlik inversiyasi quyidagilarni ko'rsatadi:

Yuqori darajadagi modullar past darajadagi modullarga bog'liq bo'lmasligi kerak. Ikkalasi ham abstraktsiyalarga bog'liq bo'lishi kerak.

Abstraktsiyalar tafsilotlarga bog'liq bo'lmasligi kerak. Tafsilotlar abstraktsiyalarga bog'liq bo'lishi kerak.

Yuqori va past darajadagi modullar nima? Kutubxonalar yoki paketlar kabi modullar haqida fikr yuritadigan bo'lsak, yuqori darajadagi modullar an'anaviy ravishda bog'liqliklarga ega bo'lganlar va ular bog'liq bo'lgan past darajadagi modullar bo'ladi.

Boshqacha qilib aytadigan bo'lsak, modulning yuqori darajasi harakat chaqirilgan joyda va past darajali harakat bajariladigan joyda bo'ladi.

Ushbu tamoyildan oqilona xulosa chiqarish mumkin: tugunlar o'rtasida bog'liqlik bo'lmasligi kerak, lekin abstraktsiyaga bog'liqlik bo'lishi kerak. Ammo biz qo'ygan yondashuvga ko'ra, biz investitsiyaga bog'liqlikdan noto'g'ri foydalanayotgan bo'lishimiz mumkin, ammo bu mavhumlik.

Tasavvur qiling, biz kodimizni shunday moslashtiramiz:

Bizda mavhumlikni belgilaydigan ma'lumotlarga kirish qatlami uchun kutubxona yoki paket bo'lar edi.

// DataAccessLayer.dll umumiy interfeysi IProductDAO umumiy klassi ProductDAO: IPProductDAO( )

Va ma'lumotlarga kirish darajasiga bog'liq bo'lgan boshqa kutubxona yoki paket darajasidagi biznes mantig'i.

// DataAccessLayer yordamida BusinessLogicLayer.dll; ommaviy sinf ProductBO (xususiy IPProductDAO productDAO; )

Garchi biz abstraktsiyaga bog'liq bo'lsak-da, biznes va ma'lumotlarga kirish o'rtasidagi munosabatlar bir xil bo'lib qolmoqda.

Bog'liqlik inversiyasiga erishish uchun qat'iylik interfeysi past darajadagi modulda emas, balki yuqori darajadagi mantiq yoki domen joylashgan modul yoki paketda aniqlanishi kerak.

Avval domen qatlami nima ekanligini aniqlang va uning aloqa mavhumligi qat'iylik bilan belgilanadi.

// Domain.dll umumiy interfeysi IPProductRepository; DataAccessLayer yordamida; ommaviy sinf ProductBO (xususiy IPProductRepository productRepository; )

Qat'iylik darajasi domenga bog'liq bo'lsa, agar qaramlik aniqlangan bo'lsa, endi uni o'zgartirish mumkin.

// Persistence.dll umumiy klassi ProductDAO: IPProductRepository( )

Printsipni chuqurlashtirish

Kontseptsiyani yaxshi tushunish, maqsad va foydani chuqurlashtirish muhimdir. Agar biz mexanikada qolib, odatiy omborni o'rgansak, biz qaramlik printsipini qayerda qo'llashimiz mumkinligini aniqlay olmaymiz.

Lekin nega biz qaramlikni o'zgartiramiz? Aniq misollardan tashqari asosiy maqsad nima?

Bu odatda kamroq barqaror narsalardan mustaqil bo'lgan eng barqaror narsalarni tez-tez o'zgartirishga imkon beradi.

Domen mantig'i yoki qat'iylik bilan bog'lanish uchun mo'ljallangan harakatlardan ko'ra, doimiylik turini yoki ma'lumotlar bazasini yoki bir xil ma'lumotlar bazasiga kirish texnologiyasini o'zgartirish osonroq. Bu qaramlikni bekor qilishga olib keladi, chunki bu o'zgarish sodir bo'lsa, qat'iylikni o'zgartirish osonroq bo'ladi. Shunday qilib, biz domenni o'zgartirishimiz shart emas. Domen qatlami eng barqaror hisoblanadi, shuning uchun u hech narsaga bog'liq bo'lmasligi kerak.

Ammo bu saqlash misolidan ko'proq narsa bor. Ushbu tamoyil qo'llaniladigan ko'plab stsenariylar mavjud va bu printsipga asoslangan arxitekturalar mavjud.

arxitektura

Bog'liqlik inversiyasi uning ta'rifi uchun kalit bo'lgan arxitekturalar mavjud. Barcha domenlarda bu eng muhimi va domen va qolgan paketlar yoki kutubxonalar o'rtasidagi aloqa protokolini aniqlaydigan abstraktsiyalar.

Toza arxitektura

Men uchun rasmiy maqolada tasvirlangan qaramlik inversiyasi printsipi

C++ tilidagi muammo shundaki, sarlavhali fayllar odatda shaxsiy maydonlar va usullarning deklaratsiyasini o'z ichiga oladi. Shunday qilib, agar yuqori darajadagi C ++ moduli past darajadagi modul uchun sarlavha faylini o'z ichiga olsa, u haqiqiyga bog'liq bo'ladi. amalga oshirish ushbu modulning tafsilotlari. Va bu juda yaxshi emasligi aniq. Ammo bugungi kunda tez-tez ishlatiladigan zamonaviy tillarda bu muammo emas.

Yuqori darajadagi modullar past darajadagi modullarga qaraganda kamroq qayta foydalanish mumkin, chunki birinchisi odatda ikkinchisiga qaraganda ko'proq dastur/kontekstga xosdir. Misol uchun, UI ekranini amalga oshiradigan komponent eng yuqori darajali va juda (to'liq?) dasturga xosdir. Bunday komponentni boshqa dasturda qayta ishlatishga urinish samarasiz bo'lib, faqat haddan tashqari rivojlanishga olib kelishi mumkin.

Shunday qilib, B komponentiga bog'liq bo'lgan (A ga bog'liq bo'lmagan) bir xil darajadagi A komponentida alohida abstraksiya yaratish faqat A komponenti turli ilovalar yoki kontekstlarda qayta foydalanish uchun foydali bo'lsagina amalga oshirilishi mumkin. Agar bunday bo'lmasa, DIP ilovasi yomon dizayn bo'ladi.

Bog'liqlik inversiyasi tamoyilini ifodalashning aniqroq usuli:

Murakkab biznes mantig'ini qamrab olgan modullaringiz biznes mantiqini qamrab oluvchi boshqa modullarga bevosita bog'liq bo'lmasligi kerak. Buning o'rniga, ular faqat oddiy ma'lumotlarga interfeyslarga bog'liq bo'lishi kerak.

Ya'ni, odatda odamlar kabi Logic sinfingizni amalga oshirish o'rniga:

Sinfga bog'liqlik ( ... ) klassi Mantiq ( xususiy bog'liqlik dep; int doSomething() ( // Bu yerda dep yordamida biznes mantiqi ) )

siz shunday bir narsa qilishingiz kerak:

Sinfga bog'liqlik ( ... ) interfeysi Ma'lumotlar ( ... ) sinfi DataFromDependency amalga oshiradi Ma'lumotlar ( xususiy Dependency dep; ... ) sinf Mantiq ( int doSomething(Data data) ( // ma'lumotlar bilan biror narsani hisoblash ) )

Data va DataFromDependency Dependency bilan emas, balki Logic bilan bir modulda yashashi kerak.

Nima uchun bu?

Yaxshi javoblar va yaxshi misollar bu erda allaqachon boshqalar tomonidan berilgan.

Bog'liqlikni o'zgartirishning maqsadi dasturiy ta'minotni qayta foydalanishga yaroqli qilishdir.

G'oya shundan iboratki, bir-biriga tayanadigan ikkita kod bo'lagi o'rniga ular qandaydir mavhum interfeysga tayanadi. Keyin istalgan qismni ikkinchisisiz qayta ishlatishingiz mumkin.

Bunga odatda Java-dagi Spring kabi boshqaruv konteynerini (IoC) teskari aylantirish orqali erishiladi. Ushbu modelda ob'ektlarning xossalari tashqariga chiqadigan va ularning bog'liqligini topishdan ko'ra, XML konfiguratsiyasi orqali o'rnatiladi.

Ushbu psevdokodni tasavvur qiling ...

Ommaviy sinf MyClass (davlat xizmati myService = ServiceLocator.service; )

MyClass to'g'ridan-to'g'ri Xizmat sinfiga va ServiceLocator sinfiga bog'liq. Agar siz uni boshqa ilovada ishlatmoqchi bo'lsangiz, bu ikkalasi uchun ham talab qilinadi. Endi buni tasavvur qiling...

Ommaviy sinf MyClass (ommaviy IService myService;)

MyClass endi bitta interfeysdan, IService interfeysidan foydalanadi. Biz IoC konteyneriga ushbu o'zgaruvchining qiymatini o'rnatishiga ruxsat beramiz.

Oziq-ovqat mahsulotlarini ishlab chiqaruvchidan so'raydigan mehmonxona bo'lsin. Mehmonxona oziq-ovqat ishlab chiqaruvchisiga ovqat nomini (masalan, tovuq go'shti) beradi va Generator so'ralgan taomni mehmonxonaga qaytaradi. Ammo mehmonxona qanday taom olishi va qanday xizmat ko‘rsatishiga ahamiyat bermaydi. Shunday qilib, Generator mehmonxonaga "Oziq-ovqat" yorlig'i bilan oziq-ovqat etkazib beradi.

Bu JAVA da amalga oshirish

Zavod usuli bilan FactoryClass. Oziq-ovqat generatori

Ommaviy sinf FoodGenerator ( Oziq-ovqat oziq-ovqat; ommaviy oziq-ovqat getFood(String nomi)( if(name.equals("fish"))( food = new Fish(); )else if(name.equals("tovuq"))( food = new Chicken(); )else food = null; ovqatni qaytarish; ))

Sinf izohi/interfeysi

Ommaviy abstrakt sinf Oziq-ovqat ( //Sifatni ta'minlash uchun bolalar sinfining hech biri bu usulni bekor qilmaydi... public void quality())( String fresh = "Bu yangi" + getName(); String tasty = "Bu mazali " + getName(); System.out.println(yangi); System.out.println(tasty); ) ommaviy abstrakt String getName(); )

Tovuq oziq-ovqat sotadi (Beton sinf)

Public Class Chicken extensions Food ( /*Barcha oziq-ovqat turlari yangi va mazali bo'lishi kerak, shuning uchun * Ular super-klass usuli "property()"*/ public String getName())( return "Chicken"; ))

Baliq oziq-ovqat sotadi (maxsus sinf)

Ommaviy toifali baliq ovqatni kengaytiradi ( /*Barcha oziq-ovqat turlari yangi va mazali bo'lishi kerak, shuning uchun * Ular "property()" super-klass usulini bekor qilmaydi*/ public String getName())( "Fish"ni qaytarish; ))

Nihoyat

Mehmonxona

Ommaviy sinf mehmonxonasi ( public static void main(String args)( //Fabrika sinfidan foydalanish.... FoodGenerator foodGenerator = new FoodGenerator(); //Oziq-ovqatlarni jonlantirish uchun zavod usuli... Oziq-ovqat mahsuloti = foodGenerator.getFood( "tovuq"); oziq-ovqat.sifat(); ) )

Ko'rib turganingizdek, mehmonxona tovuqmi yoki baliqmi, bilmaydi. Faqat ma'lumki, bu oziq-ovqat mahsuloti, ya'ni. Mehmonxona ovqatlanish sinfiga bog'liq.

Bundan tashqari, Fish and Chicken sinfi Oziq-ovqat sinfini qo'llashini va mehmonxona bilan bevosita bog'liq emasligini ham sezishingiz mumkin. bular. tovuq va baliq ham oziq-ovqat sinfiga bog'liq.

Bu shuni anglatadiki, yuqori darajadagi komponent (mehmonxona) va past darajadagi komponent (baliq va tovuq) abstraktsiyaga (oziq-ovqat) bog'liqdir.

Bunga qaramlik inversiyasi deyiladi.

Dependency Inversion Principle (DIP) buni bildiradi

i) Yuqori darajadagi modullar past darajadagi modullarga bog'liq bo'lmasligi kerak. Ikkalasi ham abstraktsiyalarga bog'liq bo'lishi kerak.

ii) Abstraktsiyalar hech qachon tafsilotlarga bog'liq bo'lmasligi kerak. Tafsilotlar abstraktsiyalarga bog'liq bo'lishi kerak.

Ommaviy interfeys ICustomer ( string GetCustomerNameById(int id); ) umumiy sinf Mijoz: ICustomer ( //ctor public Customer()() umumiy qator GetCustomerNameById(int id) ( “Dummy Customer Name”ni qaytarish; ) ) umumiy sinf CustomerFactory ( umumiy statik ICustomer GetCustomerData() ( new Customer(); ) ) umumiy sinf CustomerBLL ( ICustomer _customer; public CustomerBLL() ( _customer = CustomerFactory.GetCustomerData(); ) umumiy qator GetCustomerNameById(int id)tomer(return.NayB); ) ) umumiy sinf dasturi ( statik void Main() ( CustomerBLL customerBLL = new CustomerBLL(); int customerId = 25; string customerName = customerBLL.GetCustomerNameById(customerId); Console.WriteLine(customerName); Console.ReadKey();

Eslatma. Sinf aniq tafsilotlarga (interfeysni amalga oshirish) emas, balki interfeys yoki mavhum sinflar kabi abstraktsiyalarga bog'liq bo'lishi kerak.

baham ko'ring

Oxirgi yangilanish: 03/11/2016

Bog'liqlik inversiyasi printsipi Bog'liqlik inversiyasi printsipi sinovdan o'tkazish, o'zgartirish va yangilash oson bo'lgan erkin bog'langan ob'ektlarni yaratish uchun ishlatiladi. Ushbu printsipni quyidagicha shakllantirish mumkin:

Yuqori darajadagi modullar quyi darajadagi modullarga bog'liq bo'lmasligi kerak. Ikkalasi ham abstraktsiyalarga bog'liq bo'lishi kerak.

Abstraktsiyalar tafsilotlarga bog'liq bo'lmasligi kerak. Tafsilotlar abstraktsiyalarga bog'liq bo'lishi kerak.

Printsipni tushunish uchun quyidagi misolni ko'rib chiqing:

Sinf kitobi ( umumiy satr Matn ( get; set; ) umumiy ConsolePrinter Printer ( get; set; ) public void Print() ( Printer.Print(Matn); ) ) sinf ConsolePrinter ( public void Print(string text) ( Console.WriteLine (matn); ))

Kitobni ifodalovchi Book sinfi chop etish uchun ConsolePrinter sinfidan foydalanadi. Ushbu ta'rif bilan Book klassi ConsolePrinter sinfiga bog'liq. Bundan tashqari, biz kitobni chop etishni faqat ConsolePrinter sinfidan foydalangan holda konsolda amalga oshirish mumkinligini qat'iy belgilab oldik. Boshqa variantlar, masalan, printerga chiqarish, faylga chiqarish yoki ba'zi grafik interfeys elementlaridan foydalanish - bularning barchasi bu holda istisno qilinadi. Kitobni chop etish abstraktsiyasi ConsolePrinter sinfining tafsilotlaridan ajratilmagan. Bularning barchasi qaramlikni inversiya qilish tamoyilining buzilishidir.

Keling, sinflarimizni past darajadagi amalga oshirishdan abstraktsiyalarni ajratib, qaramlik inversiyasi printsipiga moslashtirishga harakat qilaylik:

Interfeys IPrinter ( void Print(string text); ) class Book ( public string Text ( get; set; ) public IPrinter Printer ( get; set; ) public Book(IPrinter printer) ( this.Printer = printer; ) public void Print( ) ( Printer.Print(Matn; ) ) sinf ConsolePrinter: IPrinter ( public void Print(string text) ( Console.WriteLine("Print to console"); ) ) class HtmlPrinter: IPrinter ( public void Print(string text) ( Console.WriteLine("html-da chop etish"); ) )

Kitobni chop etish abstraktsiyasi endi aniq amaliyotlardan ajratilgan. Natijada, Book sinfi ham, ConsolePrinter sinfi ham IPrinter abstraktsiyasiga bog'liq. Bundan tashqari, endi biz IPrinter abstraktsiyasining qo'shimcha past darajadagi ilovalarini yaratishimiz va ularni dasturda dinamik ravishda qo'llashimiz mumkin:

Kitob kitobi = yangi kitob (yangi ConsolePrinter()); kitob.Print(); book.Printer = new HtmlPrinter(); kitob.Print();