Jifunze zaidi

Sura ya 8: Kufungua upya kwa hatua ya hatua


Kwa kiasi kikubwa, programu ni sayansi ya kutatua matatizo kwa kompyuta. Sababu matatizo mara nyingi ni magumu, ufumbuzi-na mipango ambayo hutekeleza ufumbuzi huo-inaweza kuwa vigumu pia. Ili iwe rahisi iwe kukuza ufumbuzi huo, unahitaji kupitisha mbinu na nidhamu ambayo inapunguza kiwango cha utata huo kwa kiwango kikubwa.

Katika miaka ya mwanzo ya programu, dhana ya kompyuta kama sayansi ilikuwa zaidi au chini ya jaribio la kufikiri unataka. Hakuna mtu aliyejua mengi kuhusu programu katika siku hizo, na walidhani wachache kama nidhamu ya uhandisi kwa maana ya kawaida. Kama programu ilikua, hata hivyo, nidhamu hiyo ilianza kuibuka. Msingi wa msingi wa nidhamu hiyo ni ufahamu kwamba programu hufanyika katika mazingira ya kijamii ambayo waendeshaji wanapaswa kufanya kazi pamoja. Ikiwa utaingia kwenye sekta, hakika utakuwa mojawapo ya programu nyingi zinazofanya kazi ili kuendeleza programu kubwa. Mpango huo, zaidi ya hayo, ni karibu kuishi na unahitaji matengenezo zaidi ya matumizi yake ya awali. Mtu atakayependa mpango wa kuingiza kipengele kipya au kazi kwa njia tofauti. Wakati huo unatokea, timu mpya ya waandaaji lazima iingie na kufanya mabadiliko muhimu katika programu. Ikiwa mipango imeandikwa kwa mtindo wa kibinafsi na kawaida au hakuna kawaida, kupata kila mtu kufanya kazi pamoja kwa ufanisi ni ngumu sana.

Ili kupambana na tatizo hili, waandaaji walianza kukuza seti ya mbinu za programu zinazoitwa kwa pamoja uhandisi wa programu . Kutumia ujuzi bora wa uhandisi wa programu sio rahisi iwe rahisi kwa waandishi wengine kusoma na kuelewa mipango yako, lakini pia inakuwezesha wewe kuandika mipango hiyo mahali pa kwanza. Mojawapo ya maendeleo muhimu ya mbinu ya kuja nje ya uhandisi wa programu ni mkakati wa kubuni-juu chini au uboreshaji wa stepwise , ambayo inajumuisha matatizo kwa kuanzia na shida kwa ujumla. Ukivunja tatizo lote chini, na kisha kutatua kila kipande, ukivunja wale chini ikiwa ni lazima. Mkakati huu wa juu chini unafungwa na kupima iterative ambapo unahakikisha kwamba vipande vidogo vya ufumbuzi hufanya kazi kabla ya kuendelea.

Zoezi katika kuboresha hatua kwa hatua

Kuelezea dhana ya kuboresha hatua kwa hatua, hebu tufundishe Karel kutatua tatizo jipya. Fikiria kwamba Karel sasa anaishi katika ulimwengu unaoonekana kama hii:

Katika kila nguzo, kuna mnara wa koni s wa urefu usiojulikana, ingawa baadhi ya nguzo (kama vile 7, na 9 katika ulimwengu wa sampuli) zinaweza kuwa tupu. Kazi ya Karel ni kukusanya koni katika kila moja ya minara hii, koni chini kwenye kona ya kusini ya mstari wa 1, halafu kurudi kwenye nafasi yake ya kuanzia. Hivyo, wakati Karel alipomaliza kazi yake katika mfano hapo juu, wote 25 koni s sasa katika minara wanapaswa kuwekwa kwenye kona ya safu ya 9 na mstari wa 1, kama ifuatavyo:

Muhimu, unaweza kudhani kuwa Karel awalikuanzana sifuri koni s katika mfuko wake. Kila koni ilichukua imeongezwa kwenye mfuko wake. Wakati wa kuweka koni s kwenye kona, karel anaweza kutumia beepersInBag() mtihani.

