JavaRush /Java blogi /Random-UZ /Dizayn namunasi "Strategiya"

Dizayn namunasi "Strategiya"

Guruhda nashr etilgan
Salom! Oldingi ma'ruzalarda biz allaqachon "dizayn namunasi" kabi tushunchaga duch kelganmiz. Agar unutgan bo'lsangiz, sizga eslatib o'tamiz: bu atama dasturlashda umumiy muammoning ma'lum bir standart echimini bildiradi. "Strategiya" dizayn namunasi - 1JavaRush-da biz ko'pincha deyarli har qanday savolga javobni Google orqali topish mumkinligini aytamiz. Shuning uchun, kimdir siznikiga o'xshash muammoni allaqachon muvaffaqiyatli hal qilgan bo'lishi mumkin. Shunday qilib, naqshlar vaqt sinovidan o'tgan va amaliyotda sinovdan o'tgan eng keng tarqalgan muammolarni hal qilish yoki muammoli vaziyatlarni hal qilish usullaridir. Bu juda "velosipedlar" bo'lib, hech qanday holatda o'zingizni ixtiro qilishingiz shart emas, lekin siz ularni qanday va qachon qo'llashni bilishingiz kerak :) Naqshlarning yana bir vazifasi arxitekturani yagona standartga etkazishdir. Birovning kodini o'qish oson ish emas! Har kim buni har xil yozadi, chunki bir xil muammoni ko'p jihatdan hal qilish mumkin. Ammo naqshlardan foydalanish turli dasturchilarga kodning har bir satrini chuqur o'rganmasdan dastur mantig'ini tushunishga imkon beradi (hatto ular buni birinchi marta ko'rsalar ham!) Bugun biz "Strategiya" deb nomlangan eng keng tarqalgan naqshlardan birini ko'rib chiqamiz. "Strategiya" dizayn namunasi - 2Tasavvur qilaylik, biz Car obyekti bilan faol ishlaydigan dastur yozyapmiz. Bunday holda, bizning dasturimiz nima qilayotgani unchalik muhim emas. Buning uchun biz bir ota-ona sinfi Autova uchta bola sinfi bilan meros tizimini yaratdik: Sedan, Truckva F1Car.
public class Auto {

   public void gas() {
       System.out.println("Едем вперед");
   }

   public void stop() {

       System.out.println("Тормозим!");
   }
}

public class Sedan extends Auto {
}

public class Truck extends Auto {
}

public class F1Car extends Auto {
}
Barcha uchta bola sinfi ota-onadan ikkita standart usulni meros qilib oladi - gas()va stop() bizning dasturimiz juda oddiy: mashinalar faqat oldinga harakat qilishlari va tormozlashlari mumkin. Ishimizni davom ettirib, biz mashinalarga yangi usulni qo'shishga qaror qildik - fill()(yoqilg'i quyish). Keling, uni ota-ona sinfiga qo'shamiz Auto:
public class Auto {

   public void gas() {
       System.out.println("Едем вперед");
   }

   public void stop() {

       System.out.println("Тормозим!");
   }

   public void fill() {
       System.out.println("Заправить бензин!");
   }
}
Bunday oddiy vaziyatda muammolar paydo bo'lishi mumkindek tuyuladimi? Axir, ular allaqachon paydo bo'lgan ... Dizayn namunasi "Strategiya" - 3
public class ChildrenBuggies extends Auto {

   public void fill() {

       //хм... Это детский багги, его не надо заправлять :/
   }
}
Bizning dasturimizda umumiy tushunchaga to'g'ri kelmaydigan avtomobil - bolalar aravachasi paydo bo'ldi. Bu pedal yoki radio orqali boshqariladigan bo'lishi mumkin, lekin bir narsa aniq - unga benzin quyadigan joy yo'q. Bizning meros sxemamiz bizga umumiy usullarni hatto ularga kerak bo'lmagan sinflarga ham berishimizga olib keldi. Bunday vaziyatda nima qilishimiz kerak? Masalan, siz fill()sinfdagi usulni bekor qilishingiz mumkin ChildrenBuggies, shunda siz aravachaga yonilg'i quymoqchi bo'lganingizda, hech narsa sodir bo'lmaydi:
public class ChildrenBuggies extends Auto {

   @Override
   public void fill() {
       System.out.println("Игрушечную машину нельзя заправить!");
   }
}
Ammo bu yechimni hech bo'lmaganda kodning takrorlanishi tufayli muvaffaqiyatli deb atash qiyin. Misol uchun, ko'pchilik sinflar ota-klassning usulidan foydalanadi, ammo boshqa sinflar uni bekor qilishga majbur bo'ladi. Agar bizda 15 ta sinf mavjud bo'lsa va 5-6 sinfda biz xatti-harakatlarni bekor qilishga majbur bo'lsak, kodning takrorlanishi ancha kengayadi. Ehtimol, interfeyslar bizga yordam berishi mumkinmi? Masalan, bu:
public interface Fillable {

   public void fill();
}
FillableBiz bir usul bilan interfeys yaratamiz fill(). Shunga ko'ra, yonilg'i quyish kerak bo'lgan mashinalar ushbu interfeysni amalga oshiradilar, ammo boshqa mashinalar (masalan, bizning aravachamiz) buni amalga oshirmaydi. Ammo bu variant bizga ham mos kelmaydi. Bizning sinf ierarxiyasi kelajakda juda katta raqamga o'sishi mumkin (dunyoda qancha turdagi mashinalar borligini tasavvur qiling). Biz oldingi meros opsiyasidan voz kechdik, chunki ni bekor qilishni xohlamadik fill(). Bu erda biz uni har bir sinfda amalga oshirishimiz kerak! Agar bizda ulardan 50 tasi bo'lsa-chi? Va agar bizning dasturimizga tez-tez o'zgartirishlar kiritilsa (va haqiqiy dasturlarda bu deyarli har doim sodir bo'ladi!), Biz 50 ta sinf orasida tilimiz bilan yugurishimiz va ularning har birining xatti-harakatlarini qo'lda o'zgartirishimiz kerak. Xo'sh, oxirida nima qilishimiz kerak? Muammoni hal qilish uchun keling, boshqa yo'lni tanlaylik. Ya'ni, sinfimizning xatti-harakatlarini sinfning o'zidan ajratamiz. Bu nima degani? Ma'lumki, har qanday ob'ektning holati (ma'lumotlar to'plami) va xatti-harakati (usullar to'plami) mavjud. Mashina sinfimizning xatti-harakati uchta usuldan iborat - gas(), stop()va fill(). Birinchi ikkita usul yaxshi. Lekin biz uchinchi usulni sinfdan tashqariga o'tkazamiz Auto. Bu xatti-harakatni sinfdan ajratish bo'ladi (aniqrog'i, biz xatti-harakatlarning faqat bir qismini ajratamiz - birinchi ikkita usul o'z joyida qoladi). Usulimizni qayerga ko'chirishimiz kerak fill()? Darhol hech narsa xayolga kelmaydi :/ U butunlay o'z o'rnida edi. Biz uni alohida interfeysga o'tkazamiz - FillStrategy!
public interface FillStrategy {

   public void fill();
}
Nima uchun bizga bu interfeys kerak? Hammasi oddiy. Endi biz ushbu interfeysni amalga oshiradigan bir nechta sinflarni yaratishimiz mumkin:
public class HybridFillStrategy implements FillStrategy {

   @Override
   public void fill() {
       System.out.println("Заправляем бензином or электричеством на выбор!");
   }
}

public class F1PitstopStrategy implements FillStrategy {

   @Override
   public void fill() {
       System.out.println("Заправляем бензин только после всех остальных procedures пит-стопа!");
   }
}

public class StandartFillStrategy implements FillStrategy {
   @Override
   public void fill() {
       System.out.println("Просто заправляем бензин!");
   }
}
Biz uchta xatti-harakatlar strategiyasini yaratdik - oddiy avtomobillar, gibridlar va Formula 1 avtomobillari uchun. Har bir strategiya alohida yonilg'i quyish algoritmini amalga oshiradi. Bizning holatda, bu faqat konsolga chiqariladi, lekin usul ichida ba'zi murakkab mantiq bo'lishi mumkin. Bu bilan keyin nima qilishimiz kerak?
public class Auto {

   FillStrategy fillStrategy;

   public void fill() {
       fillStrategy.fill();
   }

   public void gas() {
       System.out.println("Едем вперед");
   }

