JavaRush /Java Blog /Random-TL /Pattern ng Disenyo "Diskarte"

Pattern ng Disenyo "Diskarte"

Nai-publish sa grupo
Kamusta! Sa mga nakaraang lektura, nakatagpo na natin ang konsepto ng "design pattern". Kung sakaling nakalimutan mo, ipaalala namin sa iyo: ang terminong ito ay tumutukoy sa isang tiyak na karaniwang solusyon sa isang karaniwang problema sa programming. Pattern ng disenyo "Diskarte" - 1Sa JavaRush madalas naming sinasabi na ang sagot sa halos anumang tanong ay maaaring i-Google. Samakatuwid, malamang na matagumpay na nalutas ng isang tao ang isang problemang katulad ng sa iyo. Kaya, ang mga pattern ay nasubok sa oras at nasubok na mga solusyon sa mga pinakakaraniwang problema o pamamaraan para sa paglutas ng mga sitwasyon ng problema. Ito ang mismong "mga bisikleta" na sa anumang kaso ay hindi mo kailangang mag-imbento ng iyong sarili, ngunit kailangan mong malaman kung paano at kailan ilapat ang mga ito :) Ang isa pang gawain ng mga pattern ay upang dalhin ang arkitektura sa isang solong pamantayan. Ang pagbabasa ng code ng ibang tao ay hindi isang madaling gawain! Ang bawat isa ay nagsusulat nito nang iba, dahil ang parehong problema ay maaaring malutas sa maraming paraan. Ngunit ang paggamit ng mga pattern ay nagbibigay-daan sa iba't ibang mga programmer na maunawaan ang lohika ng programa nang hindi sinisiyasat ang bawat linya ng code (kahit na nakita nila ito sa unang pagkakataon!) Ngayon ay titingnan natin ang isa sa mga pinakakaraniwang pattern na tinatawag na "Diskarte". Pattern ng disenyo "Diskarte" - 2Isipin natin na nagsusulat tayo ng isang programa na aktibong gumagana sa object ng Car. Sa kasong ito, hindi ito partikular na mahalaga kung ano ang eksaktong ginagawa ng aming programa. Para magawa ito, gumawa kami ng inheritance system na may isang parent class Autoat tatlong child class: Sedan, Truckat 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 {
}
Lahat ng tatlong klase ng bata ay nagmamana ng dalawang karaniwang pamamaraan mula sa magulang - gas()at stop() ang aming programa ay napakasimple: ang mga kotse ay maaari lamang magmaneho ng pasulong at preno. Sa pagpapatuloy ng aming trabaho, nagpasya kaming magdagdag ng bagong paraan sa mga kotse - fill()(refuel). Idagdag natin ito sa parent class Auto:
public class Auto {

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

   public void stop() {

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

   public void fill() {
       System.out.println("Заправить бензин!");
   }
}
Mukhang ang mga problema ay maaaring lumitaw sa isang simpleng sitwasyon? Well, sa katunayan, bumangon na sila... Pattern ng Disenyo "Diskarte" - 3
public class ChildrenBuggies extends Auto {

   public void fill() {

       //хм... Это детский багги, его не надо заправлять :/
   }
}
Ang isang kotse ay lumitaw sa aming programa na hindi akma sa pangkalahatang konsepto - isang buggy ng mga bata. Maaaring ito ay pinapagana ng pedal o kontrolado ng radyo, ngunit isang bagay ang sigurado - walang mapaglagyan ng gasolina. Ang aming inheritance scheme ay nagresulta sa pagbibigay namin ng mga karaniwang pamamaraan kahit sa mga klase na hindi nangangailangan ng mga ito. Ano ang dapat nating gawin sa ganitong sitwasyon? Well, halimbawa, maaari mong i-override ang pamamaraan fill()sa klase ChildrenBuggiesupang kapag sinubukan mong lagyan ng gatong ang buggy, walang mangyayari:
public class ChildrenBuggies extends Auto {

   @Override
   public void fill() {
       System.out.println("Игрушечную машину нельзя заправить!");
   }
}
Ngunit ang solusyon na ito ay halos hindi matatawag na matagumpay, hindi bababa sa dahil sa pagdoble ng code. Halimbawa, karamihan sa mga klase ay gagamit ng paraan mula sa parent class, ngunit ang ibang mga klase ay mapipilitang i-override ito. Kung mayroon kaming 15 na klase, at sa 5-6 napipilitan kaming i-override ang pag-uugali, magiging malawak ang pagdoble ng code. Baka makatulong sa atin ang mga interface? Halimbawa, ang isang ito:
public interface Fillable {

   public void fill();
}
Gagawa kami ng isang interface Fillablena may isang paraan fill(). Alinsunod dito, ang mga kotseng iyon na kailangang mag-refuel ay ipapatupad ang interface na ito, ngunit ang ibang mga kotse (halimbawa, ang aming buggy) ay hindi. Ngunit ang pagpipiliang ito ay hindi rin angkop sa amin. Maaaring lumaki ang aming hierarchy ng klase sa napakalaking bilang sa hinaharap (isipin kung gaano karaming iba't ibang uri ng mga sasakyan ang mayroon sa mundo). Inabandona namin ang nakaraang opsyon sa mana dahil ayaw naming i-override ang fill(). Dito kailangan nating ipatupad ito sa bawat klase! Paano kung mayroon tayong 50 sa kanila? At kung ang mga madalas na pagbabago ay ginawa sa aming programa (at sa mga tunay na programa ito ay halos palaging mangyayari!), Kailangan naming tumakbo sa paligid na ang aming dila ay nakikipag-hang sa pagitan ng lahat ng 50 mga klase at baguhin ang pag-uugali ng bawat isa sa kanila nang manu-mano. Kaya ano ang dapat nating gawin sa huli? Upang malutas ang ating problema, pumili tayo ng ibang landas. Ibig sabihin, ihiwalay natin ang ugali ng ating klase sa mismong klase. Ano ang ibig sabihin nito? Tulad ng alam mo, ang anumang bagay ay may estado (isang set ng data) at isang pag-uugali (isang hanay ng mga pamamaraan). Ang pag-uugali ng aming klase ng makina ay binubuo ng tatlong pamamaraan - gas(), stop()at fill(). Ang unang dalawang pamamaraan ay maayos. Ngunit ililipat namin ang ikatlong paraan sa labas ng klase Auto. Ito ang magiging paghihiwalay ng pag-uugali mula sa klase (mas tiyak, pinaghihiwalay namin ang bahagi lamang ng pag-uugali - ang unang dalawang pamamaraan ay nananatili sa lugar). Saan natin dapat ilipat ang ating pamamaraan fill()? Wala agad pumapasok sa isip :/ Para siyang buo sa pwesto niya. Ililipat namin ito sa isang hiwalay na interface - FillStrategy!
public interface FillStrategy {

   public void fill();
}
Bakit kailangan natin ang interface na ito? Simple lang. Ngayon ay maaari tayong lumikha ng ilang mga klase na magpapatupad ng interface na ito:
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("Просто заправляем бензин!");
   }
}
Gumawa kami ng tatlong diskarte sa pag-uugali - para sa mga kumbensyonal na kotse, para sa mga hybrid at para sa mga Formula 1 na kotse. Ang bawat diskarte ay nagpapatupad ng isang hiwalay na refueling algorithm. Sa aming kaso, ito ay output lamang sa console, ngunit maaaring mayroong ilang kumplikadong lohika sa loob ng pamamaraan. Ano ang dapat nating gawin sa susunod na ito?
public class Auto {

   FillStrategy fillStrategy;

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

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

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

}
Ginagamit namin ang aming interface FillStrategybilang isang field sa parent class Auto. Pakitandaan: hindi namin tinukoy ang isang partikular na pagpapatupad, ngunit sa halip ay ginagamit ang interface. At kakailanganin namin ng mga partikular na pagpapatupad ng interface FillStrategysa mga klase ng child car:
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();
   }
}
Tingnan natin kung ano ang nakuha natin:
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();
   }
}
Output ng console:

Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
Mahusay, gumagana ang proseso ng refueling ayon sa nararapat! Sa pamamagitan ng paraan, walang pumipigil sa amin mula sa paggamit ng diskarte bilang isang parameter sa constructor! Halimbawa, tulad nito:
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());
   }
}
Patakbuhin natin ang aming pamamaraan main()(ito ay nananatiling hindi nagbabago) at makuha ang parehong resulta! Output ng console:

Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
Tinutukoy ng pattern ng Strategy ang isang pamilya ng mga algorithm, isinasama ang bawat isa sa kanila, at tinitiyak na ang mga ito ay mapapalitan. Ito ay nagpapahintulot sa iyo na baguhin ang mga algorithm anuman ang kanilang paggamit sa panig ng kliyente (ang kahulugan na ito ay kinuha mula sa aklat na "Paggalugad ng Mga Pattern ng Disenyo" at tila sa akin ay lubhang matagumpay). Pattern ng Disenyo "Diskarte" - 4Ibinukod namin ang pamilya ng mga algorithm na kinaiinteresan namin (mga uri ng refueling na sasakyan) sa magkakahiwalay na interface na may ilang pagpapatupad. Pinaghiwalay namin sila mula sa pinaka esensya ng sasakyan. Samakatuwid, ngayon, kung kailangan nating gumawa ng anumang mga pagbabago sa ito o sa proseso ng paglalagay ng gasolina, hindi ito makakaapekto sa ating mga klase ng mga sasakyan sa anumang paraan. Tulad ng para sa pagpapalitan, upang makamit ito kailangan lang naming magdagdag ng isang paraan ng setter sa aming klase 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;
   }
}
Ngayon ay maaari na nating baguhin ang mga diskarte sa mabilisang:
public class Main {

   public static void main(String[] args) {

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

       buggies.fill();
   }
}
Kung biglang mapuno ng gasolina ang mga buggy na sasakyan ng mga bata, magiging handa ang ating programa para sa ganoong senaryo :) Yun lang, actually! Natutunan mo ang isa pang pattern ng disenyo, na walang alinlangan na kakailanganin mo at makakatulong sa iyo nang higit sa isang beses kapag gumagawa ng mga totoong proyekto :) Magkita-kita tayong muli!
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION