JavaRush /Java блогы /Random-KK /«Стратегия» дизайн үлгісі

«Стратегия» дизайн үлгісі

Топта жарияланған
Сәлеметсіз бе! Бұрынғы дәрістерде біз «дизайн үлгісі» сияқты ұғымды кездестірдік. Егер сіз ұмытып қалсаңыз, еске сала кетейік: бұл термин бағдарламалаудағы жалпы мәселенің белгілі бір стандартты шешімін білдіреді. «Стратегия» дизайн үлгісі - 1JavaRush-те біз кез келген сұрақтың жауабын Google іздеуге болатынын жиі айтамыз. Сондықтан, біреу сіздікі сияқты мәселені сәтті шешкен болуы мүмкін. Сонымен, үлгілер - бұл ең көп таралған мәселелерге немесе проблемалық жағдайларды шешудің әдістеріне уақыт пен тәжірибеде тексерілген шешімдер. Бұл «велосипедтер», олар ешбір жағдайда өзіңізді ойлап табудың қажеті жоқ, бірақ сіз оларды қалай және қашан қолдану керектігін білуіңіз керек :) Үлгілердің тағы бір міндеті - архитектураны бір стандартқа жеткізу. Басқа біреудің codeын оқу оңай жұмыс емес! Оны әркім әрқалай жазады, өйткені бір мәселені әр түрлі жолмен шешуге болады. Бірақ үлгілерді пайдалану әртүрлі бағдарламашыларға бағдарламаның логикасын codeтың әрбір жолын тереңдемей түсінуге мүмкіндік береді (тіпті олар оны бірінші рет көрсе де!) Бүгін біз «Стратегия» деп аталатын ең кең таралған үлгілердің бірін қарастырамыз. «Стратегия» дизайн үлгісі - 2Біз Car an objectісімен белсенді жұмыс істейтін бағдарлама жазып жатырмыз деп елестетейік. Бұл жағдайда біздің бағдарламаның нақты не істейтіні маңызды емес. Ол үшін біз бір ата-аналық сынып Autoжәне үш еншілес сыныптары бар мұрагерлік жүйені жасадық: Sedan, Truckжәне 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 {
}
Барлық үш бала класы ата-анадан екі стандартты әдісті мұра етеді - gas()және stop() біздің бағдарламамыз өте қарапайым: автомобильдер тек алға қарай жүріп, тежей алады. Жұмысымызды жалғастыра отырып, біз автомобильдерге жаңа әдісті қосуды шештік - fill()(жанармай құю). Оны ата-аналық сыныпқа қосамыз Auto:
public class Auto {

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

   public void stop() {

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

   public void fill() {
       System.out.println("Заправить бензин!");
   }
}
Осындай қарапайым жағдайда қиындықтар туындауы мүмкін сияқты ма? Шындығында, олар қазірдің өзінде пайда болды ... Дизайн үлгісі «Стратегия» - 3
public class ChildrenBuggies extends Auto {

   public void fill() {

       //хм... Это детский багги, его не надо заправлять :/
   }
}
Біздің бағдарламамызда жалпы ұғымға сәйкес келмейтін көлік пайда болды - балалар арбасы. Ол педальмен немесе радиомен басқарылатын болуы мүмкін, бірақ бір нәрсе анық - оған бензин құйатын жер жоқ. Біздің мұрагерлік схемамыз бізге жалпы әдістерді тіпті оларды қажет етпейтін сыныптарға беруге әкелді. Мұндай жағдайда не істеуіміз керек? Мысалы, сіз арбаға жанармай құюға тырысқанда ештеңе болмайтындай етіп fill()сыныптағы әдісті қайта анықтауға болады:ChildrenBuggies
public class ChildrenBuggies extends Auto {

   @Override
   public void fill() {
       System.out.println("Игрушечную машину нельзя заправить!");
   }
}
Бірақ бұл шешімді сәтті деп атауға болмайды, кем дегенде codeтың қайталануына байланысты. Мысалы, сыныптардың көпшілігі ата-аналық сыныптың әдісін пайдаланады, бірақ басқа сыныптар оны қайта анықтауға мәжбүр болады. Егер бізде 15 сынып болса және 5-6-да біз мінез-құлықты қайта анықтауға мәжбүр болсақ, codeтың қайталануы айтарлықтай кеңейеді. Мүмкін интерфейстер бізге көмектесе алады ма? Мысалы, мынау:
public interface Fillable {

