Stratēģijas modelis paskaidrots, izmantojot Java

Šajā amatā es runāšu par vienu no populārākajiem dizaina modeļiem - stratēģijas modeli. Ja jūs vēl nezināt, dizaina modeļi ir virkne uz objektu orientētu programmēšanas principu, ko programmatūras nozarē izveidojuši ievērojami vārdi, kurus bieži dēvē par četru bandu (GoF of Four). Šie dizaina modeļi ir ļoti ietekmējuši programmatūras ekosistēmu, un līdz šim tie tiek izmantoti, lai atrisinātu kopīgās problēmas, ar kurām saskaras uz objektorientētu programmēšanu.

Formāli definēsim stratēģijas modeli:

Stratēģijas modelis nosaka algoritmu saimi, aptver katru no tiem un padara tos savstarpēji aizvietojamus. Stratēģija ļauj algoritmam atšķirties neatkarīgi no klientiem, kuri to izmanto

Labi ar to no ceļa, ienirsim kādā kodā, lai saprastu, ko šie vārdi PATIESĪBĀ nozīmē. Mēs paņemsim piemēru ar iespējamo slazdu un pēc tam izmantosim stratēģijas modeli, lai redzētu, kā tas pārvar problēmu.

Es jums parādīšu, kā izveidot suņu simulatora programmu, lai uzzinātu stratēģijas modeli. Lūk, kā izskatīsies mūsu nodarbības: “Suns” superklase ar kopēju uzvedību un pēc tam konkrētas suņu klases, kas izveidotas, apakšklasējot suņu klasi.

Lūk, kā izskatās kods