   public void stop() {
       System.out.println("Тормозим!");
   }

}
Biz interfeysimizdan FillStrategyota-ona sinfidagi maydon sifatida foydalanamiz Auto. Iltimos, diqqat qiling: biz aniq dasturni belgilamaymiz, aksincha interfeysdan foydalanamiz. FillStrategyVa bizga bolalar avtomobillari sinflarida interfeysning o'ziga xos ilovalari kerak bo'ladi :
public class F1Car extends Auto {

   public F1Car() {
       this.fillStrategy = new F1PitstopStrategy();
   }
}

public class HybridAuto extends Auto {

   public HybridAuto() {
       this.fillStrategy = new HybridFillStrategy();
   }
}

public class Sedan extends Auto {

   public Sedan() {
       this.fillStrategy = new StandartFillStrategy();
   }
}
Keling, nima borligini ko'rib chiqaylik:
public class Main {

   public static void main(String[] args) {

       Auto sedan = new Sedan();
       Auto hybrid = new HybridAuto();
       Auto f1car = new F1Car();

       sedan.fill();
       hybrid.fill();
       f1car.fill();
   }
}
Konsol chiqishi:

Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
Ajoyib, yonilg'i quyish jarayoni xuddi shunday ishlaydi! Aytgancha, strategiyani konstruktorda parametr sifatida ishlatishimizga hech narsa to'sqinlik qilmaydi! Masalan, bu kabi:
public class Auto {

   private FillStrategy fillStrategy;

   public Auto(FillStrategy fillStrategy) {
       this.fillStrategy = fillStrategy;
   }

   public void fill() {
       this.fillStrategy.fill();
   }

   public void gas() {
       System.out.println("Едем вперед");
   }

   public void stop() {
       System.out.println("Тормозим!");
   }
}

public class Sedan extends Auto {

   public Sedan() {
       super(new StandartFillStrategy());
   }
}



public class HybridAuto extends Auto {

   public HybridAuto() {
       super(new HybridFillStrategy());
   }
}

public class F1Car extends Auto {

   public F1Car() {
       super(new F1PitstopStrategy());
   }
}
Keling, usulimizni ishga tushiramiz main()(u o'zgarishsiz qoladi) va bir xil natijaga erishamiz! Konsol chiqishi:

Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
Strategiya namunasi algoritmlar oilasini belgilaydi, ularning har birini qamrab oladi va ularning bir-birini almashtirishini ta'minlaydi. Bu sizga algoritmlarni mijoz tomonidan ishlatilishidan qat'iy nazar o'zgartirishga imkon beradi (bu ta'rif "Dizayn naqshlarini o'rganish" kitobidan olingan va menga juda muvaffaqiyatli tuyuladi). Dizayn namunasi "Strategiya" - 4Bizni qiziqtiradigan algoritmlar oilasini (yoqilg'i quyish mashinalari turlari) bir nechta ilovalar bilan alohida interfeyslarga ajratdik. Biz ularni avtomobilning mohiyatidan ajratib oldik. Shuning uchun, endi, agar biz u yoki bu yonilg'i quyish jarayoniga biron bir o'zgartirish kiritishimiz kerak bo'lsa, bu bizning avtomobillar sinfimizga hech qanday ta'sir qilmaydi. O'zaro almashish imkoniyatiga kelsak, bunga erishish uchun sinfimizga faqat bitta sozlash usulini qo'shishimiz kerak Auto:
public class Auto {

   FillStrategy fillStrategy;

   public void fill() {
       fillStrategy.fill();
   }

   public void gas() {
       System.out.println("Едем вперед");
   }

   public void stop() {
       System.out.println("Тормозим!");
   }

   public void setFillStrategy(FillStrategy fillStrategy) {
       this.fillStrategy = fillStrategy;
   }
}
Endi biz strategiyalarni tezda o'zgartirishimiz mumkin:
public class Main {

   public static void main(String[] args) {

       ChildrenBuggies buggies = new ChildrenBuggies();
       buggies.setFillStrategy(new StandartFillStrategy());

       buggies.fill();
   }
}
Agar to'satdan bolalarning aravachalari benzin bilan to'ldirila boshlasa, bizning dasturimiz bunday stsenariyga tayyor bo'ladi :) Hammasi, aslida! Siz yana bir dizayn namunasini o'rgandingiz, bu sizga shubhasiz kerak bo'ladi va haqiqiy loyihalar ustida ishlashda sizga bir necha marta yordam beradi :) Yana ko'rishguncha!
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION