Strategy design patterni
Design patternlar mavzusini davom etgan holda bugun Behavioral design patternlar oilasiga mansub Strategy Design Pattern haqida gaplashamiz. Qo’llash jihatidan ancha sodda lekin juda ko’p muammolarni birdaniga hal qila oladigan ushbu design patternni hayotda juda ko’p uchratishingiz mumkin. Bugun Observer design patterni haqidagi maqolada misol qilib olganimiz, yuk jo’natish dasturchasiga yangi imkoniyat qo’shamiz.
Vodiy tomonlardan yuk jo’natish uchun koskader taksichilar xizmatidan foydalanishadi. Voha tomonlarda esa taksiga qo’shimcha avtobuslar ham yuk yetqazib berish xizmatini ko’rsatishadi. Maqolamizda Qo’qonlik shumaxer haydovchilarni misol qilib olamiz. Odatda doimiy tarzda yuk jo’natib turuvchilar 3 yoki 4 haydovchiga mijoz bo’lib olishadi va har doim ularning xizmatidan foydalanishadi. Haydovchilar tezkorligiga va keraklik paytda bo’shligiga qarab tanlanadi. Xizmat haqqiyam turlicha bo’ladi. Misol qilib uch haydovchini olaylik:
- Alisher aka: 10,000 sum
- Sherali aka: 12,000 sum
- Turdiali aka: 13,000 sum
Yuk jo’natishdan oldin, keraklik haydovchiga bog’lanib yukni unga topshirasiz. Demak Yuk
classimizda Haydovchi haqida ma’lumot bo’lishi lozim. Quyida ushbu biznes mantiq uchun classlarni yaratib olganmiz.
//Haydovchilar ro'yhati public enum Haydovchilar { AlisherAka, SheraliAka, TurdialiAka }
//jo'natilayotgan yuk uchun class public class Yuk { public string Nomi { get; set; } public string Manzil { get; set; } //yuk jo'natilayotgan haydovchi public Haydovchilar Haydovchi { get; set; } }
Agar design patternlar bilan tanish bo’lmasangiz ushbu jarayonni quyidagicha tadbiq qilishingiz mumkin.
//Yukning narxini hisoblash uchun service public class YukJunatishHaydovchiService { public double YukJunatishNarxi(Yuk yuk) { //haydovchining kim ekanligiga qarab switch (yuk.Haydovchi) { case Haydovchilar.AlisherAka: return AlisherAkaniNarxi(yuk); case Haydovchilar.SheraliAka: return SheraliAkaniNarxi(yuk); case Haydovchilar.TurdialiAka: return TurdialiAkaNarxi(yuk); default: return 0; } } //Alisher akaning narxi private double AlisherAkaniNarxi(Yuk yuk) { return 10000; } //Sherali akaning narxi private double SheraliAkaniNarxi(Yuk yuk) { return 12000; } //Turdiali akaning narxi private double TurdialiAkaNarxi(Yuk yuk) { return 13000; } }
Yuqoridagi kodni xato deb bo’lmaydi lekin kamchiliklari bor, ko’rinishidan standard yechimga o’xshaydi. Ushbu yechimning kamchiliklari:
- Tasavvur qiling yangi haydovchiga mijoz bo’ldik. Qudratali akaning narxini tizimga qo’shish uchun
YukJunatishHaydovchiService
classni o’zgartirishimiz lozim. Avvalswitch
ni o’zgartirib keyin shu classga yangi metod qo’shishga to’gri keladi. Ya’ni, Ochiq/Yopiq prinsipiga teskari holat. Yaratayotgan classlarimizni iloji boricha o’zgartirishlarga yopiq, kengaytmalarga ochiq qilib yaratishimiz lozim. - 3 ta emas 20 ta haydovchi bilan ishlashga to’g’ri kelsa nima bo’ladi. 20 ta qismdan iborat
switch
, va 20 ta turli metod bir classning ichida joylashgan bo’ladi, aniqki bu yaxshi emas. - Bizning misol juda sodda ko’rinishda berilgan.
YukJunatishHaydovchiService
ning tizimning boshqa servislariga murojaat qilish ehtimolligi yuqori bu esa, bir classning sifat darajasi(code metrics) yomon holatga kelishi va maintance muammolari paydo bo’lishini anglatadi. Coupling darajasi o’sib, cohesion darajasi pasayadi. - Codeni yozish jarayonini yanada murakkablashtiradi. Masalan, har bir haydovchining biznes mantig’i ustida alohida dasturchi ishlayapti. Endi o’ylab ko’ring, bir faylning ustida bir vaqtning o’zida bir necha odam ishlayapti, bu narsa source code control(SVN,TFS,Git) tizimlaridan foydalanishni qiyinlashtiradi. Agar SVN kabi tizimlardan foydalanib ko’rgan bo’lsangiz, bir faylni «o’zgartirishlar uchun yopish(lock)» ba’zida qanchalik bosh og’riq bo’lishi bilasiz.
- Yana bir muhim prinsip SRP ga hilofdir.
Bu kabi muammolar uchun eng yaxshi yechim Strategy Design Patterni bo’la oladi.
Maqsadi.
- Bir maqsadda yaratilgan har xil algoritmlar oilasini bir to’plam sifatida ifodalaydi.
- Har bir algoritmni enkapsulatsiya qiladi. Tayyor yaratilib qo’yilgan classlardan foydalanayotgan dasturchi uning ichida nima borligi bilan umuman ishi bo’lmaydi, u faqat ishlatishni biladi holos.
- Runtime vaqtida bir alogiritmni boshqasiga almashtiradi.
- P2I prinsipini amalga oshiradi. Ya’ni foydalanuvchi konkret class bilan emas interface bilan ishlaydi
- Eng asosiy ustunligi sifatida Ochiq/Yopiq prinsipini mukammal tarzda ifodalashidir.
UML.
Quyidagi rasmda Strategyning standard ko’rinishi berilgan.
Design patternning tarkibi
Strategy
— barcha algoritmlar uchun umumiy bo’lgan qoidalar beriladi. BarchaConcreteStrategy
lar ushbu interfacedan meros oladi. P2I prinsipga ko’ra ushbu interface bilan ishlanadi.-
ConcreteStrategy
— bir xil vazifa uchun mo’ljallangan algoritmlarning konkret implementatsiyasi. -
Context
—ConcreteStrategy
ni o’z ichida saqlaydi va chaqiradi, lekinConcreteStrategy
ning ichidagi mantiqdan umuman bexabar bo’ladi. Yuqorida bergan misolimizda shu jihatdan yutqazganmiz, ya’niYukJunatishHaydovchiService
barcha haydovchilar haqida ma’lumotga ega.
Kamchiliklarni to’g’irlaymiz.
Endi yuqorida berilgan masala uchun Strategyni DPni qo’llashga urinib ko’raylik. YukJunatishHaydovchiService
ga Context
sifatida qarasak bo’ladi. YukJunatishHaydovchiService
ning ichidagi metodlardan qutulamiz, ularni class darajasiga olib chiqamiz. Ko’rinib turibdiki ular bir xil vazifa bajaruvchi 3 xil algoritm. Undan tashqari Quvvatali akaniyam qo’shish muammosini ko’tardik. Quyida o’zgarishlar uchun UML berilgan:
Quyida ushbu UMLning kod ko’rinishda berganmiz.
IHaydovchi
interface.
//IStrategyga mos interface, //barcha algoritmlar uchun umumiy qoidalarni belgilab beradi //Context shu interface bilan ishlaydi public interface IHaydovchi { //barcha algoritmlar ushbu metodni implementatsiya qilishi lozim double YukJunatishNarxi(Yuk yuk); }
Har bir algoritmni alohida class/fayl sifatida yozib olamiz.
//IHaydovchi ning Alisher aka algoritmi public class AlisherAka : IHaydovchi { public double YukJunatishNarxi(Yuk yuk) { Console.WriteLine("Uka, sal charchab turibman dam ovolishim kere yukiz sal kechro boradi.") //murakkab mantiqlar ham shu yerda qo'shilishi mumkin return 10000; } } //IHaydovchi ning Sherali aka algoritmi public class SheraliAka : IHaydovchi { public double YukJunatishNarxi(Yuk yuk) { Console.WriteLine("Ukam, iloji boricha vaqtida yetqazib oborib beraman, xavotir oma.") //murakkab mantiqlar ham shu yerda qo'shilishi mumkin return 12000; } } //IHaydovchi ning Turdiali aka algoritmi public class TurdaliAka : IHaydovchi { public double YukJunatishNarxi(Yuk yuk) { Console.WriteLine("Ukajon, mani bilasizu, birinchi bo'lib sizi yukiz manziliga yetadi.") //murakkab mantiqlar ham shu yerda qo'shilishi mumkin return 13000; } }
YukJunatishHaydovchiService
class.
// Staregy dagi Contextga mos class. public class YukJunatishHaydovchiService { //Programming to interface(P2I) prinsipiga binoan //ushbu class aynan qaysi algoritm ishlatilayotganlidan behabar //faqat chaqiradi holos. private readonly IHaydovchi _haydovchi; //IHayovchidan meros olingan classning //obyektida shu yerda keladi, lekin bu class uchun //bularning IHaydovchi public YukJunatishHaydovchiService(IHaydovchi haydovchi) { _haydovchi = haydovchi; } //Ihaydovchi darajasidaga metodni chaqirish orqali //konkret algoritm ichidagi mantiqni ishlatadi public double YukJunatishNarxi(Yuk yuk) { return _haydovchi.YukJunatishNarxi(yuk); } }
Strategy DP uchun test.
static void StrategyPatternTest() { //yukni yaratamiz StrategyPattern.GOF.Yuk yuk = new StrategyPattern.GOF.Yuk(); yuk.Nomi = "Iphone 5"; yuk.Manzil = "Manzil"; //Alisher aka orqali jo'natmoqchimiz AlisherAka alisherAka = new AlisherAka(); YukJunatishHaydovchiService yukJunatishHaydovchiService = new YukJunatishHaydovchiService(alisherAka); //servisdan foydalanib narxni hisoblaymiz double narx = yukJunatishHaydovchiService.YukJunatishNarxi(yuk); Console.WriteLine(narx); }
Nimalar o’zgardi?
YukJunatishHaydovchiService
konkret algoritm haqida hechnima bilmaydi, u faqatIHaydovchi
ni ishlatyapman deb o’ylaydi. Ya’ni, P2I amalda qo’llanildi.- Yangi haydovchi qo’shish uchun
YukJunatishHaydovchiService
o’zgarmaydi, ya’ni Open/Closed prinsip qoidalari buzilmagan. - Har bir algoritmni mustaqil tarzda test qilib olishimiz mumkin.
- Coupling darajasi pasaydi.
- Alohida algoritmlarni alohida dasturchilarga yuklash mumkin, ular bir-biriga halaqit qilmaydi.
To’liq kod bilan link orqali tanishishingiz mumkin.
Manba:
Algoritm
Strategy design patterni