Funguo la kutatua tatizo hili ni kuharibu programu kwa njia sahihi, huku bado unaweza kujaribu wakati unapoenda. Kazi hii ni ngumu zaidi kuliko wengine uliyoyaona, ambayo inafanya uchaguzi wa subproblems zinazofaa zaidi ili kupata suluhisho la mafanikio.

Kanuni ya kubuni ya juu-chini

Wazo muhimu katika uboreshaji wa hatua kwa hatua ni kwamba unapaswa kuanza mpango wa programu yako kutoka juu, ambayo inahusu kiwango cha programu ambayo ni ya juu na ya kufikiri zaidi. Katika ngazi hii, tatizo la mnara koni wazi katika awamu tatu za kujitegemea. Kwanza, Karel anatakiwa kukusanya wote koni s. Pili, Karel atawaweka kwenye makutano ya mwisho. Tatu, Karel ana kurudi nyumbani kwake. Uharibifu huu wa dhana ya tatizo unaonyesha kuwa njia ya run ya programu hii itakuwa na muundo wafuatayo:

   public void run() {
      kukusanyaWoteKoniS();
      toneYoteKoniS();
      kurudiNyumbani();
   }

Katika ngazi hii, tatizo ni rahisi kuelewa. Bila shaka, kuna maelezo machache yameachwa kwa namna ya njia ambazo bado haujaandikwa. Hata hivyo, ni muhimu kuangalia kila ngazi ya kuharibika na kujihakikishia kuwa, kwa muda mrefu tu kama unaamini kuwa njia ambazo unakaribia kuandika zitatatua vifungu vyenye usahihi, basi utakuwa na suluhisho kwa shida kwa ujumla .

Upimaji wa upimaji unapoenda

Sasa kwa kuwa umefafanua muundo wa programu kwa ujumla, ni wakati wa move kwenye subproblem ya kwanza, ambayo inajumuisha kukusanya wote koni s. Kazi hii ni yenye ngumu zaidi kuliko matatizo rahisi kutoka kwa sura zilizopita. Kukusanya njia zote koni kwamba unapaswa kuchukua koni s kila mnara hadi koni kona ya mwisho. Ukweli kwamba unahitaji kurudia operesheni kwa kila mnara unaonyesha kwamba unahitaji mzunguko while hapa. mzunguko while mchakato wa kukusanyaMnaraMmoja na kisha kusonga.

Tahadhari: Ni hatari kujaribu kuandika mpango mzima bila kupima ni unapoenda. Ukitenda kosa itakuwa vigumu kupata kosa. Tunajua kwamba tutarudia mchakato wa kukusanya mnara mmoja. Hebu tuandike na mtihani kukusanya mnara mmoja kabla ya kuweka KukusanyaMnaraMmoja mchakato katika mzunguko for . Hivyotemporariliytunaweza kuanza na ufafanuzi wafuatayo wa kukusanyaKoniS:

   private void kukusanyaWoteKoniS() {
      /* utekelezaji wa muda kwa madhumuni ya kupima */
      kukusanyaMnaraMmoja();
      move();
   }

Kama kanuni ya kuongoza, ikiwa una kitanzi tata, jaribumwiliya kitanzi kabla ya kuandika kitanzi kote.

Kuboresha mnara wa kukusanya

Wakati kukusanyaMnaraMmoja inaitwa, Karel amesimama chini ya mnara wa koni au amesimama kona tupu. Katika kesi ya zamani, unahitaji kukusanya koni s katika mnara. Katika mwisho, unaweza tu move juu. Hali hii inaonekana kama programu ya test si , ambayo unaweza kuandika kitu kama hiki:

   if(koniIko()){
      kukusanyaMnaraHalisi();
   }

Kabla ya kuongeza maelezo kama hiyo kwa kificho, unapaswa kufikiria kama unahitaji kufanya mtihani huu. Mara nyingi, mipango inaweza kufanywa rahisi sana kwa kuchunguza kesi ambazo kwa mara ya kwanza zinaonekana kuwa za pekee zinaweza kutibiwa kwa njia sawa na hali ya kawaida zaidi. Katika tatizo la sasa, ni nini kinachotokea ikiwa unaamua kuwa kuna mnara wa koni s kila mahali lakini kwamba baadhi ya minara hiyo ni zero koni s juu? Kutumia ufahamu huu kunapunguza programu kwa sababu huna tena kupima ikiwa kuna mnara juu ya avenue fulani.

Njia ya kukusanyaMnaraMmoja bado ni ngumu ya kutosha kuwa kiwango cha ziada cha utengano ni kwa utaratibu. Kukusanya wote koni s katika mnara, Karel anahitaji kufanya hatua zifuatazo:

  1. Pinduka kushoto ili uso na koni s katika mnara.
  2. Kukusanya wote koni s katika mnara, kuacha wakati hakuna zaidi koni s hupatikana.
  3. Pinduka kuzunguka nyuma kuelekea chini ya dunia.
  4. Rudi kwenye ukuta unaowakilisha ardhi.
  5. Pinduka kushoto ili uwe tayari hadi move kwenye kona inayofuata.

Mara nyingine tena, muhtasari huu hutoa mfano wa njia ya kukusanyaMnaraMmoja, ambayo inaonekana kama hii:

   private void kukusanyaMnaraMmoja(){
      geukaKushoto();
      kukusanyaMstariWaKoniS();
      turnAround();
      moveKwaUkuta();
      geukaKushoto();
   }

Njia za utaratibu na postconditions

Amri geukaKushoto mwanzo na mwisho wa njia ya kukusanyaMnaraMmoja ni muhimu sana kwa usahihi wa programu hii. Wakati kukusanyaMnaraMmoja inaitwa, Karel daima ni mahali fulani kwenye safu ya kwanza inakabiliwa mashariki. Baada ya kukamilisha kazi yake, mpango kwa ujumla utafanya kazi kwa usahihi tu kama Karel anakabiliwa tena mashariki kwenye kona hiyo hiyo. Masharti lazima iwe ya kweli kabla ya njia inayoitwa inajulikana kama masharti ; hali ambazo zinapaswa kutumika baada ya kumaliza njia hujulikana kama postconditions .

Unapofafanua utaratibu, utaingia shida kubwa sana ikiwa unaandika hasa nini kabla na baada ya maandishi. Mara baada ya kufanya hivyo, basi unahitaji kuhakikisha kwamba kanuni unazoandika daima hutoshea amri ya postconditions, na kuzingatia kwamba masharti yalikubalika kuanza. Kwa mfano, fikiria juu ya kile kinachotokea ikiwa unaita kukusanyaMnaraMmoja wakati Karel yuko kwenye mstari wa kwanza akitazama mashariki. Amri geukaKushoto kwanza geukaKushoto Karel akielekea kaskazini, ambayo inamaanisha kuwa Karel koni vizuri na safu ya koni s inayowakilisha mnara. Njia ya kukusanyaMaKoniS-ambayo bado haijaandikwa lakini hata hivyo hufanya kazi ambayo move conceptually - tu move s bila kugeuka. Hivyo, mwishoni mwa wito wa kukusanyaMarariWaKoniS, Karel bado atakabiliwa na kaskazini. Kwa hiyo wito turnAround huondoka Karel akielekea kusini. Kama kukusanyaMstariWaKoniS, njia ya move KwaUkuta haihusisha zamu yoyote bali badala ya move tu mpaka inapiga ukuta wa mpaka. Kwa sababu Karel anaelekea kusini, ukuta huu wa mipaka utakuwa moja chini ya skrini, chini ya mstari wa 1. Kwa hiyo amri ya geukaKushoto mwisho geukaKushoto Karel kwenye mstari wa kwanza inakabiliwa na mashariki, ambayo inatimiza postcondition.

Kurudia mchakato

Wewe run programu yako na hufanikiwa kufuta mnara mmoja na kuondoka Karel katika postcondition iliyoahidiwa. Wahoo! Umegonga tu hatua muhimu katika kutatua kazi hii ngumu! Sasa tunapaswa kurudia mchakato wa kusafisha mnara mmoja kwa kutumia mzunguko while .

Lakini hii mzunguko while inaonekana kama nini? Kwanza kabisa, unapaswa kufikiri juu ya mtihani wa masharti. Unataka Karel kuacha wakati inapiga ukuta mwishoni mwa mstari. Kwa hiyo, unataka Karel kuendelea kama muda mrefu mbele. Kwa hiyo, unajua kuwa njia ya kukusanyaWoteKoniS itajumuisha mzunguko while ambayo inatumia mtihani wa frontIsClear . Kwa kila nafasi, unataka Karel kukusanya wote koni s katika mnara koni na kona hiyo. Ikiwa unatoa jina hilo, jina ambalo linaweza kuwa kitu kama kukusanyaMnaraMmoja, unaweza kwenda mbele na kuandika ufafanuzi kwa njia ya kukusanyaWoteKoniS hata kama bado haujajaza maelezo.

Hata hivyo, unapaswa kuwa makini. Msimbo wa kukusanyaKoteKoniS haionekani kama hii:

   private void kukusanyaWoteKoniS(){
      /* buggy kitanzi! */
      while(frontIsClear()) {
         kukusanyaMnaraMmoja();
         move();
      }
   }

Utekelezaji huu ni buggy kwa sababu sawa sawa kwamba toleo la kwanza la PlaceKoniLine kwa ujumla kutoka sura ya 6 lilishindwa kufanya kazi yake. Kuna kosa la fence katika toleo hili la msimbo, kwa sababu Karel anahitaji kupima uwepo wa mnara wa koni kwenye avenue ya mwisho. Utekelezaji sahihi ni:

   private void kukusanyaWoteKoniS(){
      while(frontIsClear()) {
         kukusanyaMnaraMmoja();
         move();
      }
      kukusanyaMnaraMmoja();
   }

Kumbuka kuwa njia hii ina muundo sawa na programu kuu kutoka kwenye mpango wa PlaceKoniLine iliyotolewa katika sura ya 6. Tofauti pekee ni kwamba programu hii inaita kukusanyaMnaraMmoja ambapo nyingine inayoitwa wekaKoni . Programu hizi mbili ni kila aina ya mkakati wa jumla unaoonekana kama hii:

   private void kukusanyaWoteKoniS(){
      while(frontIsClear()) {
          kufanya kazi fulani.
         move();
      }
       kufanya operesheni sawa kwa kona ya mwisho.
   }

Unaweza kutumia mkakati huu kila wakati unahitaji kufanya operesheni kila kona kama wewe move kando ya njia move ukuta. Ikiwa unakumbuka muundo wa jumla wa mkakati huu, unaweza kuitumia wakati wowote unakabiliwa na tatizo ambalo inahitaji uendeshaji huo. Mikakati ya kurejeshwa ya aina hii huja mara kwa mara katika programu na inajulikana kama Idioms ya programu au chati . Mwelekeo zaidi unaowajua, itakuwa rahisi kwako kupata moja ambayo yanafaa aina fulani ya tatizo.

Kumaliza

Ijapokuwa kazi ngumu imefanywa, bado kuna sehemu kadhaa za kutosha zinahitaji kutatuliwa. Programu kuu inaita njia mbili za sautiNoteKoniS na kurudiNyumbani- ambazo bado hazijaandikwa. Vile vile, kukusanyaMnaraMmoja inaita kukusanyaMarariWaKoniS na move KwaUkuta. Kwa bahati nzuri, njia zote nne hizi ni rahisi kutosha kanuni bila kuharibika zaidi, hasa ikiwa unatumia move KwaUkuta katika ufafanuzi wa kurudiNyumbani. Hapa ni utekelezaji kamili:


Sura inayofuata