Patru reguli de proiectare software simplă iOS

La sfârșitul anilor 90, în timp ce dezvolta programare extremă, celebrul dezvoltator de software Kent Beck a prezentat o listă de reguli pentru proiectarea software simplă.

Potrivit lui Kent Beck, un design software bun:

  • Execută toate testele
  • Nu conține nicio duplicare
  • Exprima intenția programatorului
  • Minimizează numărul de clase și metode

În acest articol, vom discuta modul în care aceste reguli pot fi aplicate în lumea de dezvoltare a iOS, oferind exemple practice iOS și discutând cum putem beneficia de ele.

Execută toate testele

Proiectarea software-ului ne ajută să creăm un sistem care să acționeze așa cum este prevăzut. Dar cum putem verifica dacă un sistem va acționa așa cum intenționează inițial prin proiectarea sa? Răspunsul este prin crearea de teste care să o valideze.

Din păcate, testele universului de dezvoltare iOS sunt de cele mai multe ori evitate ... Dar pentru a crea un software bine proiectat, ar trebui să scriem întotdeauna codul Swift având în vedere testabilitatea.

Să discutăm două principii care pot simplifica scrierea testelor și designul sistemului. Și sunt Principiul Responsabilității Unice și Injecția Dependenței.

Principiul responsabilității unice (SRP)

SRP afirmă că o clasă ar trebui să aibă unul și un singur motiv de schimbare. SRP este unul dintre cele mai simple principii și unul dintre cele mai greu de obținut. Amestecarea responsabilităților este ceva ce facem în mod natural.

Să oferim un exemplu de cod pe care este foarte greu de testat și după care îl refactorizează folosind SRP. Apoi discutați cum a făcut testarea codului.

Să presupunem că în prezent trebuie să prezentăm un PaymentViewController de la controlorul nostru de vizualizare curent, PaymentViewController ar trebui să-și configureze vederea în funcție de prețul produsului nostru de plată. În cazul nostru, prețul este variabil în funcție de unele evenimente ale utilizatorilor externi.

Codul pentru această implementare arată în prezent:

Cum putem testa acest cod? Ce trebuie să testăm mai întâi? Reducerea prețului este calculată corect? Cum putem batjocori evenimentele de plată pentru a testa reducerea?

Testele de scriere pentru această clasă ar fi complicate, ar trebui să găsim o modalitate mai bună de a scrie. Ei bine, mai întâi să abordăm marea problemă. Trebuie să ne dezlănțuim dependențele.

Vedem că avem logică pentru încărcarea produsului nostru. Avem evenimente de plată care fac ca utilizatorul să fie eligibil pentru o reducere. Avem reduceri, un calcul de reducere și lista continuă.

Deci, să încercăm să le traducem pur și simplu în cod Swift.

Am creat un Manager de plată care gestionează logica noastră legată de plăți și un Calculator de preț separat, care este testabil cu ușurință. De asemenea, un încărcător de date care este responsabil pentru interacțiunea rețelei sau a bazei de date pentru încărcarea produselor noastre.

De asemenea, am menționat că avem nevoie de o clasă responsabilă cu gestionarea reducerilor. Să-l numim CouponManager și să-l lăsăm la fel de bine să gestioneze cupoanele cu reduceri pentru utilizatori.

Controlorul nostru de vizualizare a plăților poate arăta astfel:

Putem scrie acum teste de genul

  • testCalculatingFinalPriceWithoutCoupon
  • testCalculatingFinalPriceWithCoupon
  • testCouponExists

si multe altele! Prin crearea de obiecte separate, acum evităm duplicarea inutilă și, de asemenea, am creat un cod pentru care este ușor de scris testele.

Injecția de dependență

Al doilea principiu este Injecția dependenței. Și am văzut din exemplele de mai sus că am folosit deja injecția de dependență asupra inițiatorilor obiectului nostru.

Există două avantaje majore ale injectării dependențelor noastre ca mai sus. Este clar în ce dependențe se bazează tipurile noastre și ne permite să introducem obiecte batjocore atunci când vrem să testăm în loc de cele reale.

O tehnică bună este crearea de protocoale pentru obiectele noastre și asigurarea implementării concrete de către obiectul real și de tipul batjocor cum ar fi:

Acum putem decide cu ușurință ce clasă dorim să injectăm ca dependență.

Cuplarea strânsă face dificilă scrierea testelor. Deci, în mod similar, cu cât scriem mai multe teste, cu atât folosim principii precum DIP și instrumente precum injecția de dependență, interfețe și abstractizare pentru a minimiza cuplarea.

Crearea codului mai testabil nu numai că elimină frica noastră de a nu-l rupe (deoarece vom scrie testul care ne va sprijini), dar contribuie și la scrierea codului mai curat.

Această parte a articolului a fost preocupată mai mult de modul de scriere a codului care va fi testabil decât de scrierea testului unității reale. Dacă doriți să aflați mai multe despre scrierea testului unității, puteți consulta acest articol unde creez jocul vieții folosind dezvoltarea bazată pe test.

Nu conține nicio duplicare

Duplicarea este inamicul principal al unui sistem bine proiectat. Reprezintă muncă suplimentară, risc suplimentar, adaugă complexitate inutilă.

În această secțiune, vom discuta despre modul în care putem utiliza modelul de proiectare a șabloanelor pentru a elimina duplicarea comună în iOS. Pentru a înțelege mai ușor, vom refactoriza implementarea unui chat din viața reală.

Să presupunem că în prezent avem în aplicație o secțiune de chat standard. O nouă cerință apare și acum vrem să implementăm un nou tip de chat - un live-chat. Un chat care ar trebui să conțină mesaje cu un număr maxim de 20 de caractere și acest chat va dispărea atunci când vom respinge vizualizarea chat-ului.

Acest chat va avea aceleași vizualizări ca chatul nostru actual, dar va avea câteva reguli diferite:

  1. Cererea de rețea pentru trimiterea mesajelor de chat va fi diferită.

2. Mesajele de chat trebuie să fie scurte, nu mai mult de 20 de caractere pentru mesaj.

3. Mesajele de chat nu trebuie persistate în baza noastră de date locală.

Să presupunem că folosim arhitectura MVP și în prezent gestionăm logica pentru trimiterea de mesaje de chat în prezentatorul nostru. Să încercăm să adăugăm noi reguli pentru noul nostru tip de chat numit live-chat.

O implementare naivă ar fi următoarea:

Dar ce se întâmplă dacă în viitor vom avea mult mai multe tipuri de chat?
Dacă continuăm să adăugăm dacă verificăm starea chatului nostru în fiecare funcție, codul va deveni greu de citit și de întreținut. De asemenea, este greu de testat și verificarea stării ar fi duplicată în toată aria de aplicare a prezentatorului.

Acesta este locul în care este utilizat modelul de șabloane. Modelul de șabloane este utilizat atunci când avem nevoie de mai multe implementări ale unui algoritm. Modelul este definit și apoi construit cu alte variante. Utilizați această metodă atunci când majoritatea subclaselor trebuie să implementeze același comportament.

Putem crea un protocol pentru Chat Presenter și vom separa metode care vor fi implementate diferit de obiectele concrete din Fazele Prezentatorului Chat.

Acum putem face prezentatorul nostru să se conformeze IChatPresenter

Prezentatorul nostru se ocupă acum de trimiterea mesajului prin apelarea funcțiilor comune în sine și delegă funcțiile care pot fi implementate diferit.

Acum putem oferi Creare obiecte care se conformează fazelor prezentatorului bazate și configurați aceste funcții pe baza nevoilor lor.

Dacă folosim injecția de dependență în controlerul nostru de vedere, putem reutiliza același controler de vedere în două cazuri diferite.

Prin utilizarea modelelor de design, putem simplifica cu adevărat codul nostru iOS. Dacă doriți să aflați mai multe despre asta, articolul următor oferă explicații suplimentare.

Expresiv

Majoritatea costurilor unui proiect software sunt pentru întreținerea pe termen lung. Scrierea codului ușor de citit și de întreținut este o necesitate pentru dezvoltatorii de software.

Putem oferi un cod mai expresiv folosind Naming bun, folosind SRP și testul Writing.

Denumire

Numărul unu lucru care face ca codul să fie mai expresiv - și este denumirea. Este important să scrieți nume care:

  • Dezvălui intenția
  • Evitați dezinformarea
  • Sunt ușor de căutat

Când vine vorba de denumirea de clase și funcții, un truc bun este să folosiți un nume sau o frază de substantiv pentru clase și verbe de utilizator sau nume de fraze verbale pentru metode.

De asemenea, atunci când utilizați diferite modele de design, uneori este bine să adăugați nume de model, cum ar fi Comandă sau Vizitator în numele clasei. Așadar, cititorul ar ști imediat ce model este folosit acolo fără a fi nevoie să citească tot codul pentru a afla despre asta.

Utilizarea SRP

Un alt lucru care face ca expresivitatea codului să fie folosirea principiului responsabilității unice menționat mai sus. Vă puteți exprima păstrând funcțiile și clasele mici și pentru un singur scop. Clasele și funcțiile mici sunt de obicei ușor de denumit, ușor de scris și ușor de înțeles. O funcție ar trebui să servească doar pentru un singur scop.

Test de scriere

Testele de scriere aduc, de asemenea, multă claritate, în special atunci când lucrați cu codul moștenitor. Testele unității bine scrise sunt, de asemenea, expresive. Un obiectiv principal al testelor este de a acționa ca documentație de exemplu. Cineva care citește testele noastre ar trebui să poată înțelege rapid despre ce este vorba despre o clasă.

Minimizați numărul de clase și metode

Funcțiile unei clase trebuie să rămână scurte, o funcție ar trebui să îndeplinească întotdeauna doar un lucru. Dacă o funcție are prea multe linii, aceasta ar putea fi cazul în care execută acțiuni care pot fi separate în două sau mai multe funcții separate.

O abordare bună este să numeri linii fizice și să încerci să urmărești maxim patru-șase linii de funcții, în majoritatea cazurilor orice lucru care depășește acest număr de linii poate deveni greu de citit și de întreținut.

O idee bună în iOS este să tăiați apelurile de configurare pe care le efectuăm de obicei în funcțiile viewDidLoad sau viewDidAppear.

În acest fel, fiecare dintre funcțiile ar fi mică și de întreținut în locul unei funcții viewDidLoad mess. Același lucru ar trebui să se aplice și pentru delegatul aplicației. Ar trebui să evităm să aruncăm fiecare configurație metoda ondidFinishLaunchingWithOptions și funcții de configurare separate sau chiar clase de configurare mai bune.

Cu funcțiile, este puțin mai ușor să măsurăm dacă îl păstrăm lung sau scurt, de cele mai multe ori ne putem baza doar pe numărarea liniilor fizice. Cu orele, folosim o măsură diferită. Contăm responsabilitățile. Dacă o clasă are doar cinci metode, aceasta nu înseamnă că clasa este mică, s-ar putea ca aceasta să aibă prea multe responsabilități doar cu acele metode.

O problemă cunoscută în iOS este dimensiunea mare a UIViewControllers. Este adevărat că, prin designul controlorului de vedere Apple, este greu să păstrăm aceste obiecte pentru a servi un singur scop, dar ar trebui să încercăm tot ce putem.

Există multe modalități de a face UIViewControllers mici preferința mea este să folosesc o arhitectură care are o separare mai bună a preocupărilor, precum VIPER sau MVP, dar asta nu înseamnă că nu o putem îmbunătăți și în Apple MVC.

Încercând să separăm cât mai multe preocupări, putem ajunge la un cod destul de decent cu orice arhitectură. Ideea este de a crea clase cu un singur scop care să poată servi de ajutor controlerelor de vedere și să facă codul mai lizibil și testabil.

Unele lucruri care pot fi evitate pur și simplu fără scuză în controlerele de vedere sunt:

  • În loc să scrieți codul de rețea direct, ar trebui să existe un NetworkManager o clasă care este responsabilă pentru apelurile în rețea
  • În loc să manipuleze datele din controlerele de vizualizare, putem crea pur și simplu un DataManager o clasă care este responsabilă pentru asta.
  • În loc să joci cu șirurile UserDefaults în UIViewController, putem crea o fațadă peste asta.

In concluzie

Cred că ar trebui să compunem software de la componente care sunt numite cu exactitate, simple, mici, responsabile pentru un lucru și reutilizabile.

În acest articol, am discutat patru reguli pentru proiectarea simplă de către Kent Beck și am oferit exemple practice despre cum le putem implementa în mediul de dezvoltare iOS.

Dacă v-a plăcut acest articol, asigurați-vă că aplaudați pentru a vă prezenta sprijinul. Urmați-mă pentru a vizualiza multe alte articole care pot duce abilitățile dvs. pentru dezvoltator iOS la un nivel următor.

Dacă aveți întrebări sau comentarii, nu ezitați să lăsați o notă aici sau trimiteți-mi un e-mail la arlindaliu.dev@gmail.com.