   public void fill();
}
FillableБір әдіспен интерфейс жасаймыз fill(). Тиісінше, жанармай құюды қажет ететін көліктер бұл интерфейсті жүзеге асырады, бірақ басқа автомобильдер (мысалы, біздің арбалар) орындамайды. Бірақ бұл опция бізге де сәйкес келмейді. Біздің класс иерархиясы болашақта өте үлкен санға дейін өсуі мүмкін (әлемде қанша түрлі автомобильдер бар екенін елестетіп көріңіз). Біз алдыңғы мұрагерлік опциядан бас тарттық, себебі біз оны қайта анықтағымыз келмеді fill(). Мұнда біз оны әр сыныпта енгізуіміз керек! Егер бізде олардың 50-і болса ше? Егер бағдарламамызға жиі өзгерістер енгізілсе (және нақты бағдарламаларда бұл әрдайым дерлік болады!), біз барлық 50 сыныптың арасында тілімізбен жүгіріп, олардың әрқайсысының әрекетін қолмен өзгертуге тура келеді. Сонымен, соңында не істеуіміз керек? Мәселені шешу үшін басқа жолды таңдайық. Атап айтқанда, біздің сыныптың мінез-құлқын сыныптың өзінен бөліп алайық. Бұл нені білдіреді? Өздеріңіз білетіндей, кез келген an objectінің күйі (деректер жиынтығы) және мінез-құлқы (әдістер жиынтығы) болады. Біздің машина класының әрекеті үш әдістен тұрады - gas(), stop()және fill(). Алғашқы екі әдіс жақсы. Бірақ біз үшінші әдісті сыныптан тыс жылжытамыз Auto. Бұл мінез-құлықтың сыныптан бөлінуі болады (дәлірек айтқанда, біз мінез-құлықтың бір бөлігін ғана бөлеміз - алғашқы екі әдіс орнында қалады). Біз әдісімізді қайда жылжытуымыз керек fill()? Еске бірден ештеңе келмейді :/ Ол толығымен өз орнында болған сияқты. Біз оны бөлек интерфейске жылжытамыз - FillStrategy!
public interface FillStrategy {

   public void fill();
}
Бұл интерфейс бізге не үшін қажет? Бәрі оңай. Енді біз осы интерфейсті жүзеге асыратын бірнеше класс жасай аламыз:
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("Просто заправляем бензин!");
   }
}
Біз үш мінез-құлық стратегиясын жасадық - кәдімгі автомобильдер үшін, гибридтер үшін және Формула 1 автомобильдері үшін. Әрбір стратегия жеке жанармай құю алгоритмін жүзеге асырады. Біздің жағдайда бұл жай ғана консольге шығарылады, бірақ әдіс ішінде кейбір күрделі логика болуы мүмкін. Бұдан әрі мұнымен не істеуіміз керек?
public class Auto {

   FillStrategy fillStrategy;

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

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

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

}
Біз интерфейсімізді FillStrategyата-аналық сыныптағы өріс ретінде пайдаланамыз Auto. Назар аударыңыз: біз нақты іске асыруды көрсетпейміз, керісінше интерфейсті пайдаланамыз. FillStrategyБізге балалар көлігі сыныптарында интерфейсті нақты енгізу қажет болады :
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();
   }
}
Бізде не бар екенін көрейік:
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();
   }
}
Консоль шығысы:

Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
Керемет, жанармай құю процесі қажетінше жұмыс істейді! Айтпақшы, стратегияны конструкторда параметр ретінде пайдалануға ештеңе кедергі келтірмейді! Мысалы, келесідей:
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());
   }
}
Әдісімізді іске асырайық main()(ол өзгеріссіз қалады) және бірдей нәтижеге қол жеткізіңіз! Консоль шығысы:

Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
Стратегия үлгісі алгоритмдер тобын анықтайды, олардың әрқайсысын инкапсуляциялайды және олардың өзара алмастырылуын қамтамасыз етеді. Бұл алгоритмдерді тұтынушы жағында пайдалануына қарамастан өзгертуге мүмкіндік береді (бұл анықтама «Дизайн үлгілерін зерттеу» кітабынан алынған және маған өте сәтті болып көрінеді). Дизайн үлгісі «Стратегия» - 4Біз өзімізді қызықтыратын алгоритмдер тобын (жанармай құю машиналарының түрлері) бірнеше іске асырулары бар жеке интерфейстерге бөліп алдық. Біз оларды машинаның мәнінен ажыраттық. Сондықтан, қазір, егер бізге осы немесе басқа жанармай құю процесіне қандай да бір өзгерістер енгізу қажет болса, бұл біздің автокөліктер кластарына ешқандай әсер етпейді. Өзара алмастыруға келетін болсақ, оған қол жеткізу үшін біздің сыныпқа бір орнату әдісін қосу керек 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;
   }
}
Енді біз стратегияларды жылдам өзгерте аламыз:
public class Main {

   public static void main(String[] args) {

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

       buggies.fill();
   }
}
Егер кенеттен балалардың арбалы көліктеріне бензин құйыла бастаса, біздің бағдарлама мұндай сценарийге дайын болады :) Шын мәнінде, бәрі осы! Сіз тағы бір дизайн үлгісін үйрендіңіз, ол сізге сөзсіз қажет және нақты жобалармен жұмыс істегенде сізге бірнеше рет көмектеседі :) Тағы кездескенше!
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION