Apat na Mga Panuntunan ng Simpler iOS Software Design

Sa huling bahagi ng 1990s, habang ang pagbuo ng Extreme Programming, ang tanyag na developer ng software na si Kent Beck ay dumating sa isang listahan ng mga patakaran para sa simpleng disenyo ng software.

Ayon kay Kent Beck, isang mahusay na disenyo ng software:

  • Tumatakbo ang lahat ng mga pagsubok
  • Naglalaman ng walang pagdoble
  • Nagpapahayag ng hangarin ng programmer
  • Binabawasan ang bilang ng mga klase at pamamaraan

Sa artikulong ito, tatalakayin natin kung paano mailalapat ang mga patakarang ito sa mundo ng pag-unlad ng iOS sa pamamagitan ng pagbibigay ng praktikal na mga halimbawa ng iOS at tinatalakay kung paano tayo makikinabang sa kanila.

Tumatakbo ang lahat ng mga pagsubok

Tumutulong sa amin ang disenyo ng software na lumikha ng isang sistema na kumikilos ayon sa nais. Ngunit paano natin mai-verify na ang isang sistema ay kikilos tulad ng inilaan sa simula ng disenyo nito? Ang sagot ay sa pamamagitan ng paglikha ng mga pagsubok na nagpapatunay nito.

Sa kasamaang palad, sa mga pagsubok sa unibersidad ng pag-unlad ng iOS ang karamihan sa mga beses na iniiwasan ... Ngunit upang lumikha ng isang maayos na dinisenyo na software, dapat nating palaging isulat ang Swift code na may kakayahang mag-isip sa isip.

Talakayin natin ang dalawang mga prinsipyo na maaaring gawing mas simple ang pagsulat ng pagsubok at disenyo ng system. At sila ay Single Responsibility Principle at Dependency Injection.

Pangunahing Prinsipyo ng Pananagutan (SRP)

Sinabi ng SRP na ang isang klase ay dapat magkaroon ng isa, at iisa lamang ang dahilan upang baguhin. Ang SRP ay isa sa pinakasimpleng mga prinsipyo, at isa sa pinakamahirap na makakuha ng tama. Ang paghahalo ng mga responsibilidad ay isang bagay na natural na ginagawa natin.

Bigyan tayo ng isang halimbawa ng ilang code na napakahirap subukan at pagkatapos ng refactor na ito sa pamamagitan ng paggamit ng SRP. Pagkatapos ay pag-usapan kung paano ito nasubok ang code.

Ipagpalagay na sa kasalukuyan ay kailangan nating ipakita ang isang PaymentViewController mula sa aming kasalukuyang tagapamahala ng view, dapat na i-configure ng PaymentViewController ang pananaw nito depende sa presyo ng aming produkto sa pagbabayad. Sa aming kaso, ang presyo ay variable depende sa ilang mga panlabas na kaganapan ng gumagamit.

Ang code para sa pagpapatupad na ito ay kasalukuyang mukhang:

Paano natin masusubukan ang code na ito? Ano ang dapat nating subukan? Tama bang kinakalkula ang diskwento ng presyo? Paano natin bibigya ang mga kaganapan sa pagbabayad upang masubukan ang diskwento?

Ang pagsusulit ng mga pagsusulit para sa klase na ito ay magiging kumplikado, dapat nating makahanap ng isang mas mahusay na paraan upang maisulat ito. Buweno, unahin muna natin ang malaking problema. Kailangan nating hubarin ang ating mga dependencies.

Nakita namin na mayroon kaming lohika para sa pag-load ng aming produkto. Mayroon kaming Mga Kaganapan sa Pagbabayad na ginagawang karapat-dapat ang isang gumagamit para sa isang diskwento. Mayroon kaming mga Diskwento, isang diskwento sa diskwento at nagpapatuloy ang listahan.

Kaya't subukan nating i-translate ang mga ito sa Swift code.

Lumikha kami ng isang PaymentManager na namamahala sa aming lohika na may kaugnayan sa mga pagbabayad, at Paghiwalayin ang PriceCalculator na madaling masubok. Gayundin, isang data-loader na responsable para sa pakikipag-ugnay sa network o database para sa pag-load ng aming mga produkto.

Nabanggit din namin na kailangan namin ng isang klase na may pananagutan sa pamamahala ng mga diskwento. Tawagin natin itong CouponManager at hayaan itong maging namamahala sa mga kupon ng diskwento ng gumagamit.

Ang aming Controller view ng Pagbabayad ay maaaring magmukhang mga sumusunod:

Maaari kaming magsulat ngayon ng mga pagsubok

  • pagsubokCalculatingFinalPriceWithoutCoupon
  • pagsubokCalculatingFinalPriceWithCoupon
  • testCouponExists

at marami pang iba! Sa pamamagitan ng paglikha ng magkahiwalay na bagay ngayon maiiwasan natin ang mga hindi kinakailangang pagkopya at lumikha din ng isang code na madaling magsulat ng mga pagsubok.

Depende sa Injection

Ang pangalawang prinsipyo ay ang Dependency Injection. At nakita namin mula sa mga halimbawa sa itaas na ginamit na namin ang dependency injection sa aming mga inisyal na object.

Mayroong dalawang pangunahing benepisyo ng pag-iniksyon ng aming mga dependencies tulad ng nasa itaas. Malinaw nitong malinaw sa kung ano ang dependencies ng aming mga uri na umaasa at pinapayagan kaming maglagay ng mga bagay na tanga kapag nais naming subukan sa halip na ang mga tunay.

Ang isang mahusay na pamamaraan ay upang lumikha ng mga protocol para sa aming mga bagay at magbigay ng kongkreto na pagpapatupad ng tunay at ang bagay na pangungutya tulad ng mga sumusunod:

Ngayon madali naming magpasya kung aling klase ang nais naming mag-iniksyon bilang isang dependant.

Ang mahigpit na pagkabit ay ginagawang mahirap magsulat ng mga pagsubok. Kaya, katulad din, ang mas maraming mga pagsubok na isinusulat namin, mas ginagamit namin ang mga prinsipyo tulad ng DIP at mga tool tulad ng dependency injection, interface, at abstraction upang mabawasan ang pagkabit.

Ang paggawa ng code na mas nasusubok hindi lamang inaalis ang aming takot na masira ito (dahil isusulat namin ang pagsubok na susuportahan tayo) ngunit nag-aambag din sa pagsulat ng mas malinis na code.

Ang bahaging ito ng artikulo ay nababahala nang higit pa tungkol sa kung paano sumulat ng code na masusubukan kaysa sa pagsulat ng aktwal na pagsubok sa yunit. Kung nais mong malaman ang higit pa tungkol sa pagsulat ng unit test, maaari mong suriin ang artikulong ito kung saan nilikha ko ang laro ng buhay gamit ang pag-unlad na hinihimok ng pagsubok.

Naglalaman ng walang pagdoble

Ang pagdoble ay ang pangunahing kaaway ng isang maayos na dinisenyo na sistema. Kinakatawan nito ang karagdagang trabaho, karagdagang panganib, nagdaragdag ng hindi kinakailangang pagiging kumplikado.

Sa bahaging ito, tatalakayin namin kung paano namin magagamit ang pattern ng disenyo ng template para sa pagtanggal ng mga karaniwang pagkopya sa iOS. Upang mas madaling maunawaan na pupunta namin ang refactor pagpapatupad ng isang real-life chat.

Ipagpalagay na mayroon kaming kasalukuyang nasa aming app ng isang karaniwang seksyon ng chat. Ang isang bagong kinakailangan ay darating at ngayon nais naming ipatupad ang isang bagong uri ng chat - isang live-chat. Ang isang chat na dapat maglaman ng mga mensahe na may pinakamataas na 20 na bilang ng mga character at ang chat na ito ay mawawala kapag tinanggal namin ang view ng chat.

Ang chat na ito ay magkakaroon ng parehong pananaw tulad ng aming kasalukuyang chat ngunit magkakaroon ng ilang magkakaibang mga patakaran:

  1. Ang kahilingan ng network para sa pagpapadala ng mga mensahe ng chat ay magkakaiba.

2. Ang mga mensahe ng chat ay dapat maging maikli, hindi hihigit sa 20 mga character para sa mensahe.

3. Ang mga mensahe ng chat ay hindi dapat ipagpilit sa aming lokal na database.

Ipagpalagay na gumagamit kami ng arkitektura ng MVP at kasalukuyang pinangangasiwaan namin ang lohika para sa pagpapadala ng mga mensahe ng chat sa aming nagtatanghal. Subukan nating magdagdag ng mga bagong patakaran para sa aming bagong uri ng chat na pinangalanang live-chat.

Ang isang walang saysay na pagpapatupad ay tulad ng mga sumusunod:

Ngunit ano ang mangyayari kung sa hinaharap magkakaroon tayo ng mas maraming mga uri ng chat?
Kung patuloy nating idagdag kung ang iba pa na suriin ang estado ng aming chat sa bawat pag-andar, ang code ay magiging magulo upang mabasa at mapanatili. Gayundin, hindi masusubukan at ang pagsuri sa estado ay mai-duplicate sa buong saklaw ng nagtatanghal.

Dito ginagamit ang template ng template. Ginagamit ang pattern ng template kapag kailangan namin ng maraming pagpapatupad ng isang algorithm. Ang template ay tinukoy at pagkatapos ay binuo sa karagdagang mga pagkakaiba-iba. Gamitin ang pamamaraang ito kapag ang karamihan sa mga subclass ay kailangang ipatupad ang parehong pag-uugali.

Maaari kaming lumikha ng isang protocol para sa Chat Presenter at pinaghiwalay namin ang mga pamamaraan na maipapatupad nang iba sa pamamagitan ng mga kongkretong bagay sa Mga Phase ng Chat Presenter.

Maaari na nating gawin ang aming presenter na sumunod sa IChatPresenter

Hinahawak ngayon ng aming Presenter ang pagpapadala ng mensahe sa pamamagitan ng pagtawag ng mga karaniwang pag-andar sa loob mismo at iginawad ang mga function na maaaring maipatupad nang iba.

Ngayon ay maaari naming ibigay ang Lumikha ng mga bagay na sumasangayon sa mga yugto ng nagtatanghal batay at i-configure ang mga function na batay sa kanilang mga pangangailangan.

Kung gumagamit kami ng dependency injection sa aming view Controller maaari na nating magamit ngayon ang parehong manlalaban ng view sa dalawang magkakaibang kaso.

Sa pamamagitan ng paggamit ng Mga pattern ng Disenyo maaari nating gawing simple ang aming code sa iOS. Kung nais mong malaman ang higit pa tungkol dito, ang sumusunod na artikulo ay nagbibigay ng karagdagang paliwanag.

Nagpapahayag

Ang karamihan ng gastos ng isang proyekto ng software ay nasa pangmatagalang pagpapanatili. Ang pagsulat ng madaling basahin at mapanatili ang code ay dapat para sa mga developer ng software.

Maaari kaming mag-alok ng mas nagpapahayag na code sa pamamagitan ng paggamit ng mahusay na Pangalan, Paggamit ng pagsusuri sa SRP at Pagsulat.

Pangalan

Bilangin ang isang bagay na ginagawang mas nagpapahayag ng code - at ito ang pagbibigay ng pangalan. Mahalagang magsulat ng mga pangalan na:

  • Magbunyag ng hangarin
  • Iwasan ang disinformation
  • Madaling mahahanap

Pagdating sa mga klase at pag-andar, ang isang mahusay na bilis ng kamay ay ang paggamit ng isang pangngalan o pangngalan-parirala para sa mga klase at mga pandiwa ng gumagamit o mga parirala sa pariralang pandiwa para sa mga pamamaraan.

Gayundin kapag gumagamit ng iba't ibang mga pattern ng Disenyo paminsan-minsan mahusay na idagdag ang mga pangalan ng pattern tulad ng Command o Bisita sa pangalan ng klase. Kaya alam agad ng mambabasa kung anong pattern ang ginamit doon nang hindi kinakailangang basahin ang lahat ng code upang malaman ang tungkol doon.

Paggamit ng SRP

Ang isa pang bagay na nagpapahiwatig ng code ay ang paggamit ng Pangunahing Prinsipyo ng Pananagutan na nabanggit mula sa itaas. Maaari mong ipahiwatig ang iyong sarili sa pamamagitan ng pagpapanatiling maliit ang iyong mga pag-andar at klase at para sa isang solong layunin. Ang mga maliliit na klase at pag-andar ay karaniwang madaling pangalan, madaling isulat, at madaling maunawaan. Ang isang function ay dapat maglingkod lamang para sa isang layunin.

Pagsusulit ng pagsusulit

Ang pagsusulit sa pagsusulit ay nagdudulot din ng kalinawan, lalo na kapag nagtatrabaho sa legacy code. Ang mga nakasubok na yunit ng pagsusulit ay nagpapahayag din. Ang isang pangunahing layunin ng mga pagsubok ay ang kumilos bilang dokumentasyon ayon sa halimbawa. Ang isang tao na nagbabasa ng aming mga pagsusulit ay dapat makakuha ng mabilis na pag-unawa sa kung ano ang tungkol sa isang klase.

Paliitin ang bilang ng mga klase at pamamaraan

Ang mga pag-andar ng isang klase ay dapat manatiling maikli, isang function ay dapat palaging gumaganap ng isang bagay lamang. Kung ang isang pag-andar ay may maraming mga linya na maaaring ang kaso na ito ay gumaganap ng mga aksyon na maaaring mahati sa dalawa o higit pang magkahiwalay na pag-andar.

Ang isang mahusay na diskarte ay upang mabilang ang mga pisikal na linya at subukang maghangad ng pinakamataas na apat hanggang anim na linya ng mga pag-andar, sa karamihan ng mga kaso anumang bagay na higit pa kaysa sa bilang ng mga linya na ito ay maaaring maging mahirap basahin at mapanatili.

Ang isang mahusay na ideya sa iOS ay upang i-chop ang mga tawag sa pagsasaayos na karaniwang ginagawa namin sa viewDidLoad o viewDidAppear function.

Sa ganitong paraan, ang bawat isa sa mga pag-andar ay magiging maliit at mapanatili sa halip ng isang gulo viewDidLoad function. Parehong dapat ding mag-aplay para sa delegado ng app. Dapat nating iwasang itapon ang bawat pagsasaayos ng ondidFinishLaunchingWithOptions na pamamaraan at hiwalay na mga pag-andar ng pagsasaayos o kahit na mas mahusay na mga klase sa pagsasaayos.

Sa mga pag-andar, mas madaling masusukat kung pinapanatili natin ito mahaba o maikli, maaari nating kadalasang umaasa lamang sa pagbibilang ng mga pisikal na linya. Sa mga klase, gumagamit kami ng ibang sukatan. Binibilang namin ang mga responsibilidad. Kung ang isang klase ay may limang mga pamamaraan lamang ay hindi nangangahulugang maliit ang klase ay maaaring ito ay may napakaraming responsibilidad na may mga pamamaraan lamang.

Ang isang kilalang problema sa iOS ay ang malaking sukat ng UIViewControllers. Totoo na sa pamamagitan ng disenyo ng manlalaro ng view ng mansanas, mahirap mapanatili ang mga bagay na ito upang maghatid ng isang solong layunin ngunit dapat nating subukan ang aming makakaya.

Maraming mga paraan upang gawing maliit ang UIViewControllers na aking kagustuhan ay ang paggamit ng isang arkitektura na may mas mahusay na paghihiwalay ng mga alalahanin tulad ng VIPER o MVP ngunit hindi nangangahulugan na hindi natin ito gagawing mas mahusay sa mansanas na MVC.

Sa pamamagitan ng pagsubok upang paghiwalayin ang maraming mga alalahanin maaari naming maabot ang medyo disenteng code sa anumang arkitektura. Ang ideya ay upang lumikha ng mga klase na walang layunin na maaaring maglingkod bilang mga katulong sa mga tagapamahala ng view at gawing mas madaling mabasa at masusubukan ang code.

Ang ilang mga bagay na maaaring maiiwasan nang walang dahilan sa mga tagapamahala ng view ay:

  • Sa halip na pagsulat nang direkta sa code ng network ay dapat mayroong isang NetworkManager isang klase na responsable para sa mga tawag sa network
  • Sa halip na pagmamanipula ng data sa mga manlalaro ng view maaari lamang tayong lumikha ng isang DataManager isang klase na may pananagutan sa iyon.
  • Sa halip na maglaro sa mga string ng UserDefaults sa UIViewController maaari kaming lumikha ng isang facade sa na.

Sa Konklusyon

Naniniwala ako na dapat nating isulat ang software mula sa mga sangkap na tumpak na pinangalanan, simple, maliit, responsable para sa isang bagay at magagamit muli.

Sa artikulong ito, napag-usapan namin ang apat na mga patakaran para sa simpleng disenyo ni Kent Beck at nagbigay ng mga praktikal na halimbawa kung paano namin maipapatupad ang mga ito sa kapaligiran ng iOS Development.

Kung nasiyahan ka sa artikulong ito siguraduhin na magpalakpak upang ipakita ang iyong suporta. Sundan mo ako upang matingnan ang maraming mga artikulo na maaaring kumuha ng iyong mga kasanayan sa iOS Developer sa isang susunod na antas.

Kung mayroon kang anumang mga katanungan o komento huwag mag-iwan ng isang tala dito o mag-email sa akin sa arlindaliu.dev@gmail.com.