JavaRush /Java Blogu /Random-AZ /Dizayn nümunəsi "Strategiya"

Dizayn nümunəsi "Strategiya"

Qrupda dərc edilmişdir
Salam! Əvvəlki mühazirələrdə biz artıq “dizayn nümunəsi” kimi bir anlayışla qarşılaşmışıq. Əgər unutmusunuzsa, sizə xatırladaq: bu termin proqramlaşdırmada ümumi problemin müəyyən standart həllini ifadə edir. Dizayn nümunəsi "Strategiya" - 1JavaRush-da biz tez-tez deyirik ki, demək olar ki, istənilən sualın cavabı Google-da ola bilər. Buna görə də, yəqin ki, kimsə sizin probleminizə bənzər bir problemi uğurla həll etdi. Beləliklə, nümunələr ən çox yayılmış problemlər və ya problemli vəziyyətlərin həlli üsulları üçün zamanla sınaqdan keçirilmiş və təcrübədə sınaqdan keçirilmiş həllərdir. Bunlar elə “velosipedlər”dir ki, heç bir halda özünüzü icad etməməlisiniz, ancaq onları necə və nə vaxt tətbiq edəcəyinizi bilməlisiniz :) Naxışların digər vəzifəsi memarlığı vahid standarta çatdırmaqdır. Başqasının kodunu oxumaq asan məsələ deyil! Hər kəs bunu fərqli yazır, çünki eyni problem bir çox cəhətdən həll edilə bilər. Lakin nümunələrin istifadəsi müxtəlif proqramçılara hər bir kod sətirinə (ilk dəfə baxsalar belə!) dərinləşmədən proqramın məntiqini başa düşməyə imkan verir. Dizayn nümunəsi "Strategiya" - 2Təsəvvür edək ki, biz Car obyekti ilə aktiv işləyən proqram yazırıq. Bu halda, proqramımızın tam olaraq nə etdiyi o qədər də vacib deyil. Bunun üçün biz bir valideyn sinfi Autovə üç uşaq sinfi olan miras sistemi yaratdıq: Sedan, TruckF1Car.
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 {
}
Hər üç uşaq sinfi valideyndən iki standart metodu miras alır - gas()stop() proqramımız çox sadədir: avtomobillər yalnız irəli sürə və əyləc edə bilir. İşimizi davam etdirərək, avtomobillərə yeni bir üsul əlavə etmək qərarına gəldik - fill()(yanacaq doldurma). Gəlin onu ana sinifə əlavə edək Auto:
public class Auto {

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

   public void stop() {

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

   public void fill() {
       System.out.println("Заправить бензин!");
   }
}
Belə görünür ki, belə sadə bir vəziyyətdə problemlər yarana bilər? Əslində, onlar artıq yaranıblar... Dizayn nümunəsi "Strategiya" - 3
public class ChildrenBuggies extends Auto {

   public void fill() {

       //хм... Это детский багги, его не надо заправлять :/
   }
}
Proqramımızda ümumi konsepsiyaya uyğun gəlməyən avtomobil - uşaq arabası peyda oldu. O, pedalla və ya radio ilə idarə olunan ola bilər, amma bir şey dəqiqdir - ona benzin tökmək üçün heç bir yer yoxdur. Bizim miras sxemimiz ümumi metodları hətta onlara ehtiyacı olmayan siniflərə də verməmizlə nəticələndi. Belə bir vəziyyətdə nə etməliyik? Məsələn, fill()sinifdə metodu ləğv edə bilərsiniz ChildrenBuggieski, arabaya yanacaq doldurmağa çalışdığınız zaman heç nə baş verməsin:
public class ChildrenBuggies extends Auto {

   @Override
   public void fill() {
       System.out.println("Игрушечную машину нельзя заправить!");
   }
}
Ancaq bu həlli çətin ki, uğurlu adlandırmaq olar, heç olmasa kodun təkrarlanması səbəbindən. Məsələn, əksər siniflər ana sinifdən olan metoddan istifadə edəcək, lakin digər siniflər onu ləğv etmək məcburiyyətində qalacaqlar. 15 sinifimiz varsa və 5-6-da davranışı ləğv etmək məcburiyyətində qalırıqsa, kodun təkrarlanması kifayət qədər geniş olacaq. Bəlkə interfeyslər bizə kömək edə bilər? Məsələn, bu:
public interface Fillable {

   public void fill();
}
FillableBir üsulla interfeys yaradacağıq fill(). Müvafiq olaraq, yanacaq doldurulması lazım olan avtomobillər bu interfeysi həyata keçirəcək, lakin digər avtomobillər (məsələn, arabamız) tətbiq etməyəcək. Amma bu seçim bizə də yaraşmayacaq. Sinif iyerarxiyamız gələcəkdə çox böyük rəqəmə çata bilər (dünyada nə qədər müxtəlif növ avtomobillərin olduğunu təsəvvür edin). Əvvəlki miras seçimini ləğv etmək istəmədiyimiz üçün imtina etdik fill(). Burada biz bunu hər sinifdə tətbiq etməli olacağıq! Bəs onlardan 50-si bizdə olsa? Proqramımızda tez-tez dəyişikliklər edilərsə (və real proqramlarda bu, demək olar ki, həmişə baş verəcəkdir!), Biz bütün 50 sinif arasında dilimizlə qaçmalı və hər birinin davranışını əl ilə dəyişdirməli olacağıq. Beləliklə, sonda nə etməliyik? Problemimizi həll etmək üçün fərqli bir yol seçək. Məhz, gəlin sinifimizin davranışını sinfin özündən ayıraq. Bunun mənası nədi? Bildiyiniz kimi, hər hansı bir obyektin vəziyyəti (məlumatlar toplusu) və davranışı (metodlar toplusu) var. Maşın sinifimizin davranışı üç üsuldan ibarətdir - gas(), stop()fill(). İlk iki üsul yaxşıdır. Amma üçüncü üsulu sinifdən kənara daşıyacağıq Auto. Bu, davranışın sinifdən ayrılması olacaq (daha doğrusu, davranışın yalnız bir hissəsini ayırırıq - ilk iki üsul yerində qalır). Metodumuzu hara köçürməliyik fill()? Dərhal ağlına heç nə gəlmir :/ O, sanki tam yerində idi. Biz onu ayrı bir interfeysə keçirəcəyik - FillStrategy!
public interface FillStrategy {

   public void fill();
}
Bu interfeys bizə nə üçün lazımdır? Bu sadədir. İndi bu interfeysi həyata keçirəcək bir neçə sinif yarada bilərik:
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 üç davranış strategiyası yaratmışıq - adi avtomobillər, hibridlər və Formula 1 avtomobilləri üçün. Hər bir strategiya ayrıca yanacaq doldurma alqoritmini həyata keçirir. Bizim vəziyyətimizdə bu, yalnız konsola çıxışdır, lakin metodun içərisində bəzi mürəkkəb məntiq ola bilər. Bundan sonra bununla nə etməliyik?
public class Auto {

   FillStrategy fillStrategy;

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

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

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

}
İnterfeysimizdən FillStrategyana sinifdə sahə kimi istifadə edirik Auto. Diqqət yetirin: biz konkret tətbiqi göstərmirik, əksinə interfeysdən istifadə edirik. FillStrategyUşaq avtomobili siniflərində interfeysin xüsusi tətbiqlərinə ehtiyacımız olacaq :
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();
   }
}
Nə əldə etdiyimizə baxaq:
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 çıxışı:

Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
Əla, yanacaq doldurma prosesi lazım olduğu kimi işləyir! Yeri gəlmişkən, strategiyanı konstruktorda parametr kimi istifadə etməyə heç nə mane olmur! Məsələn, bu kimi:
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());
   }
}
Gəlin metodumuzu işə salaq main()(dəyişmədən qalır) və eyni nəticəni əldə edək! Konsol çıxışı:

Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
Strategiya nümunəsi alqoritmlər ailəsini müəyyən edir, onların hər birini əhatə edir və onların bir-birini əvəz edə bilməsini təmin edir. Müştəri tərəfində istifadəsindən asılı olmayaraq alqoritmləri dəyişdirməyə imkan verir (bu tərif “Dizayn Nümunələrinin Tədqiqi” kitabından götürülüb və mənə çox uğurlu görünür). Dizayn nümunəsi "Strategiya" - 4Bizi maraqlandıran alqoritmlər ailəsini (yanacaq doldurma avtomobillərinin növləri) bir neçə tətbiqetmə ilə ayrıca interfeyslərə ayırdıq. Biz onları avtomobilin mahiyyətindən ayırdıq. Ona görə də indi bu və ya digər yanacaqdoldurma prosesində hər hansı dəyişiklik etmək lazım gələrsə, bu, bizim avtomobil siniflərimizə heç bir şəkildə təsir etməyəcək. Bir-birini əvəz etməyə gəlincə, buna nail olmaq üçün sinfimizə sadəcə bir təyinedici metod əlavə etməliyik 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;
   }
}
İndi strategiyaları tez dəyişə bilərik:
public class Main {

   public static void main(String[] args) {

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

       buggies.fill();
   }
}
Birdən uşaq arabaları benzinlə doldurulmağa başlasa, proqramımız belə bir ssenariyə hazır olacaq :) Hamısı budur, əslində! Siz daha bir dizayn nümunəsi öyrəndiniz, şübhəsiz ki, sizə lazım olacaq və real layihələr üzərində işləyərkən sizə bir neçə dəfə kömək edəcək :) Yenidən görüşənədək!
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION