Primerjava načrtovalskih vzorcev programske kode v mobilnih aplikacijah Vid Ribic, MatevZ Pogačnik Univerza v Ljubljani, Fakulteta za elektrotehniko E-posta: ribvid@gmail.com Comparison of design patterns used in the development of mobile applications Abstract. In this paper, we examine some of the more common design patterns used in the development of mobile applications for the iOS. With their help, the application is divided into multiple components, which has certain key advantages. It is easier to reuse user interface (UI) elements and we thus follow the don't repeat yourself (DRY) concept, making the code optimized, more understandable and easier to test. The use of a suitable design pattern quickens development and simplifies bug fixing. We also describe the model-view-controller (MVC) architecture, which seems an excellent starting point for most applications. In recent years, however, certain qualms have arisen regarding its suitability for more demanding programs as the use of MVC requires caution in order to avoid the problem of massive view controller. With the MVP concept, the controller is moved to a part of the view, while man-in-the-middle becomes a new element - the presenter. Also described is the MVVM pattern, where the view owns the view model, as well as VIPER, which divides individual tasks of the application into five minor components. 1 Uvod Pri programiranju že od nekdaj pomembno vlogo igrajo načrtovalski vzorci (angl. design patterns). Z njihovo pomočjo razbijemo programsko kodo na več različnih komponent, vsako s svojo zadolžitvijo. To prinaša nekaj pomembnih prednosti. Modularno napisano kodo je lazje ponovno uporabiti in tako slediti konceptu DRY (angl. don't repeat yourself), s čimer zmanjšamo količino napisane kode in posledično verjetnost napak v njenem delovanju. Pomembna prednost je enostavnejše testiranje, ker ga lahko izvajamo nad vsako komponento posebej. Pri monolitno napisani kodi, ki opravlja več nalog hkrati, je testiranje veliko večji, če sploh rešljiv, izziv. Moznost enostavnega testiranja olajšuje spreminjanje obstoječih delov kode in dodajanje novih funkčionalnosti, prav tako kot boljša preglednost, organiziranost in jasnost, ki jo dosezemo z razbitjem kode na več smiselnih enot. Uporaba arhitekturnih vzorčev rezultira v hitrejsšem razvoju, predvsem pa v manj hroščati in bolje optimizirani kodi. Nič drugače ni pri razvoju programske opreme za mobilne naprave. Sprva so bile mobilne aplikacije enostavne in primitivne, zato so se večinoma uporabljali samo najosnovnejši arhitekturni koncepti, kot je model-pogled-kontroler oziroma MVC (angl. model-view-controller). S kompleksnejšimi in zahtevnejšimi programi so se pojavile tezave tega pristopa in začelo se je razmišljati o vpeljavi novih arhitekturnih zasnov. V razvijalskih krogih se zadnja leta čedalje pogosteje zastavlja vprašanje, ali je MVC danes še vedno najprimernejši načrtovalski vzorec ali pa je za sodobno mobilno aplikačijo preveč enostaven, zato bomo v tem članku na kratko predstavili nekaj arhitekturnih idej, primernih za razvoj mobilnih aplikačij za platformo iOS. 2 MVC Najpreprostejši, najpogostejši in tudi eden izmed najstarejših načrtovalskih vzorčev je model-pogled-kontroler ali MVC. Kratičo je leta 1979 prvič uporabil Trygve Reen-skaug za programe napisane v Smalltalku [1]. Applova različšiča, ki jo predstavljamo v nadaljevanju, je prilagojena posebnostim mobilnih aplikačij. Slika 1: Pri idejni zasnovi MVC je most med pogledom in modelom kontroler. Arhitektura je trodelna. Na eni strani je model, ki je abstrakten opis vsebine naše aplikačije. Model je popolnoma neodvisen od aplikačijskega ogrodja (angl. framework) [1]. Na drugi strani imamo pogled (angl. view), ki predstavlja vidni del aplikačije, uporabniški vmesnik. Pogled in model nista nikoli v neposredni povezavi, s čimer preprečujemo njuno soodvisnost. To je pomembno, saj lahko le tako poglede ponovno uporabimo tudi z drugimi modeli, kar je pri razvoju mobilnih aplikačij dokaj pogosto. Most ERK'2018, Portorož, 104-158 155 med modelom in pogledom je kontroler (angl. controller). Ta dobi informacijo o spremembi modela in ustrezno posodobi pogled. Ter obratno: ko uporabnik interaktira z aplikacijo in izvede določeno akcijo, kot je pritisk na gumb, lahko kontroler posodobi model, ce je to seveda potrebno [2]. Po drugi strani pa to pomeni, da ima kontroler veliko zadolžitev in prav to je kljucna teZava ter kritika te arhitekturne zasnove. Pri kompleksnih aplikacijah se namrec hitro zgodi, da postane kontroler zelo obsezen, cemur se, kot zapisano v uvodu, skušamo izogniti. Pogled v dokumentacijo razkrije, kakšne zadolzitve lahko dodelimo kontrolerju (izpeljemo ga iz razreda UIViewController) [3]. Med njimi so skrb za pravilen prikaz pogleda, nadzor nad njegovim zivljenskim ciklom, spremljanje morebitnih rotacij zaslona, zaznava in ustrezna obdelava uporabnikovih akcij, lahko se povezuje s streznikom in obdeluje odgovore, ki mu jih ta vrne, posluša obvestila (angl. notifications) in tako dalje. Seveda vseh funkcij ni potrebno implementirati, ce niso potrebne, kljub temu pa se moramo zavedati, da se lahko hitro ujamemo v past preobsezne kode. Tisti, ki trdijo, da gre za prezivet koncept, prav zaradi preobseznosti kontrolerja MVC ironicno pojasnujejo kot "massive view controller". Delno rešitev te tezave predstavlja koncept kontrolerjev otrokov (angl. child controllers), na katere lahko prenesemo kakšno izmed nalog in jih nato po potrebi vkljucujemo v druge kontrolerje. Primer take uporabe bi bila locena kontrolerja za obravnavo nalaganja vsebine in prikaz napake, kiju nato lahko vkljucimo v vse tiste kontrolerje, ki obdelujejo strezniške odgovore [4]. Z uporabo kontrolerjev otrokov se lahko izognemo nevarnosti masivnega kontrolerja, obenem pa jih lahko uporabimo na vec razlicnih mestih in s tem sledimo ideji DRY. Druga tezava kontrolerja, ki je povezan s pogledom in modelom, je otezšeno testiranje poslovne logike (angl. business logic). Testiranje sicer je mogocše, vendar zahteva nekaj vecš dela in truda. Kljub temu MVC še vedno ostaja pogosta arhitekturna izbira. Prvi razlog je njegova enostavnost - MVC-ja se je najlazje in najhitreje nauciti, hkrati pa v aplikacijo ne vnasša preveliko nepotrebne kode (angl. overheada). MVC je arhitektura, ki jo priporoca in v svojih aplikacijah uporablja Apple. Marsikdo, ki zagovarja uporabo MVC-ja, je prav zaradi tega preprican, daje aplikacijsko ogrodje napisano z mislijo na to zasnovo (ali vsaj njene neposredne izpeljanke) in so vsi elementi, potrebni za razvoj, ze del obstojecših knjizšnic. (Četudi se nam zdijo drugi nacrtovalski vzorci primernejši od MVC-ja, pa ta velja za osnovo vecini ostalih idej. Marsikakšna izmed njih se pravzaprav zdi zelo podobna MVC-ju, zaradi cesar se nemalokdo sprašuje, ali ne gre le za drugacen nacin implementacije istega vzorca in drugacšno ime (MV-karkoli, angl. MV-whatever). Masiven kontroler za zagovornike MVC-ja ni odraz slabosti tega nacšrtovalskega vzorca, pacš pa programerja, ki ga ne zna pravilno implementirati [5]. 3 MVP Eden izmed vzorcev, izpeljan iz MVC-ja, je model-pogled-prikazovalnik ali MVP (angl. model-view-presenter). Leta 1996 gaje zasnoval Mike Potel [6]. Ker vsevedni kontroler vodi v obsezno kodo in tezavno testiranje, se pri tej zasnovi pomakne v del pogleda; gledano z vidika razredov lahko recemo, daje poleg razreda UIView sedaj del pogleda tudi UIViewController. Ta sicer še vedno skrbi za UIView, vendar nima vec neposredne povezave z modelom, s cimer se izognemo številnim tezavam. Prikazovalnik zasede vlogo mozša v sredini med modelom in pogledom. Pogled delegira uporabnikove interakcije prikazovalniku, ki vsebuje logiko za njihovo obravnavo. Hkrati je tudi tisti, ki prevzame komunikacijo z modelom, podatke, ki jih od njega prejema, ustrezno pripravi in nato pošlje naprej UIViewControllerju. Prikazovalnik je prav tako kot model neodvisen od aplikacijskega ogrodja UIKit [7]. UIViewController na ta nacin razbremenimo številnih nalog, hkrati pa tako kot UIView postane nevedni cšlen arhitekture. S tem ne dosezemo le manj obsezne kode, temvec tudi olajšujemo testiranje, saj lahko sedaj poslovno logiko aplikacije stestiramo neodvisno od UIViewControllerja. Po drugi strani pa MVP prinese nekaj dodatnega dela, saj je potrebno vpeljati nov element, torej prikazovalnik, in ga povezati z ostalimi cleni arhitekture. Slika 2: Pri MVP se kontroler pomakne v del pogleda, moz v sredini pa postane prikazovalnik. 4 MVVM Pri nacšrtovalski zasnovi MVP ima pogled referenco na prikazovalnik in obratno. Cš e zšelimo prikazovalnik razbremeniti povezave s pogledom, lahko uporabimo vzorec model-pogled-pogled model ali MVVM (angl. modelview-view model). Izvorno gaje predstavil Microsoft leta 2005, da bi poenostavil dogodkovno usmerjeno programiranje (angl. event-driven programming) [2]. Se vedno ostaja ideja, da pogled predstavlja vidni del aplikacije in nima moznosti neposredne komunikacije z modelom. Kljucna razlika pa je sledeca: pogled si lasti nov nivo, poimenovan pogled model, ta pa si lasti model. Pogled model je zadolzšen za hrambo podatkov za pogled, ki si ga lasti. Za primer vzemimo aplikacijo za predvajanje glasbe. Pogled bo narisal seznam skladb, pogled model pa bo hranil informacije o njih (naslov, izvajalca, zanr, dolzšino, ...). 105 Slika 3: Med pogledom in pogledom model vzpostavimo pove zavo (angl. binding). Ker iOS nima nativnega pristopa k temu. se veliko razvijalcev odloči za vpeljavo reaktivnosti. Tok aplikacije je pri MVVM nekoliko drugačen. Postavi se vprašanje, kateri nivo je zadolžen za pridobitev podatkov s strežnika ali baze? Povedano skozi gledišče zgornjega primera: kateri člen arhitekture bo s streznika pridobil seznam skladb? Pri MVC in MVP sta za to za-dolzena kontroler oziroma prikazovalnik, ki nato uporabita model in posodobita pogled. Pri MVVM pa za povezavo s streznikom oziroma bazo skrbi pogled. Ta pridobi podatke in jih posreduje nivoju pogled model, ki jih sprocesira in pripravi v ustrezni obliki za uporabniški vmesnik ter vrne nazaj pogledu, s katerim je povezan (angl. binding). Tezava je, da pri iOS ni pravega in nativnega pristopa za tovrstno povezovanje. Obstajajo sicer mehanizmi, kot so KVO (key-value observing), delegati ali notifikacije, vendar niso tako ucinkoviti kot povezovalni mehanizmi pri drugih programskih jezikih. Osnovno aplikacijsko ogrodje ni bilo napisano z mislijo na to nacrtovalsko zasnovo. Razvijalci se zato koncepta MVVM najpogosteje lotevajo z reaktivnim pristopom, ceprav ta seveda ni nujen. MVVM sše vedno skrbi za razdelitev nalog med razlicšne nivoje, reaktivnost pa za povezovanje (binding). Ceprav sta pogled in pogled model med seboj tesno povezana, pa je pogled še vedno locen od modela, s cimer se ohranja osnovna ideja MVC-ja. Hkrati je pri MVVM poslovna logika locšena od kontrolerja in s tem ostaja enostavnost testiranja, ki smo jo vpeljali z MVP-jem. Po drugi strani pa z uvajanjem MVVM-ja prihaja do novih izzivov. Prvo vprasšanje, ki bega razvijalce, je razdelitev nalog. Kar nekoliko nenavadno se zdi, daje pogled tisti, ki je zadolzen za povezavo s streznikom ali bazo, vendar tako narekuje izvorna ideja MVVM-ja. Morda bi bila ta naloga primernejša za pogled model? Primer uporabne vrednosti tega, daje pogled zadolzen za pridobivanje podatkov, je to, da sam ve, kdaj so podatki uspesšno preneseni in kdaj je prišlo do napake. Ce bi to nalogo preselili na nivo pogled model, bi to pomenilo, da moramo dodati sše en nivo abstrakcije, za primer, ko podatkov ni in pogled model vracša pogledu napako. Druga tezava MVVM-ja pri iOS-u je manjko nativ-nega pristopa k povezovanju. Reaktivnost se slisši kot dobra resšitev, vendar je kompleksna, zahteva vecš ucšenja in posledicšno bolj nagiba nasšo kodo k mozšnosti napak. Zavedajoc se prednosti in slabosti tega koncepta, lahko sklenemo, da je MVVM zanimiva in premisleka vredna i /K User 1 action Update Presenter y_ Data X source _ Send _v events 7 Router I * Ask Notify 4, I Interactor Notify- -Manage—^ Entity Slika 4: Viper razdeli aplikacijo na pet delov. Ceprav v teoriji dobro razdeli zadolzitve in olajša testiranje, se v praksi izkaze, daje tovrstna abstrakcija nemalokrat prevelika. Pogled ostane zadolzen za prikaz uporabniškega vmesnika, uporabnikove akcije pa pošlje prikazovalniku. Ta je odgovoren za njihovo obdelavo in je popolnoma neodvisen od uporabniškega vmesnika. Tretji clen je interaktor. Vsebuje vso poslovno logiko, hkrati ima v lasti in upravljanju entiteto (angl. entity), kije cetrti nivo te zasnove, namenjen definiranju strukture za shranjevanje podatkov. Zadnja komponenta je usmerjevalnik (angl. router), ki skrbi za navigacijo na podlagi akcij, prejetih iz prikazovalnika [8]. VIPER razdeli zadolzitve aplikacije na vec clenov, s cimer še dodatno poenostavi testiranje. Po drugi strani pa uvedba toliko novih razredov aplikacijo zelo razdrobi in podaljšuje ucenje ter razumevanje zgradbe in arhitekture programa. Zavedati se moramo tudi velike razlike med osnovno zasnovo MVC in VIPER. Se je res smiselno toliko oddaljiti od koncepta, ki ga zagovarja tisti, ki skrbi za aplikacijsko ogrodje? 6 Sklep Poleg predstavljenih obstaja sše mnogo drugih nacšrtovalskih zasnov (Redux, Cake, MVC+VS, MAVB, TEA, ...). Postavlja se vprašanje, katera je najprimernejša? Bistveno je zavedanje, daje namen vsake arhitekture programerju olajšati delo, pohitriti programiranje, odpravljanje napak in dodajanje novih funkcionalnosti, poenostaviti testiranje ter zmanjšati obseg kode. Ce VIPER ponuja nadrobno razdelitev nalog med posameznimi deli, kar ima nedvomno svoje prednosti, pa je bistveno bolj kompliciran od MVC-ja. Obstaja pomislek, da se arhitekture, kot je VIPER, na papirju zdijo dobra rešitev, v praksi pa prinašajo prevec abstrakcije in posledicno zmede. Kljucna tezava 106 MVC-ja je nevarnost preobsežnega kontrolerja, vendar to ne sme biti razlog, da se tej idejni zasnovi prehitro odpovemo. Obseg kontrolerja lahko zmanjšamo. Data source in delegate za collection view ali table view, ki sta pogosta elementa uporabniškega vmesnika, lahko locimo od UIViewControllerja. Iz kontrolerja lahko vselej prestavimo mrezne klice in obdelavo datotek JSON, s cimer med drugim poenostavimo testiranje. Omenili smo tudi kontrolerje otroke, ki jih vključujemo, kjer jih potrebujemo. Tezavo pretoka podatkov med posameznimi cleni aplikacije lahko ucinkovito rešimo z uporabo koordinatorjev. Upoštevajoc moznosti za razbremenitev kontrolerja in izogib njegove preobseznosti, ugotovimo, da ima vendarle MVC še vedno široko uporabno vrednost. Ker je MVC najenostavnejši in Applov izbrani nacrtovalski vzorec za platformo iOS, je najbolje, da se mu odpovemo šele takrat, ko predstavljajo njegove omejitve nepremostljivo oviro. Literatura [1] C. Eidhof, M. Gallagher, in F. Kugler, App Architecture, iOS Application Design Patterns in Swift. CreateSpace Independent Publishing Platform, 2018. [2] A. Harbade, iOS design patterns, iOS design patterns — Part 1 (MVC, MVP, MVVM). Dostopno na: https://medium.com/swlh/ios-design-patterns-a9bd07818129. [3] Apple, Documentation, UIKit, View Controllers, UIViewController. Dostopno na: ht-tps://developer.apple.com/documentation/uikit/uiviewcontroller. [4] J. Sundell, Using child view controllers as plugins in Swift. Dostopno na: https://www.swiftbysundell.com/posts/using-child-view-controllers-as-plugins-in-swift. [5] A. Vacic, Much ado about iOS app architecture. Dostopno na: http://aplus.rs/2017/much-ado-about-ios-app-architecture/. [6] M. Potel, MVP: Model-View-Presenter The Taligent Programming Model for C++ and Java. Taligent, 1996. [7] I. Agha, A dumb UI is a good UI: Using MVP in iOS with swift. Dostopno na: http://iyadagha.com/using-mvp-ios-swift/. [8] A. Harbade, iOS design patterns — Part 2 (VIPER). Dostopno na: https://medium.com/@anup.harbade.iosdev/ios-design-patterns-part-2-viper-fa3522b22b6b. 107