IOS-ohjelmistosuunnittelun neljä sääntöä

1990-luvun lopulla kehitettäessä Extreme-ohjelmointia kuuluisa ohjelmistokehittäjä Kent Beck keksi luettelon yksinkertaisen ohjelmistojen suunnittelusta.

Kent Beckin mukaan hyvä ohjelmistosuunnittelu:

  • Suorittaa kaikki testit
  • Ei sisällä päällekkäisyyksiä
  • Ilmaisee ohjelmoijan aikomuksen
  • Minimoi luokkien ja menetelmien lukumäärän

Tässä artikkelissa keskustelemme siitä, kuinka näitä sääntöjä voidaan soveltaa iOS-kehitysmaailmaan antamalla käytännöllisiä iOS-esimerkkejä ja keskustelemalla siitä, miten voimme hyötyä niistä.

Suorittaa kaikki testit

Ohjelmistosuunnittelu auttaa meitä luomaan järjestelmän, joka toimii tarkoitetulla tavalla. Mutta kuinka voimme varmistaa, että järjestelmä toimii suunnitellulla tavalla alun perin? Vastaus on luomalla testit, jotka validoivat sen.

Valitettavasti iOS-kehitystyössä maailmankaikkeuden testejä vältetään useimmiten ... Mutta hyvin suunnitellun ohjelmiston luomiseksi meidän on aina kirjoitettava Swift-koodi testattavuuden mielessä.

Keskustelemme kahdesta periaatteesta, jotka voivat tehdä testien kirjoittamisesta ja järjestelmän suunnittelusta yksinkertaisempaa. Ja ne ovat yhden vastuun periaate ja riippuvuusinjektio.

Yksittäisen vastuun periaate (SRP)

SRP: n mukaan luokalla tulisi olla yksi ja vain yksi syy muutokseen. SRP on yksi yksinkertaisimmista periaatteista ja yksi vaikeimmista saada oikein. Vastuun sekoittaminen on asia, jota teemme luonnollisesti.

Annetaan esimerkki jostain koodista, jota on todella vaikea testata, ja refaktorin jälkeen se SRP: n avulla. Keskustele sitten kuinka se teki koodista testattavan.

Oletetaan, että meidän on tällä hetkellä esitettävä PaymentViewController nykyisestä näkymäohjaimestamme, PaymentViewControllerin on määritettävä näkymä riippuen maksutuotteen hinnasta. Tapauksessamme hinta on muuttuva riippuen joistakin ulkoisista käyttäjän tapahtumista.

Tämän toteutuksen koodi näyttää tällä hetkellä seuraavalta:

Kuinka voimme testata tämän koodin? Mitä meidän pitäisi ensin testata? Lasketaanko hinnanalennus oikein? Kuinka voimme pilkata maksutapahtumia testataksesi alennusta?

Testien kirjoittaminen tälle luokalle olisi monimutkaista, meidän pitäisi löytää parempi tapa kirjoittaa se. No, ensin käsitellään iso ongelma. Meidän on purettava riippuvuutemme.

Näemme, että meillä on logiikkaa tuotteemme lataamiseen. Meillä on maksutapahtumia, joiden avulla käyttäjä voi saada alennuksen. Meillä on alennuksia, alennuslaskelma ja luettelo jatkuu.

Yritetään siis kääntää nämä yksinkertaisesti Swift-koodiksi.

Loimme PaymentManager-sovelluksen, joka hallinnoi maksuihin liittyvää logiikkaamme, ja erillisen hintalaskurin, joka on helposti testattavissa. Lisäksi tiedonsiirtolaite, joka vastaa verkko- tai tietokantavuorovaikutuksesta tuotteidemme lataamiseen.

Mainitsimme myös, että tarvitsemme luokkaa, joka vastaa alennusten hallinnasta. Kutsumme sitä CouponManageriksi ja anna sen myös hallita käyttäjän alennuskuponkeja.

Maksunäkymämme ohjain voi sitten näyttää seuraavalta:

Voimme kirjoittaa nyt testejä kuten

  • testCalculatingFinalPriceWithoutCoupon
  • testCalculatingFinalPriceWithCoupon
  • testCouponExists

ja monet muut! Luomalla nyt erillisiä objekteja vältetään tarpeettomia päällekkäisyyksiä ja loimme myös koodin, jolle on helppo kirjoittaa testejä.

Riippuvuusinjektio

Toinen periaate on riippuvuusinjektio. Ja näimme yllä olevista esimerkeistä, että olemme jo käyttäneet riippuvuusinjektiota objektin alustuslaitteisiin.

Kuten yllä, riippuvuussuhteiden pistämisellä on kaksi suurta hyötyä. Se tekee selväksi, mihin riippuvuuksiin tyyppimme luottavat, ja se antaa meille mahdollisuuden lisätä mock-objekteja, kun haluamme testata todellisten sijasta.

Hyvä tekniikka on luoda protokollit kohteillemme ja tarjota konkreettinen toteutus oikeassa ja mallisessa objektissa kuten seuraava:

Nyt voimme helposti päättää, minkä luokan haluamme injektoida riippuvuutena.

Tiukka kytkentä vaikeuttaa testien kirjoittamista. Joten samoin, mitä enemmän testejä kirjoitamme, sitä enemmän käytämme periaatteita, kuten DIP, ja työkaluja, kuten riippuvuuden injektio, rajapinnat ja abstraktio minimoimaan kytkentä.

Koodin testattavuuden lisääminen ei pelkästään poista pelkäämme sitä rikkoa (koska kirjoitamme testin, joka tukee meitä), mutta myös osaltaan puhtaan koodin kirjoittamiseen.

Tämä artikkelin osa oli enemmän huolissaan siitä, kuinka kirjoittaa testattava koodi kuin todellisen yksikkötestin kirjoittaminen. Jos haluat lisätietoja yksikkötestin kirjoittamisesta, voit tutustua tähän artikkeliin, jossa luon elämäpelin testilähtöisen kehityksen avulla.

Ei sisällä päällekkäisyyksiä

Kopiointi on hyvin suunnitellun järjestelmän ensisijainen vihollinen. Se edustaa lisätyötä, lisää riskiä, ​​lisää tarpeetonta monimutkaisuutta.

Tässä osassa keskustelemme siitä, kuinka voimme käyttää mallipohjakuviota iOS: n yleisten päällekkäisyyksien poistamiseen. Jotta ymmärrystä olisi helpompaa, aiomme reagoida tosielämän keskustelun toteuttamiseen.

Oletetaan, että meillä on tällä hetkellä sovelluksessamme tavallinen chat-osa. Uusi vaatimus tulee esiin, ja nyt haluamme ottaa käyttöön uuden tyyppisen chatin - live-chatin. Keskustelu, jonka tulisi sisältää viestejä, joissa on enintään 20 merkkiä, ja tämä chatti katoaa, kun hylkäämme keskustelunäkymän.

Tällä keskustelulla on samat näkymät kuin nykyisellä chatilla, mutta sillä on muutama erilainen sääntö:

  1. Verkkopyynnöt chat-viestien lähettämiseksi ovat erilaiset.

2. Chat-viestien on oltava lyhyitä, korkeintaan 20 merkkiä viestiä varten.

3. Keskusteluviestejä ei tule säilyttää paikallisessa tietokannassamme.

Oletetaan, että käytämme MVP-arkkitehtuuria ja käsittelemme tällä hetkellä logiikkaa chat-viestien lähettämiseen esittelijässämme. Yritetään lisätä uusia sääntöjä uudelle chat-tyypillemme nimeltä live-chat.

Naiivi toteutus olisi seuraava:

Mutta mitä tapahtuu, jos tulevaisuudessa meillä on paljon enemmän chat-tyyppejä?
Jos jatkamme lisäämistä, jos vielä tarkistamme keskustelun tilan jokaisessa toiminnossa, koodista tulee sotkuinen vaikea lukea ja ylläpitää. Se on myös tuskin testattavissa ja tilan tarkistaminen olisi päällekkäistä koko juontajan laajuudessa.

Täällä mallipohja otetaan käyttöön. Mallikuvaa käytetään, kun tarvitsemme useita algoritmin toteutuksia. Malli määritetään ja rakennetaan sitten muilla muunnelmilla. Käytä tätä menetelmää, kun useimpien alaluokkien on toteutettava sama käyttäytyminen.

Voimme luoda protokollan Chat Presenter -sovellukselle ja eromme menetelmät, jotka Chat Presenter Phase -alueiden konkreettiset objektit toteuttavat eri tavalla.

Voimme nyt saada esittelijämme noudattamaan IChatPresenter -sovellusta

Presenterimme hoitaa nyt viestin lähettämisen kutsumalla sisäisiä yhteisiä toimintoja ja delegoimalla toiminnot, jotka voidaan toteuttaa eri tavalla.

Nyt voimme tarjota Luo esineitä, jotka vastaavat juontajavaiheiden perusteita, ja määritä nämä toiminnot tarpeidensa perusteella.

Jos käytämme riippuvuusinjektiota näyttöohjaimessa, voimme nyt käyttää samaa näkymäohjainta kahdessa eri tapauksessa.

Suunnittelumallien avulla voimme todella yksinkertaistaa iOS-koodiamme. Jos haluat tietää enemmän siitä, seuraava artikkeli antaa lisätietoja.

Ilmeikäs

Suurin osa ohjelmistoprojektin kustannuksista on pitkäaikaisessa kunnossapidossa. Koodin kirjoittaminen on helppolukuista ja ylläpidettävää ohjelmistokehittäjille.

Voimme tarjota ekspressiivisemmän koodin käyttämällä hyvää nimeämistä, SRP: tä ja kirjoitustestiä.

nimeäminen

Numero yksi asia, joka tekee koodista ilmaisullisemman - ja se on nimeäminen. On tärkeää kirjoittaa nimiä, jotka:

  • Paljasta tarkoitus
  • Vältä vääriä tietoja
  • Ovat helposti haettavissa

Luokkien ja funktioiden nimeämisessä hyvä temppu on käyttää substantiivi- tai substanssi-lauseita luokille ja käyttäjän verbejä tai verbi-lauseiden nimiä menetelmille.

Jos käytät erilaisia ​​suunnittelumalleja, on joskus hyvä lisätä mallinimet, kuten komento tai vierailija, luokanimeen. Joten lukija tietäisi heti, mitä mallia siellä käytetään, ilman että sinun on luettava kaikki koodit saadaksesi selville.

SRP: n käyttö

Toinen asia, joka tekee koodista ilmeikkään, on yllä mainitun yhden vastuuperiaatteen käyttäminen. Voit ilmaista itseäsi pitämällä toiminnot ja luokat pieninä ja yhdeksi tarkoitukseksi. Pienet luokat ja toiminnot ovat yleensä helppo nimetä, helppo kirjoittaa ja helppo ymmärtää. Toiminnon tulisi toimia vain yhdessä tarkoituksessa.

Kirjallinen testi

Testien kirjoittaminen tuo myös paljon selkeyttä, varsinkin kun työskentelet vanhalla koodilla. Hyvin kirjoitetut yksikkötestit ovat myös ilmeikkäitä. Testien päätavoite on toimia dokumentointina esimerkillä. Testien lukemisen joku voi pystyä saamaan nopeasti käsityksen luokan tarkoituksesta.

Minimoi luokkien ja menetelmien lukumäärä

Luokan toimintojen on oltava lyhyitä, toiminnon tulisi aina suorittaa vain yksi asia. Jos toiminnolla on liian monta riviä, voi olla, että se suorittaa toimintoja, jotka voidaan jakaa kahteen tai useampaan erilliseen funktioon.

Hyvä tapa on laskea fyysiset rivit ja yrittää tavoitella enintään neljästä kuuteen riville funktioita, useimmiten kaikesta, joka ylittää sen määrän rivejä, voi olla vaikea lukea ja ylläpitää.

Hyvä idea iOS: ssä on pilkkoa määrityspuhelut, joita teemme yleensä viewDidLoad- tai viewDidAppear-toiminnoissa.

Tällä tavalla kukin toiminnoista olisi pieni ja ylläpidettävissä yhden sotkua viewDidLoad-toiminnon sijasta. Samaa pitäisi soveltaa myös sovellusvaltuutettuun. Meidän ei tulisi heittää kaikkia määrityksiä ondidFinishLaunchingWithOptions -menetelmään ja erillisiin kokoonpanotoimintoihin tai jopa parempiin kokoonpanoluokkiin.

Toimintojen avulla on vähän helpompaa mitata pitämmekö sitä pitkänä vai lyhyenä. Useimmiten voimme luottaa vain fyysisten viivojen laskemiseen. Tunnilla käytämme erilaista mittaa. Luotamme vastuisiin. Jos luokalla on vain viisi menetelmää, se ei tarkoita, että luokka on pieni, voi olla, että sillä on liian paljon vastuuta vain näillä menetelmillä.

Tunnettu ongelma iOS: ssä on suuri UIViewControllers. On totta, että omenanäkymäohjaimen suunnittelulla on vaikea pitää näitä esineitä palvelemaan yhtä tarkoitusta, mutta meidän pitäisi yrittää parhaamme.

On monia tapoja tehdä UIViewControllers pieneksi. Mieluummin käytän arkkitehtuuria, jolla on paremmat erottelut esimerkiksi VIPER: stä tai MVP: stä, mutta se ei tarkoita, että emme voi tehdä sitä paremmin myös Apple MVC: ssä.

Yrittämällä erottaa niin monta huolenaihetta, voimme saavuttaa melko kunnollisen koodin millä tahansa arkkitehtuurilla. Ajatuksena on luoda yhden käyttötarkoituksen luokkia, jotka voivat toimia näytönohjaimien auttajina ja tehdä koodista luettavamman ja testattavamman.

Jotkut asiat, jotka voidaan yksinkertaisesti välttää ilman tekosyytä näyttöohjaimiin, ovat:

  • Verkkokoodin kirjoittamisen sijasta pitäisi olla NetworkManager-luokka, joka vastaa verkkopuheluista
  • Sen sijaan, että manipuloisit tietoja näyttöohjaimissa, voimme yksinkertaisesti luoda DataManager-luokan, joka vastaa siitä.
  • Sen sijaan, että pelaamme UserDefaults-merkkijonoilla UIViewControllerissa, voimme luoda julkisivun siitä.

Tiivistettynä

Uskon, että meidän pitäisi luoda ohjelmistoja komponenteista, jotka on nimetty tarkasti, yksinkertaiset, pienet, vastuussa yhdestä asiasta ja uudelleenkäytettävissä.

Tässä artikkelissa keskustelimme neljästä Kent Beckin yksinkertaisen suunnittelun säännöstä ja annoimme käytännön esimerkkejä siitä, kuinka voimme toteuttaa ne iOS-kehitysympäristössä.

Jos pidit tästä artikkelista, varmista, että taputat osoittaaksesi tukeasi. Seuraa minua nähdäksesi monia muita artikkeleita, jotka voivat viedä iOS-kehittäjätaitosi seuraavalle tasolle.

Jos sinulla on kysyttävää tai kommentteja, jätä muistiinpano tähän tai lähetä meille sähköpostia osoitteeseen arlindaliu.dev@gmail.com.