public abstract class Dog { public abstract void display(); //different dogs have different looks! public void eat(){} public void bark(){} // Other dog-like methods ... }

Displeja () metode ir abstrakta, jo dažādiem suņiem ir atšķirīgs izskats. Visas pārējās apakšklases pārņems ēšanas un mizas uzvedību vai to ignorēs, īstenojot tās pašas. Tik tālu, labi!

Ko darīt, ja jūs vēlaties pievienot jaunu uzvedību? Pieņemsim, ka jums vajag foršu robotu suni, kurš var veikt visādus trikus. Nav problēma, mums vienkārši jāpievieno metode SuTricks () mūsu suņu superklasē, un mums ir labi iet.

Bet pagaidiet minūti ... Robots suns nedrīkstētu ēst pareizi? Nedzīvi priekšmeti, protams, nevar ēst. Labi, kā tad mēs varam atrisināt šo problēmu? Nu, mēs varam ignorēt metodi eat (), lai nedarītu neko, un tā darbojas lieliski!

public class RobotDog extends Dog { @override public void eat(){} // Do nothing }

Labi izdarīts! Tagad robotu suņi nevar ēst, viņi var tikai mizu vai trikus. Kā ir ar gumijas suņiem? Viņi nevar ne ēst, ne trikus. Un koka suņi nevar ēst, mizu un trikus. Mēs ne vienmēr varam ignorēt metodes, lai nedarītu neko, tas nav tīrs, un tas vienkārši jūtas nederīgs. Iedomājieties, ka to darāt projektā, kura projekta specifikācija mainās ik pēc pāris mēnešiem. Mūsējie ir tikai naivs piemērs, bet jums ir ideja. Tātad, mums jāatrod tīrāks veids, kā atrisināt šo problēmu.

Vai interfeiss var atrisināt mūsu problēmu?

Kā ar saskarnēm? Apskatīsim, vai viņi var atrisināt mūsu problēmu. Labi, tāpēc mēs izveidojam CanEat un CanBark saskarni:

interface CanEat { public void eat(); } interface CanBark { public void bark(); }

Tagad mēs esam noņēmuši mizas () un ēst () metodes no Suņu superklases un pievienojuši tās attiecīgajām saskarnēm. Tā ka CanBark saskarni ieviesīs tikai suņi, kuri var mizot, un suņi, kuri var ēst, - CanEat saskarni. Tagad, vairs neuztraucoties par to, ka suņi manto uzvedību, kurai viņiem nevajadzētu, mūsu problēma ir atrisināta ... vai arī tā ir?

Kas notiek, ja mums ir jāmaina suņu ēšanas uzvedība? Pieņemsim, ka turpmāk katram sunim ēdienreizē jāiekļauj zināms daudzums olbaltumvielu. Tagad jums ir jāpārveido visas suņu apakšklases eat () metode. Ko darīt, ja ir 50 šādas klases, ak šausmas!

Tātad saskarnes tikai daļēji atrisina mūsu problēmu, ka Suņi dara tikai to, ko viņi spēj, bet tie vispār rada citu problēmu. Saskarnēm nav neviena ieviešanas koda, tāpēc ir nulle koda atkārtotas izmantošanas un iespējama daudz dublētu kodu. Kā mēs to atrisinām, jūs uzdodat? Stratēģijas modelis nāk palīgā!

Stratēģijas modelis

Tāpēc mēs to darīsim soli pa solim. Pirms turpinām, ļaujiet man jūs iepazīstināt ar dizaina principu:

Nosakiet savas programmas daļas, kas atšķiras, un atdaliet tās no tām, kas paliek nemainīgas.

Faktiski tas ir ļoti vienkārši - princips nosaka nošķirt un “iekapsulēt” visu, kas bieži mainās, lai viss mainīgais kods dzīvotu vienā vietā. Tādā veidā kods, kas mainās, neietekmēs pārējo programmu, un mūsu lietojumprogramma ir elastīgāka un izturīgāka.

Mūsu gadījumā “mizu” un “ēst” uzvedību var izņemt no Suņu klases un iekapsulēt citur. Mēs zinām, ka šī suņu uzvedība dažādos suņos atšķiras, un viņiem jāiegūst sava atsevišķa klase.

Mēs izveidosim divus klašu komplektus, izņemot suņu klasi, vienu ēšanas paradumu noteikšanai un otru riešanas uzvedību. Mēs izmantosim saskarnes, lai attēlotu tādu uzvedību kā “EatBehavior” un “BarkBehavior”, un konkrētās uzvedības klase ieviesīs šīs saskarnes. Tātad suņu klase vairs neievieš saskarni. Mēs veidojam atsevišķas klases, kuru vienīgais uzdevums ir pārstāvēt konkrēto uzvedību!

Šādi izskatās saskarne EatBehavior

interface EatBehavior { public void eat(); }

Un BarkBehavior

interface BarkBehavior { public void bark(); }

Visas klases, kas pārstāv šo uzvedību, ieviesīs attiecīgo saskarni.

BarkBehavior betona klases

public class PlayfulBark implements BarkBehavior { @override public void bark(){ System.out.println("Bark! Bark!"); } } public class Growl implements BarkBehavior { @override public void bark(){ System.out.println("This is a growl"); } public class MuteBark implements BarkBehavior { @override public void bark(){ System.out.println("This is a mute bark"); }

Betona nodarbības EatBehavior

public class NormalDiet implements EatBehavior { @override public void eat(){ System.out.println("This is a normal diet"); } } public class ProteinDiet implements EatBehavior { @override public void eat(){ System.out.println("This is a protein diet"); } }

Tagad, kamēr mēs veicam konkrētus pasākumus, apakšklasē “Suns”, superklase, protams, mēs vēlamies, lai mēs spētu dinamiski piešķirt uzvedību suņu gadījumiem. Galu galā problēmu izraisīja iepriekšējā koda neelastība. Suņu apakšklasē mēs varam noteikt seteru metodes, kas ļaus mums izpildes laikā iestatīt atšķirīgu uzvedību.

Tas mūs noved pie cita dizaina principa:

Programma interfeisam, nevis ieviešana.

Tas nozīmē, ka tā vietā, lai izmantotu konkrētās klases, mēs izmantojam mainīgos, kas ir šo klašu virstipi. Citiem vārdiem sakot, mēs izmantojam EatBehavior un BarkBehavior tipa mainīgos un piešķiram šiem mainīgajiem objektu klases, kas īsteno šo uzvedību. Tādā veidā suņu klasēm nav nepieciešama informācija par šo mainīgo faktiskajiem objektu tipiem!

Lai padarītu jēdzienu skaidru, šeit ir piemērs, kas atšķir abus veidus. Apsveriet abstraktu dzīvnieku klasi, kurai ir divi konkrēti īstenojumi - suns un kaķis.

Programmas ieviešana būtu:

Dog d = new Dog(); d.bark();

Lūk, kā izskatās interfeisa programmēšana:

Animal animal = new Dog(); animal.animalSound();

Šeit mēs zinām, ka dzīvniekā ir “suns”, bet šo atsauci varam izmantot polimorfiski visur citur mūsu kodā. Viss, kas mums rūp, ir tas, ka dzīvnieku eksemplārs spēj reaģēt uz animalSound () metodi un tiek izsaukta atbilstošā metode atkarībā no piešķirtā objekta.

Tas bija daudz jāuzņemas. Bez sīkākiem paskaidrojumiem redzēsim, kā tagad izskatās mūsu “Suns” superklase:

public abstract class Dog { EatBehavior eatBehavior; BarkBehaviour barkBehavior; public Dog(){} public void doBark() { barkBehavior.bark(); } public void doEat() { eatBehavior.eat(); } }

Pievērsiet īpašu uzmanību šīs klases metodēm. Suņu klase tagad “deleģē” ēst un riešanu, nevis īsteno pati vai pārmanto to (apakšklase). Metodā doBark () mēs vienkārši izsaucam metodi miza () objektam, uz kuru atsaucas barkBehavior. Tagad mums ir vienalga par objekta faktisko tipu, mums ir svarīgi tikai tas, vai tas prot riet!

Tagad patiesības brīdis, izveidosim konkrētu Suni!

public class Labrador extends Dog { public Labrador(){ barkBehavior = new PlayfulBark(); eatBehavior = new NormalDiet(); } public void display(){ System.out.println("I'm a playful Labrador"); } ... }

Kas notiek Labradoras klases konstruktorā? mēs piešķiram konkrētus gadījumus supertipam (atcerieties, ka saskarnes veidi tiek mantoti no suņu superklases). Kad mēs Labradoras instancē izsaucam doEat (), atbildība tiek nodota ProteinDiet klasei, un tā izpilda metodi eat ().

Stratēģijas modelis darbībā

Labi, redzēsim to darbībā. Ir pienācis laiks palaist mūsu dopinga suņu simulatora programmu!

public class DogSimulatorApp { public static void main(String[] args) { Dog lab = new Labrador(); lab.doEat(); // Prints "This is a normal diet" lab.doBark(); // "Bark! Bark!" } }

Kā mēs varam uzlabot šo programmu? Pievienojot elastību! Pievienosim seteru metodes Suņu klasei, lai varētu izpildes laikā mainīt uzvedību. Pievienosim vēl divas metodes suņu superklasei:

public void setEatBehavior(EatBehavior eb){ eatBehavior = eb; } public void setBarkBehavior(BarkBehavior bb){ barkBehavior = bb; }

Tagad mēs varam modificēt savu programmu un izvēlēties jebkuru darbību, kas mums patīk izpildlaikā!

public class DogSimulatorApp { public static void main(String[] args){ Dog lab = new Labrador(); lab.doEat(); // This is a normal diet lab.setEatBehavior(new ProteinDiet()); lab.doEat(); // This is a protein diet lab.doBark(); // Bark! Bark! } }

Apskatīsim kopainu:

Mums ir suņu superklase un “Labrador” klase, kas ir suņu apakšklase. Tad mums ir algoritmu saime (uzvedība) “iekapsulēta” ar attiecīgajiem uzvedības veidiem.

Apskatiet formālo definīciju, kuru es sniedzu sākumā: algoritmi nav nekas cits kā uzvedības saskarnes. Tagad tos var izmantot ne tikai šajā programmā, bet arī citas programmas to var izmantot. Ievērojiet attiecības starp klasēm diagrammā. IS-A un HAS-A sakarības var secināt no diagrammas.

Tieši tā! Es ceru, ka esat guvis plašu pārskatu par stratēģijas modeli. Stratēģijas modelis ir ārkārtīgi noderīgs, ja jūsu lietotnē ir noteikta uzvedība, kas pastāvīgi mainās.

Tas mūs noved pie Java ieviešanas beigām. Liels paldies, ka līdz šim pieturējies pie manis! Ja jūs interesē uzzināt par Kotlin versiju, sekojiet līdzi nākamajai ziņai. Es runāju par interesantām valodas funkcijām un to, kā mēs varam samazināt visu iepriekš minēto kodu vienā Kotlin failā :)

PS

Esmu izlasījis grāmatu “Pirmie dizaina modeļi”, un lielāko daļu šī ieraksta iedvesmo tās saturs. Es ļoti ieteiktu šo grāmatu visiem, kas meklē maigu ievadu dizaina modeļos.