Prototipizzazione e generazione automatica della ...
-
Upload
khangminh22 -
Category
Documents
-
view
0 -
download
0
Transcript of Prototipizzazione e generazione automatica della ...
I
POLITECNICO DI MILANO
Scuola di Ingegneria Industriale e dell’Informazione
COMPUTER SCIENCE AND ENGINEERING
A.A. 2017 / 2018
Prototipizzazione e generazione automatica
della componente Controller
per applicazioni mobili cross - platform
Relatore:
LUCIANO BARESI
Tesi di:
ALBERTO OSIO
[email protected] – 873487
II
Indice
INDICE DELLE FIGURE ................................................................................................... IV
INDICE DELLE TABELLE ................................................................................................... V
SOMMARIO .............................................................................................................. IX
ABSTRACT ................................................................................................................. X
1 INTRODUZIONE ..................................................................................................... 1
2 CONTESTO GENERALE ............................................................................................. 5
2.1 Stato dell’arte ......................................................................................................................... 5
2.1.1 Strumenti di prototipizzazione .................................................................................................. 5
2.1.2 Framework per applicazioni cross-platform .............................................................................. 8
2.1.3 Ambienti di sviluppo ................................................................................................................ 11
2.2 Presentazione della soluzione ................................................................................................ 14
2.3 Struttura del documento ....................................................................................................... 15
3 MODELLO DELL’APPLICAZIONE ................................................................................ 16
3.1 Modello della View ............................................................................................................... 16
3.1.1 Struttura generale ................................................................................................................... 16
3.1.2 Modellazione astratta dei controlli utente .............................................................................. 19
3.1.3 Modellazione dei controlli con interazione unidirezionale con l’utente ................................. 25
3.1.4 Modellazione dei controlli multimediali .................................................................................. 27
3.1.5 Modellazione di controlli che permettono l’inserimento di informazioni .............................. 30
3.1.6 Modellazione di controlli con ruoli di navigazione .................................................................. 31
3.1.7 Modellazione di altri elementi................................................................................................. 36
3.2 Modello del Model ................................................................................................................ 38
3.2.1 Archiviazione tramite database relazionale ............................................................................ 39
3.2.2 Archiviazione tramite files ....................................................................................................... 40
3.2.3 Archiviazione tramite SharedPreferences ............................................................................... 41
3.2.4 Archiviazione tramite database cloud ..................................................................................... 41
3.3 Modello del Controller .......................................................................................................... 42
3.3.1 AdapterBinding ........................................................................................................................ 44
3.3.2 ModelConnector ...................................................................................................................... 46
4 PROTOCODE ...................................................................................................... 49
4.1 Introduzione ......................................................................................................................... 49
4.2 Software e framework utilizzati ............................................................................................. 52
III
4.2.1 ReactJS ..................................................................................................................................... 52
4.2.2 Yarn .......................................................................................................................................... 52
4.2.3 Redux e Redux-persist ............................................................................................................. 53
4.2.4 Bootstrap ................................................................................................................................. 53
4.2.5 Altre librerie ............................................................................................................................. 53
4.3 Architettura dell’applicazione ................................................................................................ 55
4.3.1 Architettura software .............................................................................................................. 55
4.3.2 Design dei dati ......................................................................................................................... 59
4.3.3 Connessione tra dati e vista .................................................................................................... 82
4.3.4 Design della navigazione ....................................................................................................... 101
4.3.5 Design dell’interfaccia utente ................................................................................................ 103
4.4 Output della prototipazione ................................................................................................ 106
4.5 Struttura del codice sorgente ............................................................................................... 106
5 MOBILECODEGENERATOR ................................................................................... 107
5.1 Introduzione ....................................................................................................................... 107
5.2 Software e framework utilizzati ........................................................................................... 107
5.2.1 Eclipse Modeling Framework ................................................................................................ 108
5.2.2 OpenArchitectureWare ......................................................................................................... 108
5.3 Struttura del generatore ...................................................................................................... 109
5.3.1 Struttura preesistente ........................................................................................................... 109
5.3.2 Design della generazione di applicazioni cross–platform ..................................................... 110
5.3.3 Generazione del codice ......................................................................................................... 116
6 VALUTAZIONE .................................................................................................. 125
6.1 Presentazione del caso di test .............................................................................................. 125
6.2 Produzione dell’applicazione ............................................................................................... 127
6.3 Analisi dell’applicazione generata ........................................................................................ 128
6.3.1 Analisi del tempo impiegato .................................................................................................. 128
6.3.2 Analisi quantitativa della generazione del codice ................................................................. 129
6.3.3 Analisi qualitativa del codice generato .................................................................................. 129
6.3.4 Limitazioni .............................................................................................................................. 130
7 CONCLUSIONI ................................................................................................... 132
7.1.1 Sviluppi futuri ........................................................................................................................ 133
8 BIBLIOGRAFIA ................................................................................................... 135
IV
Indice delle figure Figura 1.1 Ampiezza e penetrazione del mercato mobile nel mondo [1] ......................................................... 1
Figura 1.2 Crescita annuale del mercato mobile [1].......................................................................................... 1
Figura 1.3 Rapporto tra utenti e connessioni mobile [1] .................................................................................. 1
Figura 1.4 Guadagno del mercato mobile [1] .................................................................................................... 2
Figura 1.5 Vendite di smartphone per produttore in migliaia di unità nel secondo trimestre 2018 [2] .......... 2
Figura 1.6 Vendite di smartphone per sistema operativo in migliaia di unità nel secondo trimestre 2018 [2] 3
Figura 2.1 Interfaccia grafica di Proto.io ........................................................................................................... 5
Figura 2.2 Presentazione dell'interfaccia grafica di MarvelApp ........................................................................ 6
Figura 2.3 Interfaccia di Adobe Experience Design CC ...................................................................................... 7
Figura 2.4 Interfaccia grafica di DecoIDE ......................................................................................................... 11
Figura 3.1 Modello degli elementi di alto livello della componente View ...................................................... 17
Figura 3.2 Modello astratto dei controlli utente ............................................................................................. 19
Figura 3.3 Modello dei componenti con interazione unidirezionale con l’utente .......................................... 26
Figura 3.4 Modellazione dei controlli multimediali ......................................................................................... 28
Figura 3.5 Modellazione dei controlli con interazione bidirezionale con l'utente .......................................... 30
Figura 3.6 Modellazione dei controlli relativi alla navigazione ....................................................................... 32
Figura 3.7 Modellazione di elementi aggiuntivi della vista ............................................................................. 37
Figura 3.8 Modello della componente Model ................................................................................................. 39
Figura 3.9 Modellazione del Controller ........................................................................................................... 44
Figura 4.1 Schermata principale di Protocode ................................................................................................ 49
Figura 4.2 Schermata dedicata alle connessioni tra modello e vista .............................................................. 51
Figura 4.3 Esempio di output di Protocode ..................................................................................................... 51
Figura 4.4 Architettura di alto livello di Protocode ......................................................................................... 55
Figura 4.5 Navigazione di alto livello ............................................................................................................. 101
Figura 4.6 Navigazione dell'editor dedicato alla View .................................................................................. 101
Figura 4.7 Navigazione dell'editor dedicato al model ................................................................................... 102
Figura 4.8 Interfaccia utente per l'editor del modello .................................................................................. 103
Figura 4.9 Interfaccia utente per l'editor della View ..................................................................................... 104
Figura 4.10 Interfaccia utente per il scene model editor .............................................................................. 105
Figura 4.11 Organizzazione del codice di Protocode ..................................................................................... 106
Figura 5.1 Architettura di riferimento per l'applicazione generata .............................................................. 111
Figura 5.2 Utilizzo di un thunk nelle interazioni con la persistenza .............................................................. 116
Figura 5.3 Esempio di vincoli in un ViewController ....................................................................................... 120
Figura 5.4 Struttura di una scena di tipo SINGLE_VC .................................................................................... 122
Figura 5.5 Struttura di una scena di tipo SINGLE_VC_TAB ............................................................................ 122
Figura 5.6 Struttura di una scena di tipo SINGLE_VC .................................................................................... 122
Figura 6.1 Analisi del tempo impiegato per lo sviluppo dell'app di valutazione ........................................... 128
V
Indice delle tabelle Tabella 3.1 Campi di Application ..................................................................................................................... 17
Tabella 3.2 Campi di scene .............................................................................................................................. 18
Tabella 3.3 Campi di ViewController ............................................................................................................... 19
Tabella 3.4 Campi di UiControl ........................................................................................................................ 20
Tabella 3.5 Campi di UiPhoneControl .............................................................................................................. 21
Tabella 3.6 Campi di DimensionConstraint ..................................................................................................... 21
Tabella 3.7 Possibili combinazioni di vincoli di dimensione ............................................................................ 22
Tabella 3.8 Relazioni possibili tra elementi rilevanti ....................................................................................... 22
Tabella 3.9 Coesistenza di vincoli .................................................................................................................... 23
Tabella 3.10 Campi di PositionConstraint ....................................................................................................... 23
Tabella 3.11 Campi di ControlChain ................................................................................................................ 25
Tabella 3.12 Estensione di ViewController ai controlli con interazione unidirezionale .................................. 26
Tabella 3.13 Campi di TextView ...................................................................................................................... 27
Tabella 3.14 Campi di Map .............................................................................................................................. 27
Tabella 3.15 Campi di Card .............................................................................................................................. 27
Tabella 3.16 Estensione di ViewController ai controlli multimediali .............................................................. 28
Tabella 3.17 Campi di AudioPlayer .................................................................................................................. 29
Tabella 3.18 Campi di ImageView ................................................................................................................... 29
Tabella 3.19 Campi di PhotocameraController ............................................................................................... 29
Tabella 3.20 Campi di ImageView ................................................................................................................... 29
Tabella 3.21 Campi di VideocameraController ................................................................................................ 30
Tabella 3.22 Estensione di ViewController ai controlli con interazione bidirezionale con l’utente ............... 30
Tabella 3.23 Campi di EditText ........................................................................................................................ 31
Tabella 3.24 Estensione di ViewController ai controlli con ruolo nella navigazione ....................................... 33
Tabella 3.25 Estensione di ViewController ai controlli con ruolo nella navigazione ....................................... 33
Tabella 3.26 Campi di Navigation .................................................................................................................... 33
Tabella 3.27 Campi di Menu ............................................................................................................................ 34
Tabella 3.28 Campi di MenuItem .................................................................................................................... 34
Tabella 3.29 Campi di Button .......................................................................................................................... 34
Tabella 3.30 Campi di ListView ........................................................................................................................ 35
Tabella 3.31 Campi di ListViewCell .................................................................................................................. 35
Tabella 3.32 Campi di ListView ........................................................................................................................ 36
Tabella 3.33 Campi di ListViewCell .................................................................................................................. 36
Tabella 3.34 Estensione di ViewController agli elementi accessori ................................................................ 37
Tabella 3.35 Campi di ProgressDialog ............................................................................................................. 38
Tabella 3.36 Campi di AlertDialog ................................................................................................................... 38
Tabella 3.37 Campi di Entity ............................................................................................................................ 40
Tabella 3.38 Campi di EntityAttribute ............................................................................................................. 40
Tabella 3.39 Campi di EntityRelation ............................................................................................................... 40
Tabella 3.40 Campi di StorageRecord .............................................................................................................. 41
Tabella 3.41 Campi di PreferenceRecord ........................................................................................................ 41
Tabella 3.42 Campi di CloudObject ................................................................................................................. 42
Tabella 3.43 Campi di ObjectAttribute ............................................................................................................ 42
Tabella 3.44 Tipologie di AdapterBinding ........................................................................................................ 45
VI
Tabella 3.45 Campi di ModelConnector .......................................................................................................... 46
Tabella 3.46 Proprietà collegabili .................................................................................................................... 48
Tabella 4.1 Proprietà di "data handler object" ................................................................................................ 60
Tabella 4.2 Proprietà di oggetti di tipo PreferenceRecord .............................................................................. 61
Tabella 4.3 Proprietà di oggetti di tipo FileStorageRecord ............................................................................. 61
Tabella 4.4 Proprietà di oggetti di tipo Entity ................................................................................................. 62
Tabella 4.5 Proprietà di oggetti di tipo EntityAttribute ................................................................................... 62
Tabella 4.6 Proprietà di oggetti di tipo EntityRelation .................................................................................... 63
Tabella 4.7 Proprietà di oggetti di tipo CloudObject ....................................................................................... 63
Tabella 4.8 Proprietà di oggetti di tipo CloudObjectAttribute ........................................................................ 64
Tabella 4.9 Proprietà dell'oggetto Application ................................................................................................ 65
Tabella 4.10 Proprietà di oggetti di tipo Scene ............................................................................................... 65
Tabella 4.11 Proprietà di oggetti di tipo ViewController ................................................................................. 66
Tabella 4.12 Proprietà di oggetti di tipo ControlChain .................................................................................... 67
Tabella 4.13 Proprietà di oggetti di tipo MenuItem ........................................................................................ 68
Tabella 4.14 Proprietà di oggetti di tipo UiPhoneControl ............................................................................... 70
Tabella 4.15 Proprietà aggiuntive di controlli AudioRecorder ........................................................................ 71
Tabella 4.16 Proprietà aggiuntive di controlli Button ..................................................................................... 71
Tabella 4.17 Proprietà aggiuntive di controlli Card ......................................................................................... 72
Tabella 4.18 Proprietà aggiuntive di controlli EditText ................................................................................... 72
Tabella 4.19 Proprietà aggiuntive di controlli GridView.................................................................................. 72
Tabella 4.20 Proprietà aggiuntive di controlli Label ........................................................................................ 72
Tabella 4.21 Proprietà aggiuntive di controlli ListView ................................................................................... 72
Tabella 4.22 Proprietà aggiuntive di controlli Map ......................................................................................... 72
Tabella 4.23 Proprietà aggiuntive di controlli PhotocameraController .......................................................... 72
Tabella 4.24 Proprietà aggiuntive di controlli VideocameraController ........................................................... 72
Tabella 4.25 Proprietà aggiuntive di controlli WebView ................................................................................. 73
Tabella 4.26 Proprietà aggiuntive di controlli Container ................................................................................ 73
Tabella 4.27 Proprietà di oggetti di tipo SourceType ...................................................................................... 74
Tabella 4.28 Proprietà di oggetti di tipo GridViewCell .................................................................................... 74
Tabella 4.29 Proprietà di oggetti di tipo ListViewCell ..................................................................................... 75
Tabella 4.30 Proprietà di oggetti di tipo Constraint ........................................................................................ 76
Tabella 4.31 Proprietà di oggetti di tipo AlertDialog ....................................................................................... 77
Tabella 4.32 Proprietà di oggetti di tipo ProgressDialog ................................................................................. 77
Tabella 4.33 Proprietà di oggetti di tipo AsyncTask ........................................................................................ 78
Tabella 4.34 Proprietà di oggetti di tipo Navigation ....................................................................................... 79
Tabella 4.35 Proprietà di oggetti di tipo AdapterBinding ................................................................................ 80
Tabella 4.36 Proprietà di oggetti di tipo ModelConnector.............................................................................. 81
Tabella 4.37 Payload dell'azione CreatePreferenceRecord ............................................................................. 83
Tabella 4.38 Payload dell'azione DeletePreferenceRecord ............................................................................. 84
Tabella 4.39 Payload dell'azione CreateFileStorageRecord ............................................................................ 84
Tabella 4.40 Payload dell'azione DeleteFileStorageRecord ............................................................................ 84
Tabella 4.41 Payload dell'azione CreateEntity ................................................................................................ 84
Tabella 4.42 Payload dell'azione EditEntity ..................................................................................................... 85
Tabella 4.43 Payload dell'azione DeleteEntity ................................................................................................ 85
Tabella 4.44 Payload dell'azione CreateEntityAttribute .................................................................................. 85
VII
Tabella 4.45 Payload dell'azione DeleteEntityAttribute .................................................................................. 85
Tabella 4.46 Payload dell'azione CreateEntityRelation ................................................................................... 85
Tabella 4.47 Payload dell'azione DeleteEntityRelation ................................................................................... 86
Tabella 4.48 Payload dell'azione CreateCloudObject ...................................................................................... 86
Tabella 4.49 Payload dell'azione EditCloudObject .......................................................................................... 86
Tabella 4.50 Payload dell'azione DeleteCloudObject ...................................................................................... 86
Tabella 4.51 Payload dell'azione CreateCloudObjectAttribute ....................................................................... 87
Tabella 4.52 Payload dell'azione DeleteCloudObjectAttribute ....................................................................... 87
Tabella 4.53 Payload dell'azione CreateScene ................................................................................................ 87
Tabella 4.54 Payload dell'azione EditScene ..................................................................................................... 87
Tabella 4.55 Payload dell'azione DeleteScene ................................................................................................ 87
Tabella 4.56 Payload dell'azione LinkViewController ...................................................................................... 88
Tabella 4.57 Payload dell'azione UnlinkViewController .................................................................................. 88
Tabella 4.58 Payload dell'azione EditContainer .............................................................................................. 88
Tabella 4.59 Payload dell'azione CreateViewController ................................................................................. 88
Tabella 4.60 Payload dell'azione EditViewController ...................................................................................... 89
Tabella 4.61 Payload dell'azione DeleteViewController.................................................................................. 89
Tabella 4.62 Payload dell'azione CreateUiPhoneControl ................................................................................ 89
Tabella 4.63 Payload dell'azione EditUiPhoneControl .................................................................................... 89
Tabella 4.64 Payload dell'azione DeleteUiPhoneControl ................................................................................ 90
Tabella 4.65 Payload dell'azione CreateControlChain ..................................................................................... 91
Tabella 4.66 Payload dell'azione EditControlChain ......................................................................................... 91
Tabella 4.67 Payload dell'azione DeleteControlChain ..................................................................................... 91
Tabella 4.68 Payload dell'azione CreateConstraint ......................................................................................... 91
Tabella 4.69 Payload dell'azione EditConstraint ............................................................................................. 92
Tabella 4.70 Payload dell'azione DeleteConstraint ......................................................................................... 92
Tabella 4.71 Payload dell'azione CreateMenuItem ......................................................................................... 92
Tabella 4.72 Payload dell'azione EditMenuItem ............................................................................................. 93
Tabella 4.73 Payload dell'azione DeleteMenuItem ......................................................................................... 93
Tabella 4.74 Payload dell'azione CreateGridViewCell ..................................................................................... 93
Tabella 4.75 Payload dell'azione EditGridViewCell ......................................................................................... 93
Tabella 4.76 Payload dell'azione DeleteGridViewCell ..................................................................................... 93
Tabella 4.77 Payload dell'azione CreateListViewCell ...................................................................................... 94
Tabella 4.78 Payload dell'azione EditListViewCell ........................................................................................... 94
Tabella 4.79 Payload dell'azione DeleteListViewCell ...................................................................................... 94
Tabella 4.80 Payload dell'azione CreateNavigation ........................................................................................ 94
Tabella 4.81 Payload dell'azione EditNavigation ............................................................................................. 95
Tabella 4.82 Payload dell'azione DeleteNavigation ........................................................................................ 95
Tabella 4.83 Payload dell'azione EditSourceType ........................................................................................... 95
Tabella 4.84 Payload dell'azione CreateAlertDialog ........................................................................................ 96
Tabella 4.85 Payload dell'azione EditAlertDialog ............................................................................................ 96
Tabella 4.86 Payload dell'azione DeleteAlertDialog ........................................................................................ 96
Tabella 4.87 Payload dell'azione CreateProgressDialog .................................................................................. 96
Tabella 4.88 Payload dell'azione EditProgressDialog ...................................................................................... 97
Tabella 4.89 Payload dell'azione DeleteProgressDialog .................................................................................. 97
Tabella 4.90 Payload dell'azione CreateAsyncTask ......................................................................................... 97
VIII
Tabella 4.91 Payload dell'azione EditAsyncTask .............................................................................................. 97
Tabella 4.92 Payload dell'azione DeleteAsyncTask ......................................................................................... 97
Tabella 4.93 Payload dell'azione ChangeName ............................................................................................... 98
Tabella 4.94 Payload dell'azione ChangeCompanyIdentifier .......................................................................... 98
Tabella 4.95 Payload dell'azione CreateAdapterBinding................................................................................. 98
Tabella 4.96 Payload dell'azione DeleteAdapterBinding ................................................................................. 99
Tabella 4.97 Payload dell'azione CreateModelConnector .............................................................................. 99
Tabella 4.98 Payload dell'azione DeleteModelConnector ............................................................................ 100
Tabella 6.1 Dimensione della codebase dell'app di valutazione ................................................................... 129
IX
Sommario
Il mondo di oggi è sempre più connesso, veloce, pervaso dalla tecnologia e dai servizi che questa può offrire
e sempre in cerca di novità. Oltre alla pervasione, l’informatica di oggi affronta il problema della velocità del
cambiamento. La risposta da parte degli sviluppatori di applicazioni mobili è sempre più orientata allo
sviluppo di strumenti capaci di supportare la variabilità e la velocità del mercato, permettendo di realizzare
applicazioni sempre più scalabili, più adattabili, ricche di funzionalità ed accattivanti nella grafica, e
prodotte e immesse sul mercato nel minor tempo possibile. La tendenza degli ultimi anni è quella di creare
strumenti a supporto dello sviluppo di applicazioni che possano adattarsi non solo a dispositivi diversi per
dimensioni o per aspetto, ma anche per sistema operativo. Sono esempi di questa tendenza i framework
Xamarin, ReactNative, Titanium e Cordova. Questo lavoro mira a fornire strumenti che consentano di
andare oltre e di abbattere ulteriormente il tempo necessario a trasformare un’idea in un’app disponibile
sul mercato. A questo scopo sono stati sviluppati due strumenti per assistere gli sviluppatori: Protocode e
MobileCodeGenerator. Protocode è uno strumento dedicato a dare forma all’idea: esattamente come uno
strumento di prototipizzazione, esso consente di modellare graficamente i diversi aspetti dell’applicazione,
dalle viste ai dati richiesti, fino alla logica che li collega. La forma che Protocode mira a dare all’idea non è
tanto quella del prototipo, ma quella del modello strutturato. L’adozione del modello strutturato consente,
infatti, di generare automaticamente il codice dell’applicazione tramite lo strumento
MobileCodeGenerator. La generazione può essere fatta per diversi linguaggi, da Java per Android a Swift
per iOS. In questo lavoro di tesi si valuterà l’efficacia di questa toolchain applicata alla generazione di codice
cross - platform, in modo da fondere i benefici di una generazione automatica e con quelli di uno sviluppo
non specifico rispetto alla piattaforma software di destinazione, senza perdere di vista l’aspetto delle
performance.
X
Abstract
Today, the world is getting more and more connected, fast, pervaded by the technology and in search of
continuous innovation. Aside pervasion, the computer science today lives and faces up to the problem of
velocity of change. Mobile application developers try to face this situation creating tools able to support
variability and velocity of the market, allowing them to realize applications that are more scalable, more
adaptable, richer in functionality and more attractive from a graphical point of view, and produced and
released in the least possible time. Over the recent years, the trend is to create tools supporting the
development of applications capable to adapt not only to different screen sizes or form factors, but also to
different operative systems. Examples of this trend are the Xamarin framework, the ReactNative library, the
Titanium software development kit and the Apache Cordova solution. This master thesis aims at providing
tools to go beyond this point, further reducing the time necessary to transform an idea into a market ready
mobile application. In order to achieve this, two tools have been developed: Protocode and
MobileCodeGenerator. The former is a tool aimed at shaping an idea: just like a prototyping tool, it allows
to model in a graphical fashion the different aspects of a mobile application, from views to required data,
up to the logic that connects them. The shape given by Protocode to the idea is not that of a prototype, but
of a structured model. The adoption of the model, in effect, enables the automatic generation the source
code of the application using the MobileCodeGenerator tool. The conversion can target different languages,
from Java for Android to Swift for iOS. This thesis work investigates the effectiveness of this toolchain
applied to the generation of cross platform source code, in such a way to blend the benefits of automatic
generation with a development flow, which is not specific to the target software platform, without sacrifice
on performance
1
1 Introduzione La rivoluzione portata dall’avvento dei dispositivi mobili è oggi un fenomeno globale in continua crescita. I
dati del 2019 (Figura 1.1) evidenziano 5 miliardi di utenti mobili, con una penetrazione di mercato del 67%,
e un trend di crescita del 2% (Figura 1.2) nell’ultimo anno, come provato dalle ricerche di We Are Social [1].
Figura 1.1 Ampiezza e penetrazione del mercato mobile nel mondo [1]
Figura 1.2 Crescita annuale del mercato mobile [1]
Tale crescita, oltre che dalla rapidità e dalla globalità, è caratterizzata da una forte variabilità per quanto
riguarda i dispositivi. Il mercato spazia da smartwatch a smartphone e tablet, da televisori connessi ad
automobili. Questo spiega come sia possibile che il numero di connessioni mobili abbia ormai superato la
popolazione mondiale (Figura 1.3).
Figura 1.3 Rapporto tra utenti e connessioni mobile [1]
2
Guardando solo al mercato mobile di smartphone, smartwatch e tablet, si comprende facilmente
l’interesse di aziende produttrici di software per le applicazioni mobili, capaci di produrre in un anno incassi
per quasi 20 miliardi di dollari solamente sugli store ufficiali (Figura 1.4). Grazie all’avanzamento
tecnologico dei dispositivi mobili, che utilizzano processori sempre più performanti e che sono equipaggiati
con un crescente numero di sensori, l’attrazione del mercato mobile è sicuramente molto interessante per
qualsiasi compagnia del settore.
Figura 1.4 Guadagno del mercato mobile [1]
Esaminando tale mercato, si nota immediatamente un contrasto tra il mondo dell’hardware e quello del
software. Il mercato dei produttori hardware appare piuttosto vario, con colossi come Samsung, Apple e
Huawei capaci di detenere il 44.5% del mercato, affiancati da realtà in rapidissima crescita, prima fra tutte
Xiaomi che ne secondo trimestre del 2018 incrementa del 3% la propria quota di mercato.
Figura 1.5 Vendite di smartphone per produttore in migliaia di unità nel secondo trimestre 2018 [2]
Tale frammentazione si oppone al consolidamento, per quanto riguarda il mercato dei sistemi operativi, di
Android e iOS, che detengono il 99.9% delle quote di mercato. Con l’annuncio, dato a inizio 2018, della
cessazione del supporto ufficiale per Windows Phone, sul mercato restano davvero solo i due colossi di
Cupertino e Mountain View. Il dominio di Android è sempre indiscusso, calcolando anche il sorpasso di
Huawei (i cui device montano sistemi operativi Android) ai danni di Apple nel secondo trimestre del 2018.
3
Figura 1.6 Vendite di smartphone per sistema operativo in migliaia di unità nel secondo trimestre 2018 [2]
Il successo di tali sistemi operativi, oltre alla stabilità e alla quantità di funzionalità offerte, sta nella
ricchezza dei rispettivi application store: GooglePlay e AppStore. Le applicazioni mobili sono, quindi, un
elemento portante del mercato mobile, in cui rappresentano, al contempo, una grande sfida e una grande
possibilità di business.
Per il mercato delle applicazioni mobili una delle sfide chiave sta nel fronteggiare la dualità di iOS e Android,
data soprattutto la loro profonda diversità. Un approccio tradizionale prevedrebbe di portare avanti due
linee separate di sviluppo della stessa applicazione, la prima per Android e la seconda per iOS, con
conseguente aumento dei costi, in particolare di manutenzione, e delle competenze richieste. La risposta
dal mondo degli sviluppatori si concretizza nello sviluppo e nel rilascio di strumenti per lo sviluppo cross –
platform che consentano, appunto, di portare avanti una sola linea di sviluppo e manutenzione ottenendo
un risultato che possa essere installato su entrambe le piattaforme, con conseguente risparmio sui costi,
riduzione dello spettro di competenze richiesto e contrazione dei tempi di sviluppo. Proprio i tempi, infatti,
costituiscono la seconda grande challenge del mercato del software moderno: la rapidità dello sviluppo,
l’anticipazione del rilascio e l’agilità del processo di produzione sono elementi chiave per il successo di
qualsiasi software, in particolare per le applicazioni mobili, che sono caratterizzate da un’estrema facilità di
acquisizione e installazione così come di rimozione. L’engagement dell’utente procede a pari importanza
con la reattività dello sviluppatore nel produrre aggiornamenti, nuove features e risoluzione di problemi.
Questo lavoro di tesi ambisce a presentare approcci e strumenti per favorire lo sviluppo rapido, efficace e
di qualità di applicazioni mobili cross-platform. In particolare, si andrà a presentare una soluzione per la
generazione automatica del codice sorgente di applicazioni mobili cross-platform a partire dalla struttura
dell’applicazione stessa, descritta nella fase di prototipizzazione. L’attività di prototipizzazione, infatti,
utilizza già una descrizione dell’applicazione per la creazione di un prototipo interattivo. In questo lavoro di
tesi si cercherà di definire una modalità per sfruttare al meglio tale momento in modo da ottenere, oltre al
prototipo funzionante, anche una rappresentazione strutturata dei dati utilizzati per la creazione del
prototipo stesso, che costituirà un modello dell’applicazione. Un prototipo tradizionalmente guarda
soltanto alla componente visiva di un’applicazione, mentre in questo lavoro di tesi ne verrà considerata una
variante arricchita della descrizione delle componenti Model e Controller, in modo da ottenere un modello
più ricco, completo e utile. È intuitivo, infatti, considerare che la completezza del modello è direttamente
proporzionale alla quantità di codice che può essere generato a partire da esso rispetto al codice totale
richiesto. All’attività di prototipizzazione segue, quindi, una generazione automatica del codice
dell’applicazione a partire dal modello creato insieme al prototipo. I due momenti di prototipizzazione e di
generazione del codice sono associati ai due tools presentati in questo lavoro di tesi: Protocode e
MobileCodeGenerator. Il primo è uno strumento di prototipizzazione orientato alla produzione di un
4
modello dell’applicazione, mentre il secondo è un generatore di codice basato su un modello
dell’applicazione da generare. Entrambi gli strumenti non sono interamente frutto di questo lavoro di tesi,
e raggiungono ora la versione 5.0. La prima versione di Mobile Code Generator è nata nel 2012 dal lavoro di
Gregorio Perego e Stefania Pezzetti [3], ed è stata successivamente migliorata da Mattia Natali [4] nel 2013
che ha inoltre introdotto lo strumento Protocode. Nell’A.A. 2015/2016 Aldo Pintus [5] ha aggiunto la
possibilità di modellare l’interfaccia per la componente smartwatch, mentre nell’A.A. 2017/2018 Alessio
Rossotti [6] ha introdotto la modellazione e la generazione del codice relativi alla parte Model. In questo
lavoro di tesi è stato aggiunto in primo luogo il supporto alla generazione di codice cross-platform basato
sul framework ReactNative. Dal punto di vista delle funzionalità supportate, questa generazione è
equivalente alla generazione nativa implementata nei precedenti lavori, tranne per la possibilità di gestire
attività in background, che non è disponibile a causa di una limitazione del framework. In secondo luogo è
stata introdotta la possibilità di modellare parte della componente Controller, soprattutto per quanto
riguarda la gestione di eventi provenienti dalla View. Una modellazione completa del Controller infatti
sarebbe eccessivamente complessa per lo scopo di questo lavoro di tesi, volto a utilizzare la fase di
prototipizzazione. Il dettaglio di quali elementi del Controller sia possibile modellare, e quindi generare, è
riportato nel capitolo 3.3. La generazione di codice corrispondente al modello del Controller è stata
implementata per la sola piattaforma ReactNative, ma può essere facilmente estesa al codice nativo.
Nei prossimi capitoli verrà presentata, dopo una valutazione dello stato dell’arte, la struttura del modello di
riferimento, sia per quanto riguarda il lavoro preesistente sia per quanto riguarda invece le novità
introdotte da questo lavoro di tesi, in modo da delineare un quadro completo delle capacità espressive
implementate. Verranno poi presentati i due strumenti Protocode e MobileCodeGenerator, e ne verrà
valutata l’efficacia rispetto alla quantità e alla qualità del codice generato.
5
2 Contesto generale
2.1 Stato dell’arte In questa sezione vengono discussi gli strumenti, i framework e i software attualmente utilizzati nella
prototipizzazione e nella produzione di applicazioni mobili. Tali strumenti affiancano gli sviluppatori sia
durante le attività di design che durante le attività di implementazione vera e propria. Vi sono poi strumenti
ibridi, che consentono lo sviluppo intelligente dell’applicazione affiancato da una live preview del risultato
finale.
2.1.1 Strumenti di prototipizzazione
Le applicazioni di prototipizzazione sono strumenti che permettono, attraverso editor grafici e senza la
necessità di scrivere alcun tipo di codice eseguibile, di disegnare l’interfaccia utente dell’applicazione che si
vuole sviluppare. Essi sono molto utili al fine di avere un’anteprima visiva di quella che sarà la GUI e
valutarne le possibili alternative, prima di iniziare la fase di sviluppo in codice vera e propria. In tal senso,
costituiscono un eccellente strumento di dialogo tra il team di design e gli stakeholders. Come verrà
evidenziato nei paragrafi successivi, la criticità maggiore risiede nel loro utilizzo da parte del team che cura
l’implementazione dell’applicazione.
Proto.io
Proto.io [7] è uno strumento di realizzazione di prototipi realistici di applicazione mobili capaci di adattarsi
a varie tipologie di dispositivi, tra cui anche smartTV, console da gaming e smartwatch. L’utilizzo è semplice
e intuitivo, basato sul drag and drop di componenti scelti da una ricca palette laterale. Sono disponibili
anche componenti specifici di una singola piattaforma software e funzionalità più avanzate come la
modellazione di diversi stati per una schermata, la gestione delle transizioni o l’assegnazione di azioni a
elementi della vista. È infine possibile importare prototipi sviluppati con altri tool, quali Sketch [8] e
Photoshop [9].
Figura 2.1 Interfaccia grafica di Proto.io
6
Alla ricchezza di features offerte corrisponde costo piuttosto elevato: il piano più economico è offerto a
29$/mese, ma limita ad un solo utente e 5 progetti. Per confronto, il piano Corporate offre 10 utenti e 30
progetti attivi al costo di 199$/mese.
Oltre al costo molto elevato, Proto.io presenta un’altra limitazione importante. Il prototipo che è possibile
ottenere con tale tool è un prodotto che può essere utilizzato a fini di specifica dell’applicazione. Esso,
infatti, può essere utilizzato quasi unicamente come strumento di confronto con gli stakeholders
relativamente all’aspetto grafico e alla navigazione all’interno dell’applicazione, e come target dell’attività
di implementazione. Tuttavia, il prototipo contiene molte informazioni riguardo alla struttura
dell’applicazione, che possono essere utilizzate per la generazione automatica almeno di uno scheletro
dell’applicazione durante la fase di implementazione, in modo da ridurre i tempi di sviluppo sfruttando il
lavoro fatto in fase di prototipizzazione.
MarvelApp
MarvelApp [10] è uno strumento di prototipizzazione caratterizzato dalla possibilità di creare prototipi
realistici sia in termini di aspetto grafico dell’applicazione sia in termini di interazione con l’utente: infatti è
possibile realizzare prototipi arricchiti con azioni e transizioni tra scene. Il punto di forza di MarvelApp è
l’attenzione al lavoro in team: questo tool offre un sistema di commenti e chat legato agli strumenti di
prototipizzazione, in modo che il team possa discutere in merito ad un elemento del prototipo legando la
chat all’elemento stesso. Da ultimo, MarvelApp offre la possibilità di importare modelli e prototipi da
Sketch [8].
Figura 2.2 Presentazione dell'interfaccia grafica di MarvelApp
Il prodotto viene offerto in un range di piani di acquisto molto variabili per prezzo e funzionalità. Si passa
dal piano base, gratuito ma limitato ad un solo utente e un solo progetto, ad un piano sempre basilare ma
al costo di 16$/mese che consente un numero illimitato di progetti, la possibilità di scaricare i prototipi e la
rimozione del logo Marvel, ad un piano Enterprise che offre workspaces, 99.9% uptime, Single Sign-On e
molte altre features aggiuntive, ad un costo da definire tramite contatto con l’azienda.
7
In modo simile a Proto.io [7], questo strumento separa ancora nettamente le attività di prototipizzazione
ed implementazione dell’applicazione. Per quanto MarvelApp si avvicini ai produttori di applicazioni,
fornendo funzionalità di supporto al lavoro in team, essa si rivolge solamente ai team di designer che
studiano l’aspetto grafico, l’organizzazione delle viste e la navigazione tra le stesse, e manca di un supporto
al team di implementazione. L’informazione che viene inserita nel prototipo nasce e muore con esso, e non
viene sfruttata appieno. Analogamente, il team che si occupa di implementare l’applicazione parte da una
pagina bianca, su cui va a riscrivere, questa volta come codice eseguibile, tutte le informazioni già utilizzate
dai designer nella produzione del prototipo. Tale approccio non è ideale in quanto è necessario lavorare
due volte con le stesse informazioni, dato che la prototipizzazione e l’implementazione non dispongono di
punti di contatto.
Adobe Experience Design CC
Adobe Experience Design CC [11] è il prodotto di Adobe disegnato appositamente per il design e la
creazione di prototipi di applicazioni mobili. Il punto di forza di questo strumento è la ricchezza di features.
Esso offre la possibilità di utilizzare strumenti rapidi ed intuitivi per il design e di arricchire i prototipi con
comandi vocali e altre esperienze beyond the screen. Inoltre, consente di adattare automaticamente
un’interfaccia a schermi di diverse forme e dimensioni, e di creare animazioni tra le diverse schermate.
Infine, utilizzando l’app mobile è possibile osservare il risultato della prototipizzazione direttamente sul
proprio device, e di interagirvi come farebbe un utente finale.
Figura 2.3 Interfaccia di Adobe Experience Design CC
Questo prodotto è disponibile con licenza gratuita limitata ad un singolo prototipo condiviso, mentre sono
disponibili i piani “Single App” e “Creative Cloud All Apps” che, oltre a rimuovere tale limitazione, offrono
l’accesso alla libreria di font di Adobe, alle Creative Cloud Libraries e a 100GB di spazio di archiviazione
cloud.
Questo prodotto è probabilmente la miglior soluzione sul mercato per la prototipizzazione di applicazioni
mobili, e possiede una ricchezza di features ineguagliata in tutti gli altri tool. Inoltre la possibilità di avere un
prototipo interattivo funzionante sul proprio device è certamente molto interessante, ma non viene
sfruttata come potrebbe. Un prototipo eseguibile su un device potrebbe, infatti, essere utilizzato come
punto di partenza per l’attività di implementazione vera e propria, in quanto contiene già tutte le
informazioni sui layout, sulle schermate, sulla navigazione e sull’interazione con l’utente. Peraltro, il fatto
che il prototipo sia eseguibile suggerisce che esso sia memorizzato in qualche formato strutturato che
potrebbe essere utilizzato come punto di partenza per la generazione di codice nativo, o come linguaggio
8
ad alto livello per la descrizione di applicazioni che poi vengono eseguite da una apposita macchina virtuale
in grado di operare su tale linguaggio. Sebbene appaia molto vicina, tale funzionalità non è disponibile in
Adobe Experience Design CC, e ciò agisce da barriera al flusso di dati tra design e prototipizzazione da un
lato e implementazione dall’altro.
2.1.2 Framework per applicazioni cross-platform
La necessità di produrre la medesima applicazione per due sistemi operativi radicalmente diversi ha portato
la community di sviluppatori a pensare soluzioni che permettessero di utilizzare la stessa codebase. Tali
soluzioni agiscono essenzialmente in base a due approcci. Il primo consiste nell’introdurre un layer di
astrazione dal sistema operativo, mentre il secondo agisce sul processo di compilazione inserendo passaggi
di cross-compilazione. In questa sezione saranno presentati i framework più popolari per lo sviluppo di
applicazioni cross-platform.
Xamarin
Xamarin [12] è la soluzione di Microsoft [13] per lo sviluppo di applicazioni cross-platform. Ideato da
un’azienda indipendente, Xamarin è ora integrato nella suite Visual Studio [14].
Xamarin si basa sul linguaggio C# per la definizione della logica applicativa e su linguaggio XAML per la
definizione delle interfacce. Inoltre, il framework mette a disposizione binding a due vie per connettere le
viste con i dati. Infine, è possibile utilizzare le funzioni della libreria .NET. L’interazione con i servizi del
sistema operativo è ottenuta tramite il linker. Xamarin, infatti, per ogni release di Android o iOS, genera
una libreria di definizioni che riflette l’interfaccia esposta dal sistema operativo ad un’app. In tal modo è
possibile utilizzare le funzioni offerte dal sistema operativo nel codice C#, compilare e poi collegare la vera
funzione nativa tramite il processo di linking.
Lo sviluppo cross – platform è ottenuto appunto agendo sul processo di compilazione: per i dispositivi che
eseguono iOS il codice C# viene compilato ahead of time, la libreria .NET viene inclusa dal linker e le parti
non necessarie vengono eliminate. Ciò è necessario data la limitazione di Apple sulla generazione di codice
a runtime sui device che eseguono iOS. Su Android, invece, il codice viene convertito in un linguaggio
intermedio che viene poi viene eseguito tramite just in time compilation sulla MonoVM, che viene attivata
in parallelo ad ART.
Su Android l’installazione di un’app Xamarin ha un costo notevole in termini di memoria richiesta, dato che
devono essere installati anche la MonoVM compatibile con il sistema operativo in uso e alcuni pacchetti di
helpers.
La principale limitazione di questo approccio è la forte dipendenza dal processo di compilazione. Essa è,
infatti, lunga e complessa e, nonostante Xamarin sia ufficialmente supportato e sviluppato da Microsoft,
molto incline a presentare problemi. Inoltre, questo rende impossibile il live editing, ovvero la possibilità di
modificare il codice sorgente e vedere in tempo reale i cambiamenti prodotti dalla modifica nel simulatore
o anche nel proprio dispositivo.
Infine, va sottolineato che Xamarin non è una soluzione completa per lo sviluppo cross-platform. Sebbene
essa fornisca la possibilità di codificare l’app utilizzando interamente il linguaggio C#, è anche vero che una
soluzione Xamarin si compone di minimo tre progetti: un progetto condiviso, un progetto specifico per
Android e un progetto specifico per iOS. Infatti, molte parti dell’applicazione vanno progettate e
implementate indipendentemente per Android e per iOS, date le profonde differenze tra i due sistemi
operativi. L’orchestrazione dei tre progetti avviene nel seguente modo: nel progetto condiviso viene
9
definita l’interfaccia del servizio che richiede codice specifico Android o iOS. Tale interfaccia viene poi
implementata nei progetti specifici delle varie piattaforme. Nel progetto condiviso il servizio viene
istanziato tramite l’utilizzo di un servizio apposito, il Dependency Service, che prende come input
l’interfaccia definita precedentemente. In fase di compilazione, dato il sistema operativo di destinazione,
viene incluso il codice corretto e le chiamate al Dependency Service vengono risolte.
PhoneGap
PhoneGap [15] è la soluzione supportata da Adobe [16] per lo sviluppo di applicazioni cross-platform, ed è
basato sul tool Cordova di proprietà di Apache [17].
PhoneGap ottiene uno sviluppo cross-platform attraverso l’utilizzo di strumenti web e di un layer di
astrazione.
L’applicazione da produrre viene sviluppata a tutti gli effetti come una applicazione web, utilizzando quindi
il linguaggio HTML per la definizione delle interfacce, il CSS per l’applicazione di stili e infine il linguaggio
JavaScript per la definizione della logica applicativa. JavaScript, oltre alla classica libreria di funzioni
utilizzate per la manipolazione del DOM, viene arricchito con l’aggiunta di una libreria di funzioni che
consentono la comunicazione con i servizi offerti dal sistema operativo del device.
Il layer di astrazione consiste nell’inserire la web app in un componente di tipo WebView, disponibile su
tutte le piattaforme. Tale componente viene esteso in modo da fornire al JavaScript eseguito al suo interno
le funzioni per interagire con il sistema operativo. Tali funzioni sono, infatti, implementate nel layer di
astrazione con codice nativo, e devono poter essere richiamate dalla logica applicativa.
La limitazione maggiore di questo approccio consiste, appunto, nel layer di astrazione e nell’utilizzo di un
linguaggio interpretato come Javascript. Questo si traduce in un problema di performance
dell’applicazione. Su iOS le applicazioni native eseguono direttamente codice macchina, che è
ineguagliabile in termini di performance. Su Android, con l’introduzione di ART, le applicazioni vengono
rilasciate come codice di livello bytecode, che viene poi compilato ahead-of-time al momento
dell’installazione dell’app stessa, ottenendo quindi performance native. È facilmente intuibile che le
performance del JavaScript sono nettamente inferiori. Inoltre, l’aspetto grafico dell’applicazione è gestito
dall’HTML e non da controlli nativi. Questo introduce un altro motivo di perdita a livello di performance.
AppceleratorTitanium
Titanium è un Software Development Kit open source prodotto dall’azienda Appcelerator [18].
Titanium ottiene uno sviluppo cross-platform attraverso l’utilizzo del linguaggio JavaScript per la definizione
della logica applicativa. Differentemente da PhoneGap, le interfacce utente non sono basate su HTML ma
su componenti nativi, disegnati su richiesta della logica applicativa.
L’SDK di Titanium espone una API costituita da oggetti proxy che collegano in modo bidirezionale il sistema
operativo e i servizi nativi da un lato e la logica JavaScript dall’altro. In questo modo, le chiamate JavaScript
a servizi nativi vengono mappate in chiamate native a tutti gli effetti, e gli eventi lanciati dai servizi nativi
possono essere catturati e gestiti tramite JavaScript. Questo meccanismo permette, infine, di unificare i
punti comuni delle interfacce esposte dai due sistemi operativi Android e iOS sotto un’unica interfaccia,
senza comunque rinunciare a funzionalità esposte da una sola delle due piattaforme. Infatti, i punti comuni
vengono unificati, e tutti gli elementi non comuni restano accessibili.
10
Il progetto prevede anche un IDE dedicato chiamato Titanium Studio, che è dotato di un debugger integrato
che permette di provare l’applicazione sia sul simulatore sia sul dispositivo fisico, indispensabile nel caso di
sviluppo su Android (dato che l’emulatore di Google non è molto performante). TitaniumStudio include
anche strumenti per la definizione grafica delle interfacce dell’applicazione e strumenti per equipaggiare
l’applicazione con funzionalità di analitica ed error detection, i cui dati sono visualizzabili tramite apposita
dashboard.
Questo approccio presenta tre limitazioni principali. La prima, più lieve, riguarda le performance di
esecuzione. Per quanto il codice sfrutti chiamate a servizi nativi, la logica è comunque eseguita in
JavaScript, le cui performance di esecuzione sono notoriamente non ottimali. Inoltre, vi è un piccolo
overhead dovuto all’utilizzo dei Proxy. La seconda limitazione sta nel fatto che l’API esposta dall’SDK al
programmatore è vasta e necessita di studio apposito. La terza e ultima limitazione consiste nel fatto che gli
strumenti per la definizione grafica delle interfacce non possono essere utilizzati a scopi di
prototipizzazione, in quando costituiscono solo un aiuto alla definizione del codice, e non uno strumento
utilizzabile dal team di design.
React Native
ReactNative [19] è la soluzione supportata da Facebook per la produzione di applicazioni cross-platform.
Tale soluzione si configura come un’estensione al mondo mobile della libreria ReactJS.
ReactNative ottiene lo sviluppo cross-platform tramite l’utilizzo di una estensione del linguaggio JavaScript,
chiamata JSX. JSX nasce nel contesto di ReactJS al fine di semplificare la definizione di componenti
includendovi il frammento di albero DOM incaricato di rendere il componente stesso. ReactNative adotta lo
stesso principio, ma sostituisce i tag HTML disponibili in ReactJS con tag mappati su controlli nativi.
I veri punti di forza di ReactNative non risiedono, tuttavia, nell’utilizzo del JSX, che è solo uno strumento.
ReactNative si basa su tre concetti essenziali: sintassi dichiarativa, composizione di componenti e flusso
unidirezionale dei dati. Questo framework, infatti, si basa sulla definizione di componenti incapsulati, che
gestiscono ciascuno il proprio stato, e sulla composizione degli stessi a formare interfacce complesse. Dato
che la logica è scritta in JavaScript e non è basata su templates, è possibile passare tra vari componenti dati
strutturati. Il flusso dei dati è obbligatoriamente unidirezionale dal componente padre ai componenti figli.
La comunicazione tra figli e padre è possibile solo se il padre passa ai figli stessi, tra i vari dati, anche alcune
funzioni, che il figlio può invocare quando ha necessità di comunicare con il padre. Tale meccanismo
permette di implementare la gestione di eventi, che si muovono quindi dalle foglie alla radice dell’albero
dei componenti, in direzione opposta ai dati. Questa struttura dichiarativa, unita al flusso unidirezionale dei
dati, rende il codice molto più leggibile, meno incline a difetti e semplifica la risoluzione dei bug. I
componenti che compongono le interfacce, pur essendo gestiti ed orchestrati tramite logica JavaScript,
sono componenti nativi a tutti gli effetti. Il collegamento tra il dominio nativo e il dominio JavaScript è a
carico del framework. Infine, l’API esposta da ReactNative ad uno sviluppatore è disegnata con un criterio di
minimalismo, in modo da ridurre notevolmente i tempi di apprendimento, ed è fortemente ispirata al
largamente conosciuto mondo web. L’integrazione con ExpoKit permette anche il live editing del codice con
anteprima direttamente sul proprio device.
La limitatezza dell’API esposta da ReactNative è anche un punto di debolezza del framework stesso. Infatti,
le applicazioni devono integrare un numero estremamente elevato di plugin e funzionalità aggiuntive per
implementare i requisiti anche più basilari. Questo implica la necessità di gestire i conseguenti problemi di
11
integrazione. Inoltre, il framework si limita a fornire strumenti implementativi, ignorando tutte le altre fasi
dello sviluppo di una applicazione mobile, a partire dalla prototipizzazione stessa.
2.1.3 Ambienti di sviluppo
Deco IDE
Deco IDE [20] è uno strumento per lo sviluppo intelligente di applicazione mobili basate su ReactNative.
Esso si configura come un ibrido tra un IDE e uno strumento grafico di prototipizzazione. Come i primi, esso consente di lavorare sul codice sorgente dell’applicazione e, come i secondi, consente di avere una preview dell’applicazione corrente, di inserire componenti tramite drag and drop direttamente da una palette, e di personalizzarli modificando graficamente parte delle proprietà. Le proprietà modificabili devono avere tipi di dato primitivi. La definizione di quali proprietà di un componente non predefinito possano essere aggiunge al pannello di modifica grafica viene effettuata manualmente dallo sviluppatore attraverso la creazione di appositi metadati. La definizione dei metadati stessi è agevolata dall’IDE, ma resta comunque un task da svolgere manualmente.
Sebbene l’idea sia ottima, il prodotto si presenta ad uno stato ancora abbastanza embrionale per essere utilizzato per progetti di larga scala. Più che un IDE vero e proprio, questo tool è classificabile come un editor guidato dalla sintassi.
La limitazione principale di questo strumento risiede nella sua ridotta disponibilità: esso infatti è disponibile solo per MacOS. Inoltre, questo IDE non contiene strumenti per definire graficamente le interfacce utente come le sue controparti native AndroidStudio e XCode. DecoIde manca, inoltre, delle funzionalità tipiche di un IDE, come ad esempio la possibilità di esportare il bundle dell’applicazione pronto per lo store, o un’integrazione con i popolari sistemi di controllo di versione, o la connessione con strumenti di debugging.
Figura 2.4 Interfaccia grafica di DecoIDE
JetBrains Android Studio
Android Studio [21] è lo strumento ufficiale per lo sviluppo di applicazioni per il sistema operativo Android.
Tale strumento è sviluppato da Google a partire dal popolare IDE Java IntelliJ Idea.
Android Studio offre un editor intelligente per i linguaggi di programmazione Java, C/C++ e Kotlin (l’ultimo
linguaggio rilasciato per lo sviluppo Android, equivalente al Java ma sintatticamente più conciso). Le
interfacce possono essere progettate graficamente tramite il componente Layout Editor, che consente la
definizione di molti modelli di layout, la personalizzazione dei componenti inseriti, la definizione di vincoli di
12
posizionamento e di dimensione, e la generazione automatica del file XML necessario ad Android per
rendere il layout progettato. Oltre agli strumenti caratteristici di tutti gli IDE, come l’integrazione con
sistemi di controllo di versione o l’automazione della pipeline di compilazione (ottenuta nel caso specifico
utilizzando gradle), Android Studio include un emulatore per il sistema operativo Android. Tale emulatore
permette all’utente di definire diversi dispositivi virtuali, ciascuno personalizzabile relativamente a versione
del sistema operativo utilizzata, modello di dispositivo, memoria di lavoro e memoria di massa
equipaggiata, sensori disponibili e molte altre opzioni secondarie. Il dispositivo così definito può essere
avviato ed essere utilizzato come un dispositivo reale. È, quindi, possibile installare la propria applicazione a
fini di debugging, attività per cui l’IDE offre un supporto dedicato. In alternativa è possibile utilizzare anche
il proprio device, purché la modalità sviluppatore sia abilitata.
Oltre al fatto che questo strumento supporta solamente il sistema operativo Android, e non è quindi
utilizzabile per uno sviluppo multipiattaforma, esso manca anche di strumenti utili ad unire le attività di
prototipizzazione e specifica con le attività di implementazione, limitando così la propria platea di
utilizzatori ai soli sviluppatori. Inoltre, esso non consente di progettare la navigazione all’interno
dell’applicazione in the wide. La navigazione è, infatti, implementata tramite l’invio di Intent al sistema
operativo, in modo locale alle singole scene che sono sorgenti dell’azione di navigazione. In questo modo
l’implementazione della navigazione risulta spezzata e distribuita nelle varie scene, e non è possibile averne
una visione d’insieme.
Apple XCode
XCode [22] è l’unico IDE supportato da Apple per la creazione di qualsiasi software per qualsiasi dispositivo
che esegua sistemi operativi della casa di Cupertino, dai driver per MacOS alle applicazioni mobile per
Apple Watch. In questa sezione verranno analizzate solamente le funzionalità a supporto dello sviluppo di
applicazioni mobili.
Dal punto di vista dei linguaggi di programmazione, il supporto di XCode è limitato al solo linguaggio Swift
4. Tale linguaggio è conciso, moderno e semplice da utilizzare, anche se non molto diffuso a causa della sua
specificità di utilizzo, limitata ai soli dispositivi Apple.
XCode automatizza molto il processo di sviluppo permettendo di svolgere moltissime attività attraverso
maschere e finestre di dialogo, senza praticamente mai scrivere un file di proprietà o di dipendenze, o il
manifesto dell’applicazione, o altri file di struttura. Questo rappresenta un punto di forza dal punto di vista
di un neofita, ma anche un fattore limitante per uno sviluppatore più esperto che si trova costretto nel
limite delle funzionalità esposte per via grafica. Lo strumento principe di questo approccio grafico è
l’Interface Builder, un potente motore di definizione di interfacce basato su drag and drop. Interface
Builder si affianca ad AutoLayout, un layout system che permette di gestire automaticamente i vincoli e il
comportamento dei singoli componenti. XCode, inoltre, integra uno strumento per la definizione grafica
delle StoryBoard, ovvero delle strutture di collegamento tra le varie schermate, dei pattern di navigazione
utilizzati e delle animazioni associate alle transizioni. In questo modo la progettazione della user
experience, dalla navigazione in the wide alla disposizione sullo schermo dei singoli componenti, è gestita
interamente tramite interfacce grafiche e il codice relativo viene automaticamente generato. Da ultimo,
XCode integra un simulatore per tutti i device Apple, in modo da poter testare la propria applicazione senza
acquistare la licenza da sviluppatore.
Nonostante sia un prodotto ottimo e curato fin nei minimi dettagli, XCode presenta alcune limitazioni. In
primo luogo, XCode può essere installato solo su dispositivi che eseguono OSX. Questa scelta della casa
13
produttrice restringe la platea di sviluppatori ai possessori di un dispositivo Mac, il cui costo di acquisizione
non è indifferente. In secondo luogo, il fatto che sia l’unico strumento di sviluppo supportato da Apple fa sì
che esso sia costretto ad integrare un numero enorme di funzionalità diverse. Questo si concretizza in uno
strumento abbastanza pesante e con un numero estremamente elevato di funzionalità che non interessano
al tipico sviluppatore mobile. Infine, questo strumento è rivolto solamente all’implementazione di una
applicazione, e non ha alcun tipo di supporto per le altre fasi di sviluppo, in particolare non supporta la
prototipizzazione e non presenta possibilità di integrazione o di comunicazione con strumenti di
prototipizzazione.
14
2.2 Presentazione della soluzione La descrizione dello stato dell’arte evidenzia alcune necessità fondamentali per il successo nel mercato
mobile
Sviluppo cross-platform
Condivisione di informazioni dal prototipo all’implementazione
Performance del processo di sviluppo
Performance del prodotto finito
In questo lavoro di tesi si vuole proporre una soluzione a queste sfide attraverso la realizzazione di
strumenti a supporto delle attività di prototipizzazione e di conseguente generazione automatica dell’app a
partire dal prototipo, adottando una soluzione cross-platform.
La soluzione proposta si basa sulla produzione di un’applicazione mobile a partire dalla definizione di un
modello strutturato dell’applicazione stessa. Questo approccio prende il nome di model – driven, ed è stato
oggetto di diversi studi da parte della comunità scientifica [23] [24] [25]. Esso prevede la definizione di una
struttura formale in cui memorizzare tutti gli elementi utili alla produzione del prodotto finito. Tale
struttura prende il nome di modello, e viene definito in base al dominio di applicazione dell’approccio
stesso. In questo lavoro viene adottato un approccio di tipo model – driven come punto di incontro tra la
progettazione grafica e la prototipizzazione di un’applicazione e il codice vero e proprio. Tale modello può,
infatti, essere definito a partire dall’attività di prototipizzazione dell’applicazione, e può in seguito essere
utilizzato in attività di generazione automatica del codice eseguibile, data la sua struttura formale.
Secondo questo schema, questo lavoro prevede la produzione di due strumenti: Protocode e
MobileCodeGenerator. Il primo supporta lo sviluppatore durante la prototipizzazione e la modellazione
dell’applicazione e rende disponibili le funzionalità caratteristiche di uno strumento di prototipizzazione:
inserimento di componenti tramite drag and drop, personalizzazione dei componenti stessi tramite
interfaccia grafica e possibilità di avere un’anteprima visuale immediata ed interattiva del lavoro. L’output
di questo strumento, e l’input del secondo strumento, è il modello dell’applicazione. Il secondo strumento
è un generatore di codice capace di tradurre il modello in input in un progetto di applicazione mobile
basato sul pattern architetturale Model – View – Controller.
Entrambi gli strumenti presentati non sono interamente frutto di questo lavoro di tesi. La prima versione di
Mobile Code Generator è nata nel 2012 dal lavoro di Gregorio Perego e Stefania Pezzetti [3] e
successivamente migliorata da Mattia Natali nel 2013 [4] che ha inoltre introdotto lo strumento Protocode
per la prototipizzazione della componente View. Nell’A.A. 2015/2016 Aldo Pintus [5] ha aggiunto la
possibilità di modellare la componente View di applicazioni per smartwatch. Infine, nell’A.A. 2016/2017
Alessio Rossotti [6] ha introdotto la possibilità di modellare la componente Model dell’applicazione.
Questo lavoro di tesi interviene su entrambi gli strumenti. Protocode viene aggiornato al fine di permettere
la modellazione e prototipizzazione grafica anche di alcune componenti afferenti all’area di competenza
della componente Controller, in particolare per quanto riguarda il binding di dati nella View, completando
così l’implementazione del pattern architetturale MVC. Le aree relative alla prototipizzazione di Model e
View restano inalterate. MobileCodeGenerator viene arricchito di un nuovo target della procedura di
generazione: la produzione di codice cross – platform per il framework ReactNative. Le motivazioni alla
base di tale scelta verranno dettagliate nel capitolo §5
15
Tale soluzione presenta elementi distintivi da tutti gli strumenti presenti sul mercato: Proto.io [7],
MarvelApp [10] e Adobe Experience Design CC [11] sono eccellenti nella prototipizzazione, ma mancano
della possibilità di generare automaticamente il codice sorgente dell’applicazione; Xamarin [12], PhoneGap
[15] e Titanium [18] sono buone soluzioni al problema dello sviluppo cross – platform, ma non si
interfacciano al meglio con strumenti di prototipizzazione; AndroidStudio [21] e XCode [22] non permettono
lo sviluppo cross – platform, pur permettendo la definizione grafica di molte componenti dell’applicazione;
DecoIDE [20] non è un tool basato su modelli ma su un riconoscimento efficace della sintassi e sulla
conseguente possibilità di agire, attraverso strumenti grafici, sulle proprietà passate ai componenti.
Rispetto ai requisiti richiesti, la soluzione proposta permette di ottenere un’applicazione cross-platform la
cui efficienza è supportata dal framework scelto. L’interazione tra prototipizzazione e implementazione è
garantita dal modello. Quanto alla performance del processo di sviluppo, essa verrà esaminata nel capitolo
§6
2.3 Struttura del documento Nei prossimi capitoli di questo documento verrà descritto l’approccio delineato nel paragrafo §2.2 e ne
verrà valutata la performance. In particolare
Il capitolo 3 descrive la struttura formale del modello di applicazione usato come interfaccia tra
Protocode e MobileCodeGenerator
Il capitolo 4 presenta le funzionalità e l’architettura di Protocode
Il capitolo 5 presenta le funzionalità e l’architettura di MobileCodeGenerator
Il capitolo 6 effettua una valutazione delle performance della soluzione proposta
Il capitolo 7 contiene alcune considerazioni finali sul lavoro svolto
16
3 Modello dell’applicazione L’approccio model-driven alla generazione di applicazioni mobili richiede che venga definito un modello
formale e strutturato di applicazione mobile. La definizione di tale modello costituisce un elemento di
primaria importanza in questo lavoro di tesi, dato che esso costituisce l’interfaccia tra i due strumenti che
compongono la soluzione proposta e il punto di partenza per l’implementazione degli stessi. In questo
capitolo viene presentata e discussa la struttura di tale modello. Al fine di agevolare la lettura, il modello
viene spezzato su una collezione di class diagram. Entità che appaiono in diagrammi diversi con lo stesso
nome sono da considerarsi la stessa entità.
Il modello descritto nei paragrafi successivi deriva in gran parte dai lavori di tesi precedenti ( [3] [4] [5] [6] ),
solo la parte relativa alla modellazione della componente Controller è frutto di questo lavoro di tesi.
3.1 Modello della View
3.1.1 Struttura generale
La componente visiva di un’applicazione mobile è essenzialmente costituita da un insieme di controlli, i
quali hanno il compito di interagire con l’utente, fornendo o raccogliendo informazioni. È intuitivo pensare
come un testo statico o un’immagine interagiscano con l’utente fornendogli informazioni testuali e
grafiche, e come una casella di testo interagisca con l’utente attraverso l’inserimento dei dati.
È possibile raggruppare i controlli a formare strutture che definiamo viewController. Un viewController è
costituito da un insieme di controlli aggregati a costituire una funzionalità precisa e riutilizzabile all’interno
dell’applicazione. È possibile pensare ad un viewController come ad una vista all’interno dell’applicazione.
I viewController possono essere a loro volta aggregati a formare scene. Ogni scena corrisponde ad un
momento nella navigazione all’interno di una applicazione. Le modalità con cui i viewController sono
aggregati possono essere varie: una scena può mostrare un viewController a tutto schermo, oppure può
fornire un menù a schede per ottenere la navigazione tra diversi viewController, sempre mostrati a tutto
schermo, oppure può frazionare la superficie disponibile sullo schermo e destinarne ciascuna parte ad un
diverso viewController.
Nel caso di logiche di aggregazione complesse, come ad esempio il partizionamento dello schermo, una
scena utilizza un componente, chiamato parentViewController, per gestire il layout. Un
parentViewController si pone ad un livello superiore rispetto ai viewController ordinari chiamati a
comporre la scena, ma può comunque essere considerato un particolare viewController in cui sono inseriti
appositi controlli, chiamati container, la cui funzione è quella di incapsulare a loro volta un viewController.
Un’ulteriore problematica risolta tramite l’adozione dei parentViewController riguarda l’adattabilità del
layout a dispositivi diversi: una scena può possedere da zero a due parentViewController, ciascuno
associato ad una classe di dispositivi (smartphone o tablet). Durante l’esecuzione, la scena seleziona il
parentViewController corrispondente al tipo di device che sta eseguendo l’applicazione, variando la
modalità di visualizzazione dei viewController in essa inseriti in modo anche radicale.
La View di una applicazione mobile è, quindi, definita da quattro livelli gerarchici: la scena, i
parentViewController, i viewController, i controlli utente. Il livello relativo ai parentViewController è
opzionale e può essere omesso nei casi in cui la logica di presentazione dei viewController componenti una
scena sia semplice.
17
Figura 3.1 Modello degli elementi di alto livello della componente View
La figura 3.1 descrive il modo in cui il modello di applicazione mobile in uso in questo lavoro di tesi
formalizza i livelli alti di questa gerarchia.
Application
La classe Application descrive le proprietà generali di un’applicazione mobile, il dettaglio delle quali è
riportato nella seguente tabella
name Il nome dell’applicazione
companyIdentifier L’identificativo del produttore dell’applicazione, creato secondo le regole a cui conformano i prefissi dei nomi dei package di una classica applicazione Java
scenes La collezione delle scene definite per l’applicazione
viewControllers La collezione dei viewController definiti per l’applicazione Tabella 3.1 Campi di Application
È necessario definire già a livello di Application la collezione dei viewController definiti per l’applicazione in
quanto la definizione di un viewController è indipendente dalla definizione della/e scena/e in cui esso sarà
incluso, e pertanto non è possibile stabilire una relazione di composizione tra una scena e i relativi
viewController componenti. Tale relazione di composizione andrebbe infatti ad impedire il riuso dei
viewController tra diverse scene, e questo farebbe venir meno il motivo stesso della definizione dei
viewController.
Scene
La classe Scene è dedicata a modellare le scene, in base alla descrizione di cui sopra. Le proprietà di una
scena sono dettagliate nella seguente tabella
18
id Identificativo univoco della scena (otto caratteri alfanumerici)
name Il nome distintivo della scena, assegnato dal modellatore
launcher Indica se una scena costituisce il punto di inizio della navigazione all’interno dell’applicazione
typeSmartphone Indica la logica di presentazione dei viewController componenti la scena su un device di tipo smartphone
typeTablet Indica la logica di presentazione dei viewController componenti la scena su un device di tipo tablet
childViewControllers Lista di riferimenti ai viewController componenti la scena
parentViewControllers Lista dei parentViewController definiti per la scena Tabella 3.2 Campi di scene
I campi typeSmartphone e typeTablet indicano la modalità di presentazione sullo schermo dei
viewController componenti la scena rispettivamente su smartphone e tablet. I valori possibili per tali campi
sono
SINGLE_VC: la scena presenta un solo viewController a tutto schermo. Nel caso in cui
childViewControllers contenga più elementi, viene selezionato il primo di essi. Il passaggio ad
eventuali altri elementi è a carico del designer, che lo può implementare tramite apposite azioni di
navigazione (§3.1.6)
SINGLE_VC_TAB: la scena presenta un solo viewController a tutto schermo. Nel caso in cui
childViewControllers contenga più elementi, viene mostrato il primo di essi. Il passaggio ad
eventuali altri elementi è possibile attraverso un menù a schede fornito automaticamente, oppure
tramite apposite azioni di navigazione (§3.1.6). La definizione di azioni di navigazione tuttavia non
altera il menù a schede, che contiene sempre e comunque una scheda per ogni viewController
componente
MULTI_VC: la scena mostra più viewController contemporaneamente, in base ad una logica
definita dal progettista. Tale logica è descritta attraverso un elemento di tipo parentViewController
contenuto nella lista parentViewControllers. La selezione del parentViewController dalla lista
avviene secondo le seguenti modalità:
o Se typeSmartphone ha valore MULTI_VC e typeTablet non ha valore MULTI_VC la lista
parentViewControllers contiene un solo elemento e tale elemento è il parentViewController
da utilizzare quando la scena viene mostrata su smartphone
o Se typeSmartphone non ha valore MULTI_VC e typeTablet ha valore MULTI_VC la lista
parentViewControllers contiene un solo elemento e tale elemento è il parentViewController
da utilizzare quando la scena viene mostrata su tablet
o Se typeSmartphone non ha valore MULTI_VC e typeTablet non ha valore MULTI_VC la lista
parentViewControllers è vuota
o Se typeSmartphone ha valore MULTI_VC e typeTablet ha valore MULTI_VC la lista
parentViewControllers contiene due elementi di tipo parentViewController di cui il primo
implementa la logica di visualizzazione su smartphone e il secondo su tablet
19
ViewController
La classe ViewController è dedicata alla modellazione dei viewController definiti per l’applicazione. Tale
classe è il modello di riferimento anche per i parentViewController, in quanto particolari viewController
dedicati a raggruppare controlli che agiscono come box per l’inclusione di altri viewController. La tabella 3.3
dettaglia le proprietà di un viewController.
id Identificativo univoco del viewController (otto caratteri alfanumerici) name Nome distintivo del viewController assegnato dal modellatore. Nel
momento in cui il viewController viene inserito in una scena con modalità di visualizzazione SCENE_VC_TAB, il titolo della scheda del menù associata al viewController corrisponde al suo nome.
backgroundColor Il colore di sfondo del viewController, espresso come stringa RGB esadecimale preceduta dal carattere #. E’ opzionale, e di default vale #FFFFFF (colore bianco)
backgroundImage Il percorso dell’immagine da utilizzare come sfondo del viewController Tabella 3.3 Campi di ViewController
La modellazione di un viewController non è limitata a tali campi, e verrà completata procedendo nella
trattazione (§3.1.3, §3.1.4, §3.1.5, §3.1.6, §3.1.7, §3.3).
ChildViewController
La classe ChildViewController permette la definizione del legame tra una scena e i viewController che la
compongono. Un oggetto di questa classe contiene solamente due riferimenti: il primo alla scena che
include il viewController, il secondo al viewController incluso. Al fine di preservare la riusabilità dei
viewController, quest’ultima relazione è unidirezionale.
3.1.2 Modellazione astratta dei controlli utente
Come descritto nel paragrafo §3.1.1, gli elementi di base utilizzati per la definizione della componente View
di un’applicazione sono i controlli utente. La modellazione di tali controlli richiede, quindi, particolare
attenzione. In questo capitolo verrà dapprima presentato il modello generale di un controllo utente e in
seguito i vari controlli che è possibile utilizzare all’interno di questo modello.
Figura 3.2 Modello astratto dei controlli utente
20
La figura 3.2 descrive l’astrazione utilizzata in questo modello per descrivere un generico controllo utente.
Ciò che caratterizza tutti i controlli utente è il fatto che essi devono essere posizionati sullo schermo
secondo una logica anche molto complessa, data la variabilità, in termini di dimensioni, degli schermi a cui
l’applicazione deve potersi adattare. Tutte le classi coinvolte nel modello astratto di un controllo utente
sono destinate a descrivere la logica di apparizione del controllo stesso sullo schermo.
Le possibili dinamiche di posizionamento definite in questo modello sono tre: il posizionamento assoluto, il
posizionamento vincolato e le catene di controlli. Il posizionamento assoluto prevede la definizione statica
delle coordinate (espresse in dip) dell’angolo superiore sinistro (destro per ambienti RTL) del componente.
Il posizionamento vincolato prevede la definizione di vincoli di posizione tra diversi elementi, o tra un
elemento e il suo contenitore. Tali vincoli si basano su condizioni di allineamento di elementi notevoli,
quali, ad esempio, il centro del controllo o il lato superiore. Il meccanismo di posizionamento vincolato
viene descritto nel capitolo PositionConstraint. Il posizionamento mediante catena di controlli prevede
l’appartenenza di un controllo ad una catena, caratterizzata da un asse (x o y) e da una politica di
suddivisione dello spazio. I controlli che fanno parte di una catena vengono allineati lungo l’asse della
catena stessa, e lo spazio disponibile sull’asse viene distribuito tra i vari controlli in base alla politica scelta.
Per maggiori dettagli si veda il capitolo ControlChain. Come per il posizionamento, è possibile esprimere
anche vincoli riguardo alla dimensione di un elemento. È possibile vincolare la larghezza, l’altezza o il loro
rapporto a valori statici (espressi in dip) o percentuali rispetto al contenitore. I vincoli di dimensione sono
dettagliati nel paragrafo DimensionConstraint.
UiControl
La classe astratta UiControl contiene le proprietà basilari di qualsiasi controllo utente, le quali sono
dettagliate in tabella 3.4. Essa è separata da UiPhoneControl in quanto le proprietà ivi definite sono comuni
anche a controlli destinati a piattaforme smartwatch o di altro genere.
id Identificativo univoco del controllo (stringa alfanumerica di 8 caratteri)
posX Coordinata orizzontale per il posizionamento assoluto
posY Coordinata verticale per il posizionamento assoluto
marginTop Dimensione, in dip, dell’ampiezza dello spazio vuoto sopra il lato superiore del controllo
marginBottom Dimensione, in dip, dell’ampiezza dello spazio vuoto sotto il lato inferiore del controllo
marginStart Dimensione, in dip, dell’ampiezza dello spazio vuoto oltre il lato sinistro (destro in ambienti RTL) del controllo
marginEnd Dimensione, in dip, dell’ampiezza dello spazio vuoto oltre il lato destro (sinistro in ambienti RTL) del controllo
paddingTop Dimensione, in dip, dell’ampiezza dello spazio vuoto tra il lato superiore del controllo e il contenuto del controllo stesso
paddingBottom Dimensione, in dip, dell’ampiezza dello spazio vuoto tra il lato inferiore del controllo e il contenuto del controllo stesso
paddingStart Dimensione, in dip, dell’ampiezza dello spazio vuoto tra il lato sinistro (destro in ambienti RTL) del controllo e il contenuto del controllo stesso
paddingEnd Dimensione, in dip, dell’ampiezza dello spazio vuoto tra il lato destro (sinistro in ambienti RTL) del controllo e il contenuto del controllo stesso
Tabella 3.4 Campi di UiControl
Questa classe di controlli fornisce, attraverso gli attributi posX e posY, il meccanismo di posizionamento
assoluto.
21
UiPhoneControl
La classe astratta UiPhoneControl modella un controllo utente di un’applicazione per smartphone o tablet.
Essa si occupa di gestire meccanismi di posizionamento avanzati, e fornisce la possibilità di specificare
vincoli di posizione. La tabella 3.5 descrive le proprietà di un UiPhoneControl.
defaultWidth Indica la larghezza che assume il componente in assenza di vincoli di dimensione relativi
defaultHeight Indica l’altezza che assume il componente in assenza di vincoli di dimensione relativi
controlChain Indica la catena di controlli di cui il controllo è parte. Se il controllo non è parte di alcuna catena questo attributo ha valore nullo.
indexInChain Indica la posizione di un controllo all’interno della catena in cui è inserito. Le posizioni sono contate a partire da 1. Se il controllo non è parte di alcuna catena questo attributo ha valore nullo.
precedentInChain Se il controllo corrente è inserito in una catena questo attributo contiene un riferimento al controllo che lo precede nella catena stessa. Se il controllo non è parte di alcuna catena o è il primo elemento di una catena questo attributo ha valore nullo.
followingInChain Se il controllo corrente è inserito in una catena questo attributo contiene un riferimento al controllo che lo segue nella catena stessa. Se il controllo non è parte di alcuna catena o è l’ultimo elemento di una catena questo attributo ha valore nullo.
weight Se il controllo corrente è inserito in una catena di tipo weighted, questo attributo contiene il peso relativo del controllo nella catena. Se il controllo non è parte di alcuna catena questo attributo ha valore nullo.
positionConstraints Collezione di vincoli di posizione applicati a questo elemento
dimensionConstraint Vincolo di dimensione applicato a questo elemento. È opzionale, e qualora non sia presente le dimensioni dell’elemento assumono i valori specificati da defaultWidth e defaultHeight
Tabella 3.5 Campi di UiPhoneControl
È immediato notare che la totalità degli attributi propri di UiPhoneControl è destinata alla gestione del
posizionamento e delle dimensioni di un controllo.
DimensionConstraint
La classe DimensionConstraint indica un vincolo applicato ad una o entrambe le dimensioni di un elemento.
La tabella 3.6 presenta i campi di tale classe, ciascuno dei quali corrisponde ad un preciso vincolo. Alcuni
vincoli si riferiscono all’elemento contenitore che, in questo modello, coincide sempre con il viewController
in cui il controllo è inserito.
fixedWidth Impone all’elemento a cui è applicato una larghezza assoluta
fixedHeight Impone all’elemento a cui è applicato un’altezza assoluta
fixedRatio Impone all’elemento a cui è applicato un rapporto fisso tra larghezza e altezza, codificato come un numero razionale nella forma numeratore:denominatore.
widthPercent Impone all’elemento a cui è applicato una larghezza percentuale rispetto al contenitore
heightPercent Impone all’elemento a cui è applicato un’altezza percentuale rispetto al contenitore
Tabella 3.6 Campi di DimensionConstraint
22
Al fine di non creare situazioni di vincoli non risolvibili o contraddittori, è possibile specificare solo due
attributi per ogni istanza di DimensionConstraint. Le combinazioni possibili sono riportate nella seguente
tabella 3.7.
fixedWidth fixedHeight fixedRatio widthPercent heightPercent
fixedWidth -
fixedHeight - fixedRatio -
widthPercent -
heightPercent - Tabella 3.7 Possibili combinazioni di vincoli di dimensione
PositionConstraint
Ogni controllo inserito in un viewController possiede una collezione di vincoli ad esso applicati, ciascuno dei
quali è un’istanza della classe PositionConstraint.
Ogni vincolo è definito tra due elementi: il primo è l’elemento a cui il vincolo è applicato e a cui il vincolo
appartiene, il secondo è l’elemento che agisce come riferimento per quel vincolo. Il primo elemento verrà
chiamato controllo proprietario, mentre il secondo controllo di appoggio. Il ruolo del controllo di appoggio
può essere assunto anche dal viewController in cui il controllo che possiede il vincolo è inserito: un vincolo
di questo tipo è definito vincolo con il padre.
Definiamo bounding box di un controllo utente il più piccolo rettangolo capace di contenere l’intero
contenuto del controllo ed il padding ad esso applicato. Definiamo quindi i seguenti elementi rilevanti di
controllo utente
top: la coordinata lungo l’asse verticale del lato superiore del bounding box del controllo
bottom: la coordinata lungo l’asse verticale del lato inferiore del bounding box del controllo
centerY: la posizione del centro del bounding box lungo l’asse verticale del controllo
left: la coordinata lungo l’asse orizzontale del lato sinistro del bounding box del controllo
right: la coordinata lungo l’asse orizzontale del lato destro del bounding box del controllo
centerX: la posizione del centro del bounding box lungo l’asse verticale del controllo
Esprimere un vincolo significa stabilire una relazione tra un elemento rilevante del controllo proprietario e
un elemento rilevante del controllo di appoggio. Tale relazione è significativa solo per alcune coppie di
elementi rilevanti, le quali sono descritte nella tabella 3.8. I valori contrassegnati da un asterisco indicano
che tale combinazione è possibile soltanto se il vincolo non viene stabilito con il padre.
Elemento di appoggio
Elemento proprietario
top bottom centerY left right centerX top *
bottom *
centerY
left *
right *
centerX
Tabella 3.8 Relazioni possibili tra elementi rilevanti
Ad esempio è possibile relazionare il top dell’elemento proprietario con il bottom dell’elemento di
riferimento per creare un layout verticale in cui i due componenti sono disposti uno sotto l’altro.
23
Tali combinazioni tuttavia non escludono la possibilità di definire, per un elemento, un insieme di vincoli
non risolvibile, ovvero un insieme in cui siano presenti due o più vincoli in contraddizione. La seguente
tabella 3.9 indica quali elementi rilevanti siano contemporaneamente vincolabili. In caso di più vincoli
applicati, gli elementi ancora vincolabili sono dati dall’intersezione logica delle righe relative ai vincoli già
presenti.
Elementi rilevanti ancora liberi per la definizione di ulteriori vincoli
Elemento rilevante vincolato
top bottom centerY left right centerX top
bottom
centerY
left
right
centerX
Tabella 3.9 Coesistenza di vincoli
Nel caso in cui un elemento appartenga ad una catena di controlli (si veda ControlChain):
sono considerati già vincolati gli elementi rilevanti top, bottom e centerY nel caso in cui la catena
abbia asse verticale
sono considerati già vincolati gli elementi rilevanti left, right e centerX nel caso in cui la catena
abbia asse orizzontale
La definizione di vincolo che abbiamo dato consente di definire la relazione R(x, y) che contiene tutte le
coppie di controlli (x,y) tali per cui esiste un vincolo appartenente ad x e avente come appoggio y. Tale
relazione è rappresentabile mediante un grafo. In Protocode, tale grafo è un DAG (Directed Acyclic Graph,
grafo orientato aciclico). Nel caso in cui l’utente inserisca un vincolo che introduce un ciclo nel suddetto
grafo, tale vincolo viene marcato come invalido ed escluso dal set di vincoli effettivamente applicati
all’elemento stesso.
id Identificativo univoco del vincolo (stringa alfanumerica di 8 caratteri)
layoutEdge Elemento rilevante del controllo proprietario coinvolto nel vincolo
withParent Vero se il controllo di appoggio è l’elemento contenitore, falso altrimenti
referenceLayoutEdge Elemento rilevante del controllo di appoggio coinvolto nel vincolo
referenceElement Riferimento al controllo di appoggio del vincolo. Se il vincolo è con il padre questo attributo ha valore nullo
Tabella 3.10 Campi di PositionConstraint
ControlChain
Una control chain, o catena di controlli, è un meccanismo di layout introdotto al fine di rendere possibile la
ripartizione dello spazio disponibile tra diversi controlli. Le dinamiche del posizionamento vincolato
presentano due limitazioni alla definizione di layout complessi: sono separate dalle dinamiche di calcolo
delle dimensioni dei componenti e operano su due componenti alla volta, perdendo quindi la visione
d’insieme.
Una catena di controlli permette la distribuzione di una lista ordinata di controlli lungo un asse fissato, che
può essere l’asse verticale o l’asse orizzontale. L’ordine dei componenti nella lista associata ad una catena
riflette l’ordine in cui essi vengono disposti lungo l’asse: se l’asse è orizzontale, i controlli sono distribuiti da
sinistra a destra; se l’asse è verticale, i controlli sono distribuiti dall’alto verso il basso. Nel contesto di
24
questo modello sono definite quattro tipologie di catene, ciascuna corrispondente ad una diversa dinamica
di distribuzione: spread, spread inside, packed e weighted.
Definiamo punto di inizio dell’asse di una catena orizzontale la posizione lungo l’asse del bordo sinistro
dell’elemento contenitore, e punto finale la posizione del bordo destro dello stesso lungo l’asse della
catena. Analogamente, per una catena verticale il punto iniziale è definito dal bordo superiore e il punto
finale dal bordo inferiore. Definiamo altresì alcune funzioni e variabili che verranno utilizzate durante la
definizione delle varie dinamiche di distribuzione
𝑛 è il numero di controlli inseriti nella catena
𝑚𝑒𝑚𝑏𝑒𝑟𝑠 è la lista che contiene i controlli inseriti nella catena
𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖] è l’𝑖-esimo controllo facente parte della catena (per 0 ≤ 𝑖 ≤ 𝑛)
𝑑𝑖𝑚(𝑐) è una funzione che restituisce la dimensione di un controllo lungo l’asse della catena,
margini inclusi. Se la catena è verticale, tale funzione restituisce la somma tra l’altezza, il margine
superiore e il margine inferiore del controllo 𝑐. Se invece la catena è orizzontale tale funzione
restituisce la somma tra la larghezza, il margine sinistro e il margine destro del controllo 𝑐
𝑚𝑎𝑟𝑔𝑖𝑛𝑆𝑡𝑎𝑟𝑡(𝑐) è una funzione che restituisce il margine che precede il controllo 𝑐 lungo l’asse
della catena. Tale margine coincide con il margine sinistro se la catena è orizzontale e con il
margine superiore se la catena è verticale.
𝐿𝐸𝑁 la lunghezza dell’asse (in dip)
Definiamo infine posizione di un elemento la coordinata lungo l’asse del primo punto appartenente
all’elemento (il margine non è considerato parte dell’elemento).
In una catena di tipo spread, i componenti sono distribuiti lungo l’asse selezionato in modo che sia costante
la dimensione dello spazio bianco tra ciascuna coppia di componenti consecutivi; tale spazio si presenta
anche tra il primo controllo e il punto di inizio dell’asse e tra l’ultimo controllo e il punto finale dell’asse.
Qualora gli elementi della catena possedessero un margine non nullo lungo l’asse della catena, tale margine
viene calcolato come parte della dimensione dell’elemento stesso. La dimensione dei componenti di una
catena di tipo spread non è influenzata dalla loro appartenenza alla stessa. In particolare, lo spazio libero
totale 𝐿 è calcolato come 𝐿 = 𝐿𝐸𝑁 − ∑ 𝑑𝑖𝑚(𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖])𝑛𝑖=0 . A partire da 𝐿 viene calcolato lo spazio tra
due componenti 𝑠 =𝐿
𝑛+1. Per quanto detto, l’𝑖-esimo elemento della catena avrà posizione 𝑝(𝑖) = 𝑠 ∗
(𝑖 + 1) + ∑ 𝑑𝑖𝑚 (𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑗])𝑖−1𝑗=0 + 𝑚𝑎𝑟𝑔𝑖𝑛𝑆𝑡𝑎𝑟𝑡(𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖]) lungo l’asse della catena.
Una catena di tipo spread inside segue le stesse regole di una catena di tipo spread, con la sola differenza
che non vi è alcuno spazio bianco (a meno del margine dell’elemento) né tra il primo elemento della catena
e il punto iniziale dell’asse né tra l’ultimo elemento della catena e il punto finale dell’asse. Rispetto al caso
precedente quindi le equazioni di calcolo della posizione risultano così modificate
𝐿 = 𝐿𝐸𝑁 − ∑ 𝑑𝑖𝑚(𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖])
𝑛
𝑖=0
𝑠 =𝐿
𝑛 − 1
𝑝(𝑖) = 𝑠 ∗ 𝑖 + ∑ 𝑑𝑖𝑚 (𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑗])
𝑖−1
𝑗=0
+ 𝑚𝑎𝑟𝑔𝑖𝑛𝑆𝑡𝑎𝑟𝑡(𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖])
25
Una catena di tipo packed distribuisce i controlli in modo che ognuno di essi sia affiancato al controllo
precedente e successivo senza spazio bianco a meno del margine e di una costante definita per la catena e
detta spacing: lo spazio bianco tra elementi adiacenti è dato dalla sola somma del margine finale del primo
componente con il margine iniziale del primo componente e con tale costante. La dimensione dei controlli
appartenenti ad una catena di tipo packed non è influenzata dalla loro appartenenza alla stessa. Lo spazio
in eccesso è suddiviso in due e disposto prima e dopo il gruppo di controlli. La suddivisione è controllata da
un parametro della catena chiamato 𝑏𝑖𝑎𝑠. La posizione dei vari controlli inseriti nella catena può essere
definita a partire da una funzione ausiliaria 𝑧(𝑖) nel seguente modo
𝐿 = 𝐿𝐸𝑁 − (∑ 𝑑𝑖𝑚(𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖])
𝑛
𝑖=0
+ (𝑛 − 1) ∗ 𝑠𝑝𝑎𝑐𝑖𝑛𝑔))
𝑧(0) = 𝐿 ∗ 𝑏𝑖𝑎𝑠
𝑧(𝑖) = 𝑧(𝑖 − 1) + 𝑠𝑝𝑎𝑐𝑖𝑛𝑔 + 𝑑𝑖𝑚(𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖 − 1]) per 1 ≤ 𝑖 ≤ 𝑛
𝑝(𝑖) = 𝑧(𝑖) + 𝑚𝑎𝑟𝑔𝑖𝑛𝑆𝑡𝑎𝑟𝑡(𝑖)
Una catena di tipo weighted agisce sulla dimensione lungo l’asse della catena dei controlli ad essa
appartenenti. Ad ogni elemento appartenente alla catena viene associato un numero intero detto weight o
peso. Sulla catena viene inoltre definito il parametro spacing, che definisce la dimensione dello spazio
bianco tra due componenti adiacenti. Tale spazio non viene inserito prima del primo componente o dopo
l’ultimo. La posizione 𝑝 e la dimensione 𝑔 di ciascun elemento appartenente alla catena possono essere
definite come segue (𝑝 e 𝑔 prendono sono parametriche in base all’indice del componente nella catena)
𝐿 = 𝐿𝐸𝑁 − (𝑛 − 1) ∗ 𝑠𝑝𝑎𝑐𝑖𝑛𝑔 𝑊 = ∑ 𝑤𝑒𝑖𝑔ℎ𝑡(𝑖)𝑛𝑖=0
𝑘(𝑖) =𝐿
𝑊∗ 𝑤𝑒𝑖𝑔ℎ𝑡(𝑚𝑒𝑚𝑏𝑒𝑟𝑠[𝑖])
𝑝(𝑖) = 𝑖 ∗ 𝑠𝑝𝑎𝑐𝑖𝑛𝑔 + ∑ 𝑘(𝑗) + 𝑚𝑎𝑟𝑔𝑖𝑛𝑆𝑡𝑎𝑟𝑡(𝑖)
𝑖−1
𝑗=0
𝑔(𝑖) = 𝑘(𝑖) − 𝑚𝑎𝑟𝑔𝑖𝑛𝑆𝑡𝑎𝑟𝑡(𝑖) − 𝑚𝑎𝑟𝑔𝑖𝑛𝐸𝑛𝑑(𝑖)
Tutte le rappresentazioni descritte possono essere modellate tramite la definizione della classe
controlChain in figura 3.2, i cui campi sono descritti nella seguente tabella 3.11.
id Identificativo univoco della catena (otto caratteri alfanumerici)
axis L’asse lungo cui la catena distribuisce i controlli ad essa appartenenti
type Il tipo di catena, che determina l’algoritmo di distribuzione
nControls Il numero di elementi inseriti nella catena
spacing Il parametro spacing descritto per le tipologie packed e weighted
bias Il parametro bias descritto per le catene di tipo packed Tabella 3.11 Campi di ControlChain
3.1.3 Modellazione dei controlli con interazione unidirezionale con l’utente
In questa sezione andremo a specializzare il modello astratto di un controllo utente descritto nel capitolo
§3.1.2 per i controlli che presentano un’interazione unidirezionale con l’utente. I controlli in questo gruppo
infatti sono esclusivamente dedicati a fornire informazioni, di diversa tipologia, all’utente; non è possibile
26
per l’utente inserire informazioni nell’applicazione tramite questi controlli. Ciascuno dei controlli descritti
estende la classe UiPhoneControl descritta nel capitolo UiPhoneControl con attributi propri e caratteristici.
Figura 3.3 Modello dei componenti con interazione unidirezionale con l’utente
Al fine di includere nel modello i controlli di questo gruppo, la classe ViewController presentata nel capitolo
ViewController viene arricchita delle seguenti relazioni
containers Collezione dei controlli di classe Container inseriti nel viewController. Questa collezione è non vuota solo per i parentViewController
cards Collezione dei controlli di tipo Card inseriti nel viewController
maps Collezione dei controlli di tipo Map inseriti nel viewController
textViews Collezione dei controlli di tipo TextView inseriti nel viewController
webViews Collezione dei controlli di tipo WebView inseriti nel viewController Tabella 3.12 Estensione di ViewController ai controlli con interazione unidirezionale
TextView
Una textView, detta anche casella di testo o label, è un controllo che contiene esclusivamente del testo
statico, nel senso che non può essere modificato direttamente dall’utente dell’applicazione. Al testo
contenuto in una textView possono essere applicate diverse personalizzazioni:
È possibile modificare la modalità di allineamento del testo all’interno della casella di testo
scegliendo tra l’allineamento a sinistra (predefinito), l’allineamento centrato e l’allineamento a
destra
È possibile applicare effetti di evidenziazione del testo scegliendo tra grassetto o corsivo. Nessuno
stile è applicato per impostazione predefinita.
È possibile modificare il colore del testo scegliendo qualsiasi colore dello spazio RGB
È possibile modificare la dimensione del testo (espressa in dip)
27
La classe che descrive una textView all’interno del modello possiede quindi i seguenti campi
content La stringa di testo contenuta nel controllo
textAlign La modalità di allineamento del testo
textDecoration La decorazione applicata al testo
textColor Il colore del testo
textSize La dimensione del testo Tabella 3.13 Campi di TextView
WebView
Una webView è un controllo capace di emulare il funzionamento di un browser e permettendo quindi di
includere una pagina web all’interno dell’applicazione. La pagina mostrata può essere una risorsa
disponibile in Internet o un file HTML statico incluso nell’applicazione. Il percorso locale o di rete del file
mostrato nella WebView è contenuto nel parametro HTMLFileName.
Map
Il controllo Map è in grado di mostrare all’utente una mappa stradale centrata su uno specifico punto della
superficie terrestre. Il punto su cui la mappa è inizialmente centrata viene scelto dal designer, ma l’utente
ha la possibilità di interagire con la mappa muovendola e cambiando quindi l’area geografica visualizzata.
Tale azione tuttavia non ha ripercussioni dirette sui dati inseriti nell’applicazione. Le proprietà di un
controllo Map sono descritte nella seguente tabella
lat Latitudine su cui la mappa è centrata dal designer
lon Longitudine su cui la mappa è centrata dal designer Tabella 3.14 Campi di Map
Card
Il controllo Card viene utilizzato per mostrare all’utente un contenuto e le azioni ad esso correlate. Tale
contenuto è caratterizzato da un titolo, un sottotitolo (che può anche essere una breve descrizione) e un
numero variabile di azioni. Il comportamento di tali azioni non è incluso in questo modello.
title Il titolo del contenuto della Card
subtitle Il sottotitolo, o una breve descrizione, del contenuto della Card
numActions Il numero di azioni associate al contenuto della Card. Non è possibile in questo modello personalizzare il nome o il comportamento delle diverse azioni
Tabella 3.15 Campi di Card
Container
Un controllo di classe Container può essere inserito solamente in un parentViewController. Un container si
occupa di delimitare una porzione della superficie del viewController in cui è inserito e di destinarla ad un
altro viewController, che viene istanziato su tale superficie. Questo controllo consente, quindi, di realizzare
qualsiasi logica arbitraria di suddivisione dello schermo tra diversi viewController, purché tale logica si
conformi all’ipotesi che l’area destinata ad un viewController ha forma rettangolare. La proprietà
childViewController contiene un riferimento al viewController che deve essere inserito nello spazio su cui il
container si estende.
3.1.4 Modellazione dei controlli multimediali
In questa sezione andremo a specializzare il modello astratto di un controllo utente descritto nel capitolo
§3.1.2 per i controlli che presentano un’interazione multimediale con l’utente. In questo modello definiamo
28
controlli multimediali tutti quei controlli che utilizzano immagini, flussi audio o flussi video. Ciascuno dei
controlli descritti estende la classe UiPhoneControl con attributi propri e caratteristici.
Figura 3.4 Modellazione dei controlli multimediali
Al fine di includere nel modello i controlli di questo gruppo, la classe ViewController viene arricchita delle
seguenti relazioni
audioPlayers Collezione dei controlli di classe AudioPlayer inseriti nel viewController
audioRecorder Riferimento all’eventuale controllo di classe AudioRecorder inserito nel viewController
imageViews Collezione dei controlli di classe ImageView inseriti nel viewController
photocameraController Riferimento all’eventuale controllo di classe PhotocameraController inserito nel viewController
videoViews Collezione dei controlli di classe VideoView inseriti nel viewController
videocameraController Riferimento all’eventuale controllo di tipo VideocameraController inserito nel viewController
Tabella 3.16 Estensione di ViewController ai controlli multimediali
Il modello in uso in questo lavoro di tesi permette la gestione di tre tipologie di elementi multimediali:
immagini, audio e video. Per ciascuna di esse, è definita una coppia di controlli: un controllo capace di
riprodurre un contenuto multimediale, l’altro controllo capace di acquisirlo. È possibile utilizzare i controlli
di riproduzione in modo autonomo o in modo combinato con un controllo di acquisizione. La modalità
autonoma prevede che il designer selezioni il contenuto da riprodurre e indichi il percorso, locale o di rete,
del file in cui tale contenuto è archiviato. La modalità combinata prevede che il designer colleghi un
controllo di acquisizione con un controllo di riproduzione: quest’ultimo mostrerà il risultato dell’ultima
operazione di acquisizione.
Ciascun controllo destinato alla riproduzione di elementi multimediali può, quindi, funzionare con tre
tipologie di sorgenti
Hardware file (preview): il controllo funziona in modalità combinata
29
Local file: il controllo funziona in modalità autonoma e riproduce il contenuto di un file archiviato
sul filesystem del dispositivo
Remote file: il controllo funziona in modalità autonoma e riproduce il contenuto di un flusso
multimediale disponibile in rete
AudioPlayer
Un AudioPlayer è un controllo capace di riprodurre un flusso multimediale di tipo audio. La tabella
seguente descrive le proprietà di questa classe di controlli
sourceType La tipologia di sorgente utilizzata
fileUri Per sorgenti di tipo Local file o Remote file, il percorso, rispettivamente locale e di rete, del file da riprodurre
Tabella 3.17 Campi di AudioPlayer
AudioRecorder
Un AudioRecorder è un controllo capace di acquisire un flusso audio dall’utente attraverso il microfono del
dispositivo e di salvarlo nella memoria persistente del dispositivo stesso, eventualmente inviandolo anche
ad un AudioPlayer. L’unico attributo di questa classe, chiamato audioPlayerId, contiene l’identificativo
univoco del controllo AudioPlayer che riproduce i flussi multimediali acquisiti dal registratore.
ImageView
Un’ImageView è un controllo capace di visualizzare un’immagine. La tabella seguente descrive le proprietà
di questa classe di controlli
sourceType La tipologia di sorgente utilizzata
fileUri Per sorgenti di tipo Local file o Remote file, il percorso, rispettivamente locale e di rete, dell’immagine da visualizzare
Tabella 3.18 Campi di ImageView
PhotocameraController
Un PhotocameraController è un controllo che consiste in un pulsante capace di avviare la fotocamera per
l’acquisizione di una fotografia. L’immagine acquisita viene salvata sul dispositivo ed eventualmente
mostrata in una ImageView collegata. Il pulsante può mostrare due tipologia di contenuto: la scritta camera
o l’icona di una fotocamera. L’implementazione di tale classe di controlli richiede quindi i seguenti attributi
backgroundType Il tipo di contenuto del pulsante
imageViewId L’identificativo unico del controllo ImageView che mostra l’ultima immagine acquisita tramite questo controllo
Tabella 3.19 Campi di PhotocameraController
VideoView
Una VideoView è un controllo capace di riprodurre un video. La tabella seguente descrive le proprietà di
questa classe di controlli
sourceType La tipologia di sorgente utilizzata
fileUri Per sorgenti di tipo Local file o Remote file, il percorso, rispettivamente locale e di rete, del file video da riprodurre
Tabella 3.20 Campi di ImageView
30
VideocameraController
Un VideocameraController è un controllo che consiste in un pulsante capace di avviare la fotocamera per
l’acquisizione di un video. Il video viene salvato sul dispositivo ed eventualmente riprodotto in una
VideoView collegata. Il pulsante può mostrare due tipologia di contenuto: la scritta video o l’icona di una
videocamera. L’implementazione di tale classe di controlli richiede quindi i seguenti attributi
backgroundType Il tipo di contenuto del pulsante
videoViewId L’identificativo unico del controllo VideoView che mostra l’ultimo video acquisito tramite questo controllo
Tabella 3.21 Campi di VideocameraController
3.1.5 Modellazione di controlli che permettono l’inserimento di informazioni
In questa sezione andremo a specializzare il modello astratto di un controllo utente descritto nel capitolo
§3.1.2 per i controlli che presentano un’interazione bidirezionale con l’utente. Tali controlli permettono sia
all’applicazione di mostrare dati all’utente sia all’utente di agire sui dati mostrati, modificandoli. Ciascuno
dei controlli descritti estende la classe UiPhoneControl con attributi propri e caratteristici.
Figura 3.5 Modellazione dei controlli con interazione bidirezionale con l'utente
Al fine di includere nel modello i controlli di questo gruppo, la classe ViewController viene arricchita delle
seguenti relazioni
datePickers Collezione dei controlli di classe DatePicker inseriti nel viewController
editTexts Collezione dei controlli di classe EditText inseriti nel viewController
sliders Collezione dei controlli di classe Slider inseriti nel viewController
spinners Collezione dei controlli di classe Spinner inseriti nel viewController
switches Collezione dei controlli di classe Switch inseriti nel viewController
timePickers Collezione dei controlli di classe TimePicker inseriti nel viewController Tabella 3.22 Estensione di ViewController ai controlli con interazione bidirezionale con l’utente
31
EditText
La classe EditText rappresenta controlli ad interazione bidirezionale di tipo testuale. Ogni controllo di
questo tipo contiene un testo che può essere modificato dall’utente. Se il testo da mostrare non contiene
caratteri, il controllo può apparire vuoto oppure mostrare un messaggio predefinito chiamato placeholder.
Inoltre, è possibile per il designer personalizzare la dimensione e il colore del testo contenuto in questo
controllo. La personalizzazione del colore non riguarda il placeholder, al contrario di quella sulla dimensione
del testo.
L’implementazione di questa tipologia di controlli utente richiede quindi i seguenti attributi
initialContent Il testo contenuto nel controllo prima che l’utente lo modifichi
placeholder Il messaggio placeholder per il controllo
textColor Il colore del testo contenuto nel controllo
textSize La dimensione (in dip) del testo contenuto nel controllo Tabella 3.23 Campi di EditText
Slider
Un controllo di tipo Slider permette all’utente di selezionare un valore da un insieme discreto e opaco di
valori ordinati. Esso si presenta come una barra orizzontale su cui è innestato un cursore: muovendo il
cursore a destra o a sinistra si scorre l’insieme dei valori possibili per il controllo. Tipicamente l’insieme di
base è un intervallo sull’insieme dei numeri naturali. Questo controllo è molto usato anche nelle
funzionalità del sistema operativo: ad esempio, viene utilizzato nelle interfacce di controllo dei dispositivi
audio per la regolazione del volume. Dato il basso livello di personalizzazione disponibile, tale controllo non
ha proprietà caratteristiche.
Spinner
Un controllo di tipo Spinner viene utilizzato per permettere all’utente di selezionare un valore da una lista
di opzioni, in modo analogo ai menu a tendina tipici delle interfacce di ambienti desktop. In questo modello
non è possibile definire la lista delle opzioni o l’opzione selezionata in modo predefinito. Dato il basso
livello di personalizzazione disponibile, tale controllo non ha proprietà caratteristiche.
Switch
Un controllo di tipo Switch viene utilizzato per permettere all’utente di modificare in modo intuitivo un
valore booleano. Il comportamento di tale controllo è analogo a quello di un interruttore a due fasi,
ciascuna delle quali corrisponde ad un valore logico booleano. Il valore predefinito del controllo è false.
Dato il basso livello di personalizzazione disponibile, tale controllo non ha proprietà caratteristiche.
DatePicker e TimePicker
Un controllo di tipo DatePicker permette la visualizzazione e la modifica di una data, mentre un controllo di
tipo TimePicker permette la visualizzazione e la modifica di un orario. Tipicamente si presentano, in
modalità visualizzazione, come una normale casella di testo contenente una data o un orario formattati in
base alle convenzioni locali del dispositivo, mentre in modalità modifica presentano una UI che supporta
l’utente nell’operazione. Tale interfaccia è controllata dal sistema operativo su cui l’app è in esecuzione.
Dato il basso livello di personalizzazione disponibile, tale controllo non ha proprietà caratteristiche.
3.1.6 Modellazione di controlli con ruoli di navigazione
In questa sezione andremo a specializzare il modello astratto di un controllo utente descritto nel capitolo
§3.1.2 per i controlli che implementano funzionalità di navigazione all’interno dell’applicazione.
32
Questo modello contiene due tipologie di azioni di navigazione: la navigazione all’interno di una scena e la
navigazione tra scene diverse. Una scena può essere composta da più viewController, ma nella maggior
parte dei casi questi non sono tutti simultaneamente visibili sullo schermo. Nel caso specifico di una scena
di tipo SINGLE_VC_TAB (si veda il paragrafo §3.1.1) solo una vista è visibile in un dato istante, ma è possibile
cambiarla tramite l’apposito menù. Nel caso invece di una scena di tipo SINGLE_VC la navigazione a schede
non è presente, ed è quindi necessario introdurre la possibilità di implementare un meccanismo di
passaggio da una vista ad un’altra. Tale meccanismo di navigazione interno ad una scena può essere
implementato assegnando ad appositi controlli azioni di navigazione aventi come destinazione un
viewController appartenente alla scena da cui l’azione origina. È possibile assegnare a tali controlli
un’azione diversa per ogni contesto di partenza. In altre parole, è possibile assegnare a tali controlli un
viewController di destinazione diverso per ogni scena in cui viene incluso il viewController a cui il controllo
appartiene. La navigazione tra scene diverse può essere implementata nel medesimo modo, se l’azione di
navigazione origina da un controllo utente. La navigazione tra scene tuttavia deve essere resa possibile
anche nel caso in cui non ci sia un contesto di partenza: è il caso del menù dell’applicazione, che consente
di passare a qualunque scena indipendentemente dal contesto di partenza.
Il seguente diagramma mostra le parti del modello dedicate ad implementare tali meccanismi di
navigazione.
Figura 3.6 Modellazione dei controlli relativi alla navigazione
Oltre al Menu, i controlli che hanno un ruolo nella navigazione all’interno dell’applicazione sono tre: Button,
ListView e GridView. Il diagramma mostra infatti come queste entità contengano uno o più oggetti che
descrivono azioni di navigazione (istanze di Navigation). Tutte le altre entità introdotte in questo
diagramma sono entità accessorie al funzionamento dei controlli di navigazione, ma non hanno un ruolo di
primo piano per quanto riguarda la navigazione stessa.
33
Al fine di includere nel modello i controlli di questo gruppo, la classe ViewController viene arricchita delle
seguenti relazioni
gridViews Collezione dei controlli di classe GridView inseriti nel viewController
listViews Collezione dei controlli di classe ListView inseriti nel viewController
buttons Collezione dei controlli di classe Button inseriti nel viewController Tabella 3.24 Estensione di ViewController ai controlli con ruolo nella navigazione
Anche la classe Application viene arricchita di una relazione
menu Collezione dei controlli di classe DatePicker inseriti nel viewController Tabella 3.25 Estensione di ViewController ai controlli con ruolo nella navigazione
Nel seguito di questo capitolo descriveremo prima la modellazione dell’azione di navigazione, quindi i vari
controlli che hanno capacità di navigazione. Gli elementi accessori verranno trattati insieme agli elementi a
cui sono collegati.
Navigation
La classe Navigation descrive un’azione di navigazione all’interno dell’applicazione. Il modello di
navigazione adottato è molto semplice, e consente la sola definizione della destinazione, dato il contesto di
partenza. Nonostante sia molto semplice, questo modello permette la gestione di diversi casi
La navigazione a partire da un elemento del menu principale non è caratterizzata da un contesto di
partenza, ma solo da un elemento di arrivo, e tale elemento è sempre una scena
La navigazione a partire da un controllo utente ha sempre un contesto di partenza costituito dal
viewController in cui il controllo è inserito e soprattutto dalla scena di cui il viewController è parte
nel momento in cui origina l’azione di navigazione. Le destinazioni possibili possono essere due: un
altro viewController della stessa scena o una scena diversa
L’implementazione riportata nel diagramma in figura 3.6 permette di gestire tutti questi casi. La seguente
tabella descrive il ruolo dei vari attributi della classe.
id Identificativo univoco dell’azione di navigazione (otto caratteri alfanumerici)
contextScene Riferimento alla scena da cui origina l’azione di navigazione. Ha valore nullo in caso di navigazione a partire dal menu principale
destinationScene La scena di destinazione dell’azione di navigazione. Se la destinazione è un viewController all’interno della scena associata a contextScene, questo attributo ha valore nullo
destinationViewController Riferimento al viewController all’interno della scena associata a contextScene che costituisce la destinazione della navigazione. Se l’azione di navigazione ha come destinazione una scena, questo attributo assume valore nullo
Tabella 3.26 Campi di Navigation
Menu e MenuItem
La classe Menu descrive il menu principale dell’applicazione. In questo modello il menù è ridotto ad una
collezione di oggetti di classe MenuItem, ciascuno dei quali rappresenta una voce all’interno del menu
principale. Ogni elemento del menù è caratterizzato da un titolo descrittivo e contiene una sola azione di
navigazione, al fine di evitare ambiguità nella scelta della destinazione di navigazione.
Le seguenti tabelle descrivono i campi di Menu e MenuItem necessari alla loro descrizione
34
menuItems Lista ordinata degli elementi da includere nel menù dell’applicazione. Nel menù applicativo, gli elementi sono mostrati nell’ordine definito dalla lista
Tabella 3.27 Campi di Menu
id Identificativo univoco dell’elemento del menù (stringa alfanumerica da otto caratteri)
title Il titolo dell’elemento di menù, ovvero il testo descrittivo di questo elemento che appare all’utente quando apre il menù
navigation L’azione di navigazione da eseguire quando l’utente seleziona questo elemento dal menù
Tabella 3.28 Campi di MenuItem
Button
Un elemento di classe Button rappresenta un pulsante che l’utente può cliccare. Un pulsante è un controllo
utente, e in quanto tale estende la classe UiPhoneControl.
Un pulsante è caratterizzato dal testo in esso contenuto, che ne descrive la funzionalità all’interno
dell’applicazione, e da vari parametri di personalizzazione stilistica. In questo modello è infatti possibile
definire il raggio con il quale vengono arrotondati i bordi del pulsante e modificare il colore del testo in esso
contenuto. È inoltre possibile modificare il colore di sfondo del pulsante e definire un colore che ne prende
temporaneamente il posto durante la pressione da parte dell’utente. Il valore predefinito per
l’arrotondamento dei bordi è 2 dip, il colore predefinito del testo è il nero, mentre i colori di sfondo e di
pressione sono dipendenti dal sistema operativo.
È possibile associare ad ogni pulsante una azione di navigazione per ciascuna scena in cui viene istanziato il
viewController a cui il pulsante appartiene. Ogni pulsante quindi possiede una collezione di azioni di
navigazione, che globalmente implementano una funzione di navigazione parametrica rispetto alla scena di
origine della navigazione stessa.
La descrizione di un pulsante richiede quindi i seguenti parametri particolari
title Il testo descrittivo contenuto nel pulsante
textColor Il colore nello spazio RGB con cui viene reso il testo contenuto in title
backgroundColor Il colore di sfondo del pulsante
clickColor Il colore di sfondo del pulsante durante la pressione da parte dell’utente
borderRadius Il raggio in base al quale sono arrotondati gli angoli del pulsante. Se ha valore 0, gli angoli sono angoli retti non arrotondati.
navigations Collezione di azioni di navigazione associate al pulsante Tabella 3.29 Campi di Button
ListView e ListViewCell
Un controllo di classe ListView rappresenta una lista di elementi, ciascuno di classe ListViewCell. La classe
ListView estende la classe comune a tutti i controlli utente: UiPhoneControl.
È interessante definire il controllo ListView come controllo di navigazione in quando è possibile assegnare
alla lista stessa un’azione di navigazione che viene eseguita quando un elemento della lista viene
selezionato. Questa soluzione è congegnale all’implementazione di pattern di navigazione come il noto
Master – Detail che viene utilizzato in qualsiasi applicazione di messaggistica: il lato master è costituito da
una lista di elementi, ciascuno dei quali rappresenta nell’esempio una conversazione, e il lato detail è
costituito dalla vista che mostra effettivamente i messaggi inviati e ricevuti. Le modalità di navigazione di
35
un pattern Master – Detail sono generalmente note all’utente, e quindi è interessante poterle modellare in
questo lavoro. Come per i pulsanti (Button) è possibile definire una azione di navigazione per ogni scena in
cui viene inserito il viewController a cui la lista appartiene. Tali azioni nel loro complesso possono essere
interpretate come una funzione di navigazione parametrica rispetto alla scena da cui origina la navigazione
stessa.
È possibile assegnare un colore di sfondo alla lista, che resta visibile dietro al contenuto di ciascuna cella.
Un oggetto della classe ListViewCell non rappresenta un controllo utente. Tale classe è infatti strumentale
alla definizione del controllo ListView. Un oggetto di classe ListViewCell rappresenta un elemento della lista.
Ci sono tre possibili tipologie di liste, che si distinguono tra loro per la ricchezza di informazioni contenute
nelle celle che le compongono. Il layout di una cella è quindi determinato dalla tipologia di lista a cui essa
appartiene. Le possibili tipologie di lista sono
Simple: le celle contengono del semplice testo
Image: le celle contengono un’immagine e un testo descrittivo
Detailed: le celle contengono un’immagine, un titolo e un sottotitolo
Nel modello attuale non è possibile personalizzare l’immagine associata ad una cella. Il modello è
comunque facilmente estendibile in questo senso.
L’implementazione del controllo ListView e della classe accessoria ListViewCell richiedono i seguenti
attributi
backgroundColor Il colore di sfondo della lista
listType Il tipo di lista, che definisce il layout delle celle ad essa appartenenti
navigations Collezione delle azioni di navigazione associate al controllo ListView Tabella 3.30 Campi di ListView
title Titolo della cella
subtitle Sottotitolo della cella Tabella 3.31 Campi di ListViewCell
GridView e GridViewCell
Un controllo di classe GridView rappresenta una griglia di elementi, ciascuno di classe GridViewCell. La
classe GridView estende la classe comune a tutti i controlli utente: UiPhoneControl.
È interessante definire il controllo GridView come controllo di navigazione in quando è possibile assegnare
alla griglia stessa un’azione di navigazione che viene eseguita quando un elemento della lista viene
selezionato. Questa soluzione è congegnale all’implementazione di pattern di navigazione come il noto
Master – Detail che viene utilizzato in qualsiasi applicazione che contiene una galleria fotografica: il lato
master è costituito da una griglia di elementi, ciascuno dei quali contiene un’anteprima dell’immagine, e il
lato detail è costituito dalla vista che mostra effettivamente l’immagine a tutto schermo. Le modalità di
navigazione di un pattern Master – Detail sono generalmente note all’utente, e quindi è interessante
poterle modellare in questo lavoro. Come per i pulsanti (Button) è possibile definire una azione di
navigazione per ogni scena in cui viene inserito il viewController a cui la lista appartiene. Tali azioni nel loro
complesso possono essere interpretate come una funzione di navigazione parametrica rispetto alla scena
da cui origina la navigazione stessa.
36
Un oggetto della classe GridViewCell non rappresenta un controllo utente. Tale classe è infatti strumentale
alla definizione del controllo GridView. Un oggetto di classe GridViewCell rappresenta una cella della griglia
a cui appartiene. Ci sono tre possibili tipologie di griglie, che si distinguono tra loro per la ricchezza di
informazioni contenute nelle celle che le compongono. Il layout di una cella è quindi determinato dalla
tipologia di griglia a cui essa appartiene. Le possibili tipologie di griglia sono
Simple: le celle contengono del semplice testo
Image: le celle contengono un’immagine
Detailed: le celle contengono un’immagine e una didascalia
Nel modello attuale non è possibile personalizzare l’immagine associata ad una cella. Il modello è
comunque facilmente estendibile in questo senso.
L’implementazione del controllo GridView e della classe accessoria GridViewCell richiedono i seguenti
attributi
gridType Il tipo di lista, che definisce il layout delle celle ad essa appartenenti
navigations Collezione delle azioni di navigazione associate al controllo GridView Tabella 3.32 Campi di ListView
title Il testo contenuto nella cella, che viene mostrato come didascalia se la griglia è di tipo detailed
Tabella 3.33 Campi di ListViewCell
3.1.7 Modellazione di altri elementi
La modellazione della componente View può essere arricchita di ulteriori elementi che non appartengono
alla classe dei controlli utente, ma che possono comunque risultare molto utili alla buona riuscita di
un’applicazione. Tali elementi sono:
alertDialogs: finestre di dialogo popup che informano l’utente in merito a qualche evento nel
dominio dell’applicazione. Dato che essi appaiono sempre in primo piano ed in posizione centrale
sullo schermo, sono utilizzati per fornire informazioni urgenti e/o eccezionali
progressDialog: finestre di dialogo popup utilizzate per informare di una qualche attesa. Il tipo e la
durata dell’attesa dipendono dallo specifico caso di utilizzo
asyncTasks: attività che vengono eseguite in background. Lo scopo specifico dell’attività dipende
dallo specifico caso d’uso, ma in generale un’attività di sfondo viene utilizzata per svolgere compiti
di lunga durata: la loro esecuzione nel thread principale infatti renderebbe l’interfaccia grafica poco
reattiva nei confronti dell’utente
Il seguente diagramma mostra come questi elementi sono inclusi nel modello
37
Figura 3.7 Modellazione di elementi aggiuntivi della vista
L’inserimento di tali elementi richiede che alla classe ViewController vengano aggiunte le seguenti relazioni
asyncTasks Collezione delle attività in background disponibili all’interno del viewController
alertDialogs Collezione delle finestre di dialogo che possono essere aperte nel contesto del viewController
prograssDialogs Collezione delle finestre di attesa che possono essere utilizzate nel contesto del viewController
Tabella 3.34 Estensione di ViewController agli elementi accessori
AsyncTask
Un oggetto della classe AsyncTask descrive un’attività in background all’interno dell’applicazione. Data la
variabilità degli scopi che possono essere assegnati ad un’attività, questo modello non permette la
modellazione del compito eseguito, ma soltanto dell’esistenza di tale compito. In tal modo, è possibile per il
modellatore descrivere quante attività dovranno essere implementate e denominarle individualmente, e,
quindi, è possibile generare automaticamente tutta la struttura necessaria alla loro creazione e messa in
esecuzione. Il team di implementazione dovrà solamente curare l’algoritmo attualmente eseguito, senza
doversi occupare delle condizioni di contorno. Il modello di un’attività in background è quindi molto
semplice e contiene una sola proprietà: il campo name che contiene il nome distintivo dell’attività in
background.
ProgressDialog
Un oggetto della classe ProgressDialog descrive un messaggio di attesa che viene mostrato all’utente in
particolari condizioni. La generalità di tali condizioni richiederebbe una modellazione eccessivamente
complessa per gli scopi di questo lavoro, che si limita, quindi, a permettere la modellazione dell’aspetto del
messaggio. Un messaggio di attesa è caratterizzato da un titolo e da un breve testo che hanno lo scopo di
informare l’utente in merito al motivo dell’attesa stessa, e dal classico elemento animato presente, con
varie grafiche, su ogni piattaforma con interfaccia grafica.
Il modello quindi permette la definizione delle seguenti proprietà
38
id Identificativo univoco della finestra di attesa (stringa alfanumerica di otto caratteri)
title Il titolo della finestra
message Il messaggio informativo contenuto nella finestra
spinner Se è falso, l’elemento animato di attesa viene omesso Tabella 3.35 Campi di ProgressDialog
AlertDialog
Un oggetto della classe AlertDialog modella un messaggio che l’applicazione può inviare all’utente al
verificarsi di un particolare evento. Questo modello si concentra sull’aspetto grafico di tale messaggio e non
sulla descrizione dell’evento che lo genera, dato che la complessità della descrizione introdurrebbe più
problemi che benefici. Un alertDialog è quindi completamente descritto da un titolo e dal messaggio che
esso porta all’utente. La sua implementazione consiste quindi dei seguenti campi
id Identificativo univoco della finestra di dialogo (stringa alfanumerica di otto caratteri)
title Il titolo del messaggio
message Il messaggio da inviare all’utente Tabella 3.36 Campi di AlertDialog
3.2 Modello del Model La componente Model di un’applicazione si occupa della gestione dei dati di interesse per l’applicazione
stessa, i quali sono contenuti in una struttura rappresentativa del dominio applicativo. La modellazione
della componente Model permette la definizione di tale struttura e di una logica per la persistenza della
stessa. Il collegamento diretto tra la struttura del modello e la logica di persistenza rende possibile il
salvataggio automatico delle modifiche ai dati, feature questa tradizionalmente presente nella maggior
parte della applicazioni mobili.
Questo modello consente di lavorare con quattro strategie di persistenza
SharedPreferences permette l’archiviazione di dati come coppie chiave – valore.
File Storage permette l’archiviazione di dati come contenuto di files memorizzati nel file system del
dispositivo
Relational database permette l’archiviazione di dati tramite un database relazionale
Cloud database permette l’archiviazione di dati tramite il database non relazionale fornito da
Firebase, chiamato Firebase Realtime Database
È possibile suddividere il modello su varie strategie di persistenza, selezionando per ogni elemento del
Model la strategia di persistenza più adatta: oggetti di tipo testuale di discreta lunghezza possono essere
archiviati facilmente su files; il nome dell’utente dell’applicazione trova la sua collocazione ideale in una
chiave nel dizionario delle SharedPreferences; dati strutturati e fortemente interconnessi possono essere
archiviati facilmente nel database relazionale; dati che devono essere sincronizzati tra più dispositivi
possono essere archiviati nel database in cloud.
La seguente figura 3.8 mostra la struttura del modello del Model.
39
Figura 3.8 Modello della componente Model
È immediatamente evidente come il modello sia ripartito in quattro sezioni, ciascuna corrispondente ad
una diversa strategia di persistenza. La classe DataHandler è la radice del modello del Model e possiede una
visione d’insieme delle strategie di persistenza su cui il Model è distribuito. Ciascuno degli Handler che
possono essere inseriti nel DataHandler corrisponde ad una strategia di persistenza. Ogni Handler contiene
al suo interno oggetti che definiscono la struttura del modello memorizzata tramite la strategia ad esso
associata.
Nel seguito di questo capitolo andremo ad analizzare ciascuna tipologia di persistenza.
3.2.1 Archiviazione tramite database relazionale
I dati archiviati nel database relazionale sono strutturati secondo il tradizionale modello Entità – Relazione.
Per ogni entità, il modello consente la descrizione di attributi e relazioni verso altre entità. Ciascun attributo
è caratterizzato da un nome e un tipo, a scelta tra i classici tipi disponibili in tutti i DBMS relazionali:
string: rappresenta un dato di tipo testuale di lunghezza arbitraria
int: rappresenta un numero intero con segno
bool: rappresenta un valore logico booleano (vero o falso)
date: rappresenta una data
float: rappresenta un numero decimale a singola precisione
double: rappresenta un numero decimale a doppia precisione
40
Per quanto riguarda le relazioni, esse sono descritte, oltre che dall’entità che le possiede, dall’entità
collegata e dalla cardinalità della relazione. Le cardinalità possibili sono
1:1: ad ogni istanza dell’entità che possiede la relazione corrisponde un’istanza dell’entità collegata
1:N: ad ogni istanza dell’entità che possiede la relazione corrispondono 0 o più istanze dell’entità
collegata
N:1: ad ogni istanza dell’entità collegata corrispondono 0 o più istanze dell’entità che possiede la
relazione
Una relazione di cardinalità N:N non può essere immediatamente inserita, ma è comunque modellabile
attraverso l’ausilio di un’entità di corrispondenza. Date due entità A e B e una relazione r tra A e B con
cardinalità N:N, è possibile decomporre tale relazione in due relazioni s e t attraverso l’introduzione di una
entità C di appoggio: ogni istanza di C corrisponde all’associazione tra un’istanza di A e una di B, ed è quindi
ottenuta dalla concatenazione delle chiavi primarie delle due entità coinvolte. La relazione s tra A e C e la
relazione t tra B e C hanno entrambe cardinalità 1:N e possono quindi essere espresse nel modello sopra
descritto.
La descrizione di una entità (espressa dalla classe Entity) richiede quindi le seguenti proprietà
name Il nome distintivo dell’entità
primaryKey Il nome dell’attributo che svolge il ruolo di chiave primaria nella relazione. Il tipo di tale attributo è string.
entityAttributes Collezione di attributi assegnati all’entità
entityRelations Collezione di relazioni che originano dall’entità Tabella 3.37 Campi di Entity
La descrizione di un attributo (espressa dalla classe EntityAttribute) richiede le seguenti proprietà
name Il nome distintivo dell’attributo
type Il tipo del dato memorizzato nell’attributo Tabella 3.38 Campi di EntityAttribute
La descrizione di una relazione (espressa dalla classe EntityRelation) richiede le seguenti proprietà
name Il nome distintivo della relazione
destination Il nome distintivo dell’entità di destinazione della relazione
type La cardinalità della relazione (espressa rispetto all’entità che possiede la relazione stessa)
Tabella 3.39 Campi di EntityRelation
3.2.2 Archiviazione tramite files
I dati archiviati tramite files in questo modello sono implicitamente di tipo testuale. Questo modello di
archiviazione permette di specificare un numero arbitrario di files, ciascuno caratterizzato da un nome
distintivo e dalla relativa estensione. È inoltre possibile marcare un file come temporaneo: questo autorizza
il sistema operativo ad eliminare il file qualora sia richiesta la liberazione di spazio su disco.
La descrizione della parte di modello archiviata tramite files è molto semplice: la classe StorageHandler
contiene una collezione di descrittori di files, ciascuno istanza della classe StorageRecord. Ogni
storageRecord descrive un file in cui un elemento del modello è archiviato. Le proprietà di uno
storageRecord sono descritte nella seguente tabella
41
name Il nome distintivo del file all’interno del filesystem
extension L’estensione associata al nome del file
tempFile Marcatura che distingue i files temporanei se ha valore di verità true.
Tabella 3.40 Campi di StorageRecord
3.2.3 Archiviazione tramite SharedPreferences
L’archiviazione tramite SharedPreferences si basa sull’utilizzo di un dizionario tipizzato a livello di record.
Ogni dato è associato ad una specifica chiave, come in ogni dizionario, ed ha un proprio tipo.
Potenzialmente, ogni chiave può avere associato un dato di tipo diverso da tutti gli altri. I tipi di dato
possibili sono
string: dati di tipo testuale di lunghezza non predefinita
int: dati di tipo intero con segno (32 bit)
long: dati di tipo intero con segno (64 bit)
boolean: dati di tipo logico booleano (logica a due valori vero e falso)
float: numero decimale a singola precisione
double: numero decimale a precisione doppia
La classe PreferenceRecord consente la definizione delle varie chiavi del dizionario e del relativo tipo di
dato, necessitando quindi delle seguenti proprietà
key La chiave da inserire nel dizionario
value Il valore predefinito associato alla chiave
type Il tipo del valore associato alla chiave Tabella 3.41 Campi di PreferenceRecord
La classe PreferenceHandler raggruppa tutte le chiavi definite per le SharedPreferences, ovvero tutte le
istanze di PreferenceRecord.
3.2.4 Archiviazione tramite database cloud
Il modello utilizzato in questo lavoro di tesi consente di descrivere la forma di un database cloud in grado di
memorizzare i dati di interesse per l’applicazione.
Per capire la logica catturata dal modello, è necessario descrivere la struttura del database cloud
considerato in questo lavoro: Firebase Realtime Database. Al suo interno, i dati sono archiviati in formato
JSON. L’intero database consiste di un solo oggetto JavaScript, codificato in JSON per l’archiviazione. È
possibile memorizzare oggetti complessi e liste tramite nesting: ad una chiave dell’oggetto globale viene
assegnato un oggetto, le cui chiavi possono ricorsivamente contenere oggetti. Questa modalità definisce
quindi un’archiviazione basata su un albero in cui ciascuna foglia è un valore elementare, ciascun nodo
intermedio è un oggetto complesso e ciascun ramo è etichettato con la chiave con cui nel nodo padre è
memorizzato l’oggetto contenuto nel nodo figlio. Questa struttura induce un accesso ai dati basato su
referenze: una referenza rappresenta il percorso del dato che si vuole accedere a partire dalla radice
dell’albero.
Il modello per l’archiviazione cloud si basa sulla struttura dei dati nel database cloud stesso: il componente
CloudHandler raccoglie una collezione di oggetti di tipo CloudObject, ciascuno dei quali descrive la forma di
un oggetto memorizzato nel database cloud. Ogni oggetto di questo tipo può contenere attributi di diverso
tipo. I tipi disponibili sono
42
string: il dato contenuto nell’attributo è di tipo testuale
int: il dato contenuto nell’attributo è di tipo intero (con segno)
float: il dato contenuto nell’attributo è un numero decimale (singola precisione)
double: il dato contenuto nell’attributo è un numero decimale (doppia precisione)
ref: il dato contenuto nell’attributo è un oggetto complesso
ref_list: il dato contenuto nell’attributo è una lista di oggetti complessi omogenei
Un attributo il cui tipo sia ref o ref_list deve necessariamente contenere un riferimento alla struttura
dell’oggetto complesso memorizzato, singolarmente o come lista, al suo interno. I tipi ref e ref_list
consentono così di realizzare il nesting sopra descritto.
All’interno della collezione di oggetti posseduta dal CloudHandler vi è sempre un oggetto chiamato
RootObject che costituisce il punto di ingresso della struttura: tale oggetto infatti è il modello dell’oggetto
JSON globale archiviato nel database cloud. Attraverso la definizione di attributi di tipo ref o ref_list
sull’oggetto RootObject è possibile introdurre un primo livello di nesting. I successivi livelli possono essere
definiti ricorsivamente in modo analogo. Ogni oggetto appartenente al CloudHandler diverso dal
RootObject, per essere accessibile, deve essere collegato ad un altro oggetto accessibile o al RootObject per
mezzo di un attributo di tipo ref o ref_list.
Per descrivere l’intera struttura sono quindi necessarie tre classi: CloudHandler, che mantiene la collezione
di tutti gli oggetti cloud definiti per l’applicazione, CloudObject, che rappresenta un oggetto caratterizzato
da una collezione di attributi, e ObjectAttribute che rappresenta uno di tali attributi. Le seguenti tabelle
dettagliano il ruolo delle proprietà di CloudObject e ObjectAttribute
name Il nome distintivo dell’oggetto cloud.
attributes La collezione degli attributi dell’oggetto Tabella 3.42 Campi di CloudObject
name Il nome distintivo dell’attributo. È anche la chiave con cui tale attributo è memorizzato nell’oggetto padre
type Il tipo dell’attributo
object Se l’attributo ha tipo ref o ref_list, questa proprietà contiene il nome distintivo dell’oggetto archiviato in questo attributo, altrimenti ha valore nullo
Tabella 3.43 Campi di ObjectAttribute
3.3 Modello del Controller Il ruolo del Controller all’interno di una applicazione MVC è quello di tradurre gli eventi della View in azioni
di modifica del Model. Non essendo possibile catturare in modello la complessità di tale definizione, questo
lavoro di tesi si concentra su una tra tutte le possibili implicazioni: il ruolo che assume il controller
nell’interazione (indiretta) tra la View e il modello persistente. Dato che non è possibile, per
un’applicazione MVC, lavorare direttamente con il modello persistente (che non è osservabile), è
necessario mantenere una copia in memoria degli elementi su cui l’applicazione va ad operare. Questo
introduce la necessità di una politica di mantenimento della coerenza tra i due “modelli” e di strumenti per
importare dati dal modello persistente al modello di lavoro. Il Controller deve quindi operare su due fronti
Quando una scena viene istanziata per essere mostrata all’utente, il Controller carica i dati richiesti
dalla scena stessa dal modello persistente al modello di lavoro, se necessario. È infatti possibile che
43
i dati richiesti siano già presenti nel modello di lavoro per effetto di operazioni di caricamento
precedenti
Quando all’interno di una scena si verifica un evento di modifica di un dato, il Controller aggiorna
immediatamente il modello di lavoro, in modo che la View possa mostrare all’utente i dati
aggiornati, e aggiorna anche il modello persistente in modo da mantenere la coerenza tra modello
di lavoro e modello persistente.
Tuttavia, questi due ruoli si differenziano solamente per il tipo di operazione che svolgono sul modello
persistente: il primo di lettura e il secondo di scrittura. In entrambi i casi il modello di lavoro è oggetto di
un’operazione di scrittura e l’azione scatenante l’operazione è un evento della vista: nel primo caso un
evento di transizione ad una scena, nel secondo caso un interazione dell’utente con la vista.
La condizione necessaria per la realizzazione di questo tipo di Controller è la possibilità di descrivere di quali
dati ciascuna vista necessiti, e più nel dettagli di quali dati ogni controllo necessiti. Tale requisito è infatti
strumentale alla realizzazione del secondo ruolo del controller: gli eventi di modifica del Model infatti
provengono principalmente da controlli ad interazione bidirezionale con l’utente (paragrafo §3.1.5).
È possibile suddividere quest’ultimo problema in due problemi più circoscritti. Il primo problema riguarda il
rapporto tra il Model e una scena. Ciascuna scena infatti non necessita generalmente dell’accesso all’intero
modello, ma solo ad una sua parte. Gli elementi del modello che vengono effettivamente utilizzati dalla
scena formano il modello della scena, o scene model. Il scene model è, quindi, un sottoinsieme delle
informazioni contenute nel Model. Il secondo problema riguarda il rapporto tra una scena e i controlli
contenuti nei viewController che la compongono. La descrizione di quali dati sono utilizzati da ogni
controllo rende possibile la generazione automatica del codice necessario a fornire tali dati e a gestire gli
eventi prodotti dai controlli stessi. Grazie all’introduzione del scene model, tale descrizione può essere
semplificata: è possibile definire, per ogni controllo, una o più proprietà capaci di assumere valori derivati
dal scene model, e definire per tali proprietà una politica di derivazione del valore stesso. Tali proprietà
sono definite proprietà collegabili. Come per la navigazione (§3.1.6), questa descrizione può essere infine
resa parametrica rispetto alla scena in cui il controllo viene mostrato. In questo lavoro di tesi verrà descritta
solamente la derivazione diretta parziale, ma è possibile estendere questo approccio ad altre politiche. La
politica di derivazione diretta prevede che ogni proprietà collegabile possa assumere un valore elementare
direttamente disponibile nel scene model. Tale valore può quindi essere solamente:
Un record del dizionario delle SharedPreferences la cui chiave sia nota a design time
L’intero contenuto di un file
Un attributo di un’entità del database relazionale (non una relazione)
Un attributo di tipo elementare (ovvero non ref o ref_list) di un’oggetto cloud
Sebbene sia piuttosto restrittiva, la derivazione diretta copre un buon numero di casi d’uso: le operazioni di
inserimento o di modifica di dati complessi sono infatti sempre realizzate come composizione di operazioni
di modifica di dati elementari, a vantaggio di un riduzione della complessità dell’operazione per l’utente
dell’applicazione. La maggiore limitazione della derivazione diretta risiede nell’impossibilità di produrre un
valore aggregato per una proprietà collegabile. Al fine di ridurre la portata di tale limitazione, è possibile
rilassare la derivazione diretta introducendo la possibilità di collegare ad una proprietà anche un oggetto
complesso o una lista di oggetti. Tale collegamento può essere sfruttato per generare automaticamente il
codice per il caricamento nel scene model di tutti gli elementi necessari alla computazione del dato
aggregato, lasciando poi l’implementazione effettiva della logica di calcolo ad un programmatore che
44
interviene sul codice generato a partire dal modello. Questo rilassamento della derivazione diretta è
chiamato derivazione diretta parziale.
La seguente figura 3.9 mostra gli strumenti necessari a descrivere il scene model e la derivazione diretta
parziale.
Figura 3.9 Modellazione del Controller
La figura 3.9 introduce due nuove entità all’interno del modello: AdapterBinding e ModelConnector.
AdapterBinding viene utilizzata per la descrizione del scene model, mentre ModelConnector viene utilizzata
per la descrizione della derivazione diretta parziale.
Al fine di collegare il controller alle altre componenti, è necessario estendere le classi Scene e
UiPhoneControl con le seguenti relazioni:
Alla classe Scene viene aggiunta la relazione adapterBindings che contiene una collezione di
elementi di classe AdapterBinding. Tale collezione, nel suo complesso, descrive il scene model.
Alla classe UiPhoneControl viene aggiunta la relazione modelConnectors, che contiene una
collezione di elementi di classe ModelConnector. Tale collezione, nel suo complesso, descrive la
derivazione diretta parziale dei dati necessari al componente in modo parametrico rispetto alla
scena in cui tale componente è inserito.
Nei capitoli seguenti andremo ad analizzare come le due entità introdotte consentano di realizzare la
descrizione data del Controller.
3.3.1 AdapterBinding
La classe AdapterBinding ha il compito di descrivere la dipendenza di una scena da un elemento del Model.
Dato che il Model è strutturato, in prima analisi, a partire dai diversi meccanismi di persistenza, la classe
AdapterBinding deve implementare la capacità di poter connettere ad una scena dati da qualsiasi archivio
45
persistente in modo trasparente alla scena. Questo richiede che la tipologia del meccanismo di persistenza
sia incapsulata all’interno della classe. Gli attributi e le relazioni di AdapterBinding sono quindi pensati per
realizzare questo incapsulamento.
L’attributo id contiene sempre una stringa univoca alfanumerica di 8 caratteri che costituisce un
identificativo per ciascuna istanza.
L’attributo isList indica se una istanza introduce nel scene model un elemento scalare o una collezione di
elementi. Questo attributo è utile soprattutto quando si lavora con le relazioni in un database relazionale:
introducendo un adapterBinding con il parametro isList impostato a true è possibile descrivere la presenza
di una lista di entità omogenee in tipo all’interno del scene model. Una lista così costruita può servire ad
includere nel scene model le entità collegate ad una entità data attraverso una relazione di tipo 1:N
(§3.2.1).
L’attributo name contiene un nome distintivo dell’adapterBinding. Il nome serve al designer per poi
riconoscere un adapterBinding quando lo utilizza per generare uno o più modelConnector (si veda il
capitolo successivo).
L’attributo type indica il meccanismo di persistenza da cui l’adapterBinding ottiene i dati da importare nel
scene model. Questo attributo può assumere i seguenti valori
Valore Meccanismo di persistenza
file Archiviazione tramite file
preference Dizionario delle SharedPreferences
entity Database relazionale
cloud Database cloud Tabella 3.44 Tipologie di AdapterBinding
Le relazioni file, preference, object e entity assumono tutte valore nullo tranne la relazione corrispondente
al valore dell’attributo type, che contiene la descrizione del valore inserito nel scene model
dall’adapterBinding. In particolare:
Se type contiene il valore file, la relazione file contiene il descrittore del file (§3.2.2) il cui contenuto
viene importato nel scene model con il nome definito dall’attributo name.
Se type contiene il valore preference, la relazione preference contiene il descrittore del record nel
dizionario delle SharedPreferences (§3.2.3 il cui valore viene importato nel scene model con il nome
definito dall’attributo name.
Se type contiene il valore entity, la relazione entity contiene il descrittore dell’entità relazionale
(§3.2.1) di cui una o più istanze (in base al valore dell’attributo isList) vengono importate nel scene
model con il nome definito dall’attributo name.
Se type contiene il valore cloud, la relazione object contiene il descrittore dell’oggetto cloud
(§3.2.4) che viene importato nel scene model con il nome definito dall’attributo name. Se
l’attributo isList ha valore vero nel scene model viene importata una lista di oggetti omogenei e di
tipo definito dal valore della relazione object. Se invece l’attributo isList ha valore falso nel modello
viene importato un singolo oggetto di tipo definito dal valore della relazione object.
Tra le varie tipologie di adapterBinding, la più complessa è quella relativa al database relazionale, dato che
l’estrazione di dati da un database relazionale richiede una query SQL, che in alcuni casi è semplice e
generabile automaticamente (si pensi ad esempio all’estrazione di un’entità dato il valore della sua chiave
46
primaria), mentre in altri richiede un intervento da parte di un programmatore. Dato che l’introduzione di
un meccanismo di modellazione di una generica query SQL renderebbe il modello molto più vicino ad
un’implementazione che ad un prototipo, si è preferito lasciare tali casi all’intervento di un programmatore,
fornendo tuttavia gli strumenti per ridurre al minimo l’intervento dello stesso.
In caso di adapterBinding di tipo preference o file la descrizione dell’elemento da importare nel scene
model è esaustiva rispetto alle informazioni richieste per il caricamento dalla persistenza del dato stesso.
Infatti, nel momento in cui si seleziona un file da importare nel scene model sono noti nome ed estensione,
e non sono necessari altri elementi per accedere al file e leggerne il contenuto. Il medesimo concetto vale
per le SharedPreferences, la specifica di un record da aggiungere al scene model si basa sulla specifica della
sua chiave, che è anche l’unica informazione necessaria ad accedere al record.
In caso di adapterBinding di tipo cloud, è possibile completare la descrizione dell’adapterBinding fornendo
l’attributo cloudRefPath che contiene la referenza dei dati da caricare. In questo modo l’adapterBinding
contiene sia la descrizione del tipo dei dati (tramite la relazione object) sia tutte le informazioni necessarie
ad importare i dati dalla cloud (la referenza all’oggetto, come descritto in §3.2.4).
Infine in caso di adapterBinding di tipo entity, per le considerazioni fatte precedentemente, non è possibile
includere tutte le informazioni necessarie al caricamento dei dati nel scene model. La descrizione del tipo
dell’entità da caricare e del fatto che si tratti di una lista o di uno scalare permette sia di generare tutto il
codice necessario al caricamento dalla persistenza di tali oggetti, lasciando come parametro la query
necessaria alla selezione degli stessi, sia tutto il codice necessario ad aggiornamento ed eliminazione di
ciascuna entità, dato che queste operazioni avvengono sempre in modo scalare. La quantità di codice
generato rispetto al codice totale necessario è quindi inferiore per le sole parti relative alla formulazione
della query di selezione.
3.3.2 ModelConnector
La classe ModelConnector descrive la connessione tra una proprietà collegabile di un controllo utente e un
dato contenuto nel scene model. Tale descrizione coinvolge quattro elementi
Il controllo utente coinvolto nella connessione
La proprietà del controllo utente coinvolta nella connessione
L’adapterBinding da cui il dato viene estratto
Per adapterBinding di tipo non elementare, la proprietà dell’oggetto caricato dall’adapterBinding
che deve fornire i dati al controllo
Di tali elementi, il controllo utente coinvolto nella connessione è costituito dall’istanza di UiPhoneControl
che possiede l’istanza di ModelConnector, mentre gli altri elementi sono inseriti nel modelConnector stesso
come attributi o relazioni, come esplicitato nella seguente tabella
id Identificativo univoco dell’istanza di ModelConnector (stringa alfanumerica di 8 caratteri)
adapter Riferimento all’istanza di adapterBinding che fornisce i dati al modelConnector
property Il nome della proprietà collegabile del controllo che possiede il modelConnector
keypath La proprietà dell’oggetto contenuto nell’adapterBinding da cui leggere i valori per la proprietà collegabile
Tabella 3.45 Campi di ModelConnector
47
È possibile distinguere due casi di modelConnector: i connettori semplici e i connettori complessi. Sono
considerati connettori semplici quei connettori che traggono dati da un adapterBinding di tipo preference o
file. Sono invece considerati connettori complessi quei connettori che leggono dati da un adapterBinding di
tipo entity o cloud.
Nei connettori semplici la proprietà keypath ha sempre come valore una stringa vuota, a significare che il
dato contenuto nell’adapterBinding referenziato dalla proprietà adapter è un dato elementare e non
ulteriormente scomponibile.
Nei connettori complessi, keypath contiene il nome della proprietà da associare alla proprietà collegabile.
Nel caso specifico di un’entità relazionale, tale proprietà può essere data da qualsiasi attributo, ma non da
una relazione. Nel caso specifico di oggetti cloud, tale proprietà può essere data da qualsiasi attributo il cui
tipo non sia ref o ref_list. Questo è imposto perché l’accesso ad oggetti complessi, oltre ad essere
problematico nel rapporto con la persistenza in quanto richiede un’operazione asincrona per ogni
puntatore risolto, può dare origine a grafi di dipendenza ciclici la cui risoluzione a livello di codice
applicativo diventa inutilmente complessa. Tale imposizione non risulta comunque limitante: nel momento
in cui si abbia la necessità di accedere ad un oggetto complesso, è possibile creare per esso un nuovo
adapterBinding a livello di scene model e utilizzarlo per definire opportuni modelConnector.
Definiamo il valore di un modelConnector come il valore dell’attributo di nome keypath all’interno
dell’oggetto inserito nel scene model dall’adapter qualora keypath sia non vuoto. Nel caso in cui keypath sia
vuoto, il valore del modelConnector coincide con l’oggetto inserito nel scene model dall’adapter.
L’implementazione della derivazione diretta parziale è realizzata grazie all’assenza di controlli di
compatibilità di tipo tra la proprietà collegabile e il valore del modelConnector. È infatti possibile collegare
ad una qualsiasi proprietà un oggetto complesso o una lista di oggetti complessi senza invalidare il modello,
con la condizione che un programmatore completi il codice generato a partire dal modello. In mancanza di
intervento da parte di un programmatore, il valore del modelController verrà utilizzato come valore della
proprietà collegabile tramite operazioni di typecasting.
La maggior parte dei controlli dispone di proprietà che possono essere associate ad elementi del model. Tali
proprietà sono riportate in questa sezione. Quasi ogni proprietà collegabile ha una controparte
indipendente dal model, che viene utilizzata dall’applicazione generata nel caso in cui il valore dinamico
letto dal model non sia disponibile.
La seguente tabella riporta l’elenco di tutte le proprietà collegabili di ciascun controllo utente disponibile a
livello di View.
48
Tipologia di controlli Proprietà Controparte
AudioPlayer source fileUri
Button title title
Card title title
Card subtitle subtitle
Datepicker value - (*1)
GridView items - (*2)
GridViewCell (*3) title title
ImageView source fileUri
Label title title
ListView items - (*2)
ListViewCell (*3) title title
ListViewCell (*3) subtitle subtitle
Map latitude latitude
Map longitude longitude
Slider value - (*1)
Spinner value - (*1)
Switch value - (*1)
TextEdit value title
Timepicker value - (*1)
VideoView source fileUri
WebView htmlFileName htmlFileName Tabella 3.46 Proprietà collegabili
(*1)
La proprietà non ha una controparte per motivi di retrocompatibilità
(*2)
Le celle di una lista o di una griglia sono generate dinamicamente dalla lista passata tramite la proprietà
items, oppure possono essere definite staticamente. Il trattamento dei due casi è però radicalmente
diverso e la controparte non può essere trovata in una singola proprietà, ma piuttosto in un insieme di
elementi.
(*3)
Questo elemento non è un controllo utente, e in quanto tale non dispone di una relazione con oggetti di
tipo modelConnector. Le proprietà evidenziate sono pertanto definite sul controllo che contiene questo
elemento con il nome prefisso dalla stringa “@cell.”. Un generatore di codice associato a questo modello
deve considerare questo caso e implementare opportunamente il collegamento con tali proprietà. La
definizione di un modelConnector per questo tipo di oggetti sarebbe un errore concettuale: la proprietà
collegabile assume un significato nel momento in cui questo elemento è generato dinamicamente a partire
dalla proprietà items del controllo che lo contiene, e un modelConnector dovrebbe essere definito su una
istanza disegnata staticamente dal modellatore. Tale situazione è paradossale.
49
4 Protocode
4.1 Introduzione Protocode 4.0 è un’applicazione web per lo sviluppo model-driven di applicazioni mobili. Essa consente di
progettare un modello per le componenti View e Model di un’applicazione mobile implementata secondo il
pattern architetturale MVC.
Il progetto, nato da un lavoro di tesi di Mattia Natali nel 2013 [4], è stato successivamente revisionato ed
ampliato grazie al lavoro di Aldo Pintus [5] e Alessio Rossotti [6]. Inizialmente l’applicazione era stata
pensata per modellare la parte View, l’estensione alla componente Model è stata introdotta solo a partire
dalla versione 3.0 sviluppata da Rossotti.
Con questo lavoro di tesi è stata realizzata la versione 5.0 di Protocode, che introduce la possibilità di
modellare, a fianco delle componenti Model e View, anche alcune parti della componente Controller. In
particolare, questo lavoro si concentra sull’utilizzo dei dati del Model da parte della View. Per ogni scena
l’utente di Protocode può selezionare i dati del model necessari al funzionamento della scena stessa. Tali
dati possono poi essere collegati (anche in modo bidirezionale) ai controlli che compongono la stessa scena.
Inoltre, a causa della deprecazione di molte librerie su cui la versione 4.0 si basava, è stato necessario
riprogettare completamente il codice per adattarlo a framework più moderni, in favore di una maggiore
manutenibilità ed estensibilità futura del progetto. Protocode 4.0 era basato sul framework EmberJS 1.6,
mentre Protocode 5.0 è basato su React 16.6.0.
Figura 4.1 Schermata principale di Protocode
50
Protocode è basato sul paradigma WYSIWYG (What You See Is What You Get), in modo da fornire all’utente
un feedback immediato e fedele su come apparirà il prodotto finito. Questo concetto è comune ad altri
prodotti di progettazione visuale, quali ad esempio Interface Builder di XCode o Layout Editor di Android
Studio, entrambi dedicati alla progettazione di interfacce utente per via grafica. La somiglianza consente a
sviluppatori provenienti da altri prodotti di trovare familiarità nell’interfaccia di Protocode, riducendo
quindi la learning curve di Protocode stesso.
Vi sono due sostanziali differenze tra Protocode e i suoi competitor: la tipologia di prodotto che viene
generato a seguito della fase di prototipizzazione e la possibilità di creare prototipi anche per quanto
riguarda le componenti Model e Controller.
Per quanto riguarda il prodotto che viene generato, Protocode consente di esportare i prototipi in un
formato strutturato, basato su XML, e non grafico. Questo permette poi di generare automaticamente il
codice per diverse piattaforme software (si veda la descrizione di MobileCodeGenerator nel capitolo
successivo).
Per quanto riguarda invece la prototipizzazione delle componenti Model e Controller, Protocode consente
in modo intuitivo e guidato di produrre sia un modello dei dati di interesse per l’applicazione, sia un
modello per la persistenza di tali dati, sia un meccanismo di connessione di tali dati con le interfacce
grafiche.
Al fine di implementare tali requisiti e per contenere la complessità di utilizzo, l’applicativo mette a
disposizione diversi editor, ciascuno con una precisa prerogativa
View Editor
Consente di progettare le viste dell’applicazione. E’ basato sul drag and drop di componenti quali,
ad esempio, immagini, pulsanti, caselle di testo e testi statici. Per ogni controllo creato è possibile
modificare attributi relativi alla posizione e alla spaziatura rispetto agli altri componenti. Inoltre, in
linea con le indicazioni dei principali produttori di sistemi operativi mobili, è possibile definire
vincoli di posizione e di dimensione tra i vari elementi, in modo da creare interfacce adattabili a
schermi di dimensioni diverse. Per ogni classe di controlli sono inoltre disponibili proprietà
specifiche (il placeholder di una casella di testo, o lo stile di un testo statico, ad esempio). Alcune di
queste proprietà specifiche possono essere collegate ai dati disponibili nel contesto della scena in
cui sono inseriti (si veda nel seguito il Scene Model Editor).
Model Editor
Consente di progettare il modello dei dati e il modello di persistenza dell’applicazione. E’ possibile
scegliere tra quattro tipologie di persistenza: shared preferences, file storage, relational database e
cloud database. Per ciascuna tipologia, è possibile definire la struttura dei dati che vengono salvati
utilizzando quel modello di persistenza. E’ quindi possibile definire entità e relazioni per il relational
database, le chiavi del dizionario delle shared preferences, i files accessibili nel file storage e la
struttura degli oggetti archiviati nel cloud database (la cui implementazione si basa su Firebase
Realtime Database)
Scene Model Editor
Consente di definire, per ogni scena, quali dati estrarre dai diversi modelli di persistenza perché
siano resi disponibili ai controlli all’interno della scena. Per ogni dato estratto è necessario
scegliere un nome identificativo. Questo consente all’utente, attraverso il view editor, di collegare
tali dati a specifiche proprietà dei controlli, astraendo dalla complessità della persistenza.
51
Manifest Editor
Consente di definire proprietà dell’applicazione nel suo complesso quali, ad esempio, il nome
dell’applicazione o dell’ente sviluppatore.
Di tali editor, il view editor e il model editor erano già disponibili nelle versioni precedenti. Il model editor
non ha subito modifiche sostanziali da un punto di vista concettuale durante questo lavoro di tesi, mentre
al view editor è stata aggiunta la possibilità di connettere proprietà dei controlli a proprietà di oggetti
estratti dal Model. Il Scene Model Editor è interamente frutto di questo lavoro di tesi. Il Manifest Editor era
una parte del Model Editor nella versione precedente, ma data la diversa natura concettuale dei dati da
esso gestiti è stato estratto come componente di primo livello.
Figura 4.2 Schermata dedicata alle connessioni tra modello e vista
Figura 4.3 Esempio di output di Protocode
52
4.2 Software e framework utilizzati Protocode 5.0 è una applicazione web basata su ReactJS ed è accessibile tramite tutti i browser che
supportano HTML5 e ECMAscript 6. Oltre a Javascript (nella sua estensione JSX) ed HTML5, durante lo
sviluppo sono stati utilizzati Yarn come gestore di pacchetti, Sass per la generazione di fogli di stile, e altre
librerie dell’ecosistema di ReactJS che verranno descritte nel seguito.
4.2.1 ReactJS
ReactJS [26] è il framework di base su cui si innesta tutta l’architettura di Protocode. E’ un framework
recente, rilasciato da Facebook nel 2015, ma in forte crescita, con una community di sviluppatori molto
attiva e un ottimo supporto. Tra i framework web più popolari si affianca ad AngularJS, EmberJS e
Backbone.
ReactJS è fortemente basato sulla composizione di componenti, piuttosto che sull’ereditarietà, e questo
consente un forte riuso del codice. A differenza di tutti gli altri framework, che sono basati su data binding
a due vie, ReactJS si basa sul concetto di esprimere la vista come funzione del modello. Grazie all’adozione
del VirtualDom, un albero di componenti parallelo e legato al classico albero DOM di cui costituisce una
estensione, i componenti sono organizzati gerarchicamente e i dati vengono passati solo da un componente
ai suoi figli, creando così un flusso direzionale dalla radice alle foglie. Non vi è flusso di dati nella direzione
opposta, ma solo di eventi. I componenti di ReactJS possono essere sia stateful che stateless. Ogni
componente possiede un metodo render nel quale viene definito il corrispondente sottoalbero (l’albero
avente radice in esso) di componenti (sia componenti definiti utilizzando ReactJS sia componenti nativi del
DOM. Il metodo render ha accesso alle proprietà ricevute dal componente padre e all’eventuale stato
interno del componente stesso e può quindi personalizzare il sottoalbero conseguentemente. L’espansione
dei componenti ReactJS procede ricorsivamente fino ad ottenere un albero costituito da soli elementi DOM
nativi, che può quindi essere mostrato dal browser.
La scelta di ReactJS come framework nasce dai seguenti fattori:
Attività della community online
Semplicità del framework (i concetti alla base di ReactJS sono molti meno di quelli alla base di altri
framework)
Avanzamento di ReactNative come piattaforma per lo sviluppo di applicazioni mobili
Ricchezza dell’ecosistema di plugin
Approccio innovativo al flusso dei dati
4.2.2 Yarn
Yarn [27] è un gestore di pacchetti basato su NodeJS. Nel mercato, si presenta come una evoluzione di
NPM, e si affianca ad altri gestori di pacchetti quali, ad esempio, NPM stesso, Bower (deprecato dagli stessi
sviluppatori, ma ancora in uso) e PNPM.
La scelta di Yarn come gestore di pacchetti si basa sulla maggiore velocità rispetto ad NPM e alla maggiore
popolarità rispetto a PNPM, che corrisponde quindi ad un più ampio supporto nella community. Per gli
elementi di interesse del progetto, da un punto di vista funzionale, i tre prodotti sono equivalenti.
Yarn è stato utilizzato per installare React e tutte le sue dipendenze, oltre che i vari plugin descritti nel
seguito.
53
4.2.3 Redux e Redux-persist
Redux [28] è una libreria per la gestione dello stato che si presenta come un “predictable state container”.
Infatti, l’idea centrale di Redux è proprio quella di schematizzare l’evoluzione dello stato come la risposta
ad una azione. Quando un’azione viene generata, lo stato successivo è calcolato da una funzione pura,
detta reducer, che prende come argomenti soltanto lo stato precedente e l’azione stessa. Questo rende
molto semplice tracciare l’evoluzione dello stato, replicare errori ed evitare problemi comuni di gestione
dello stato, che sono tradizionalmente molto presenti in un linguaggio come JavaScript.
Redux non nasce come libreria associata a React, ma si integra perfettamente con il concetto alla base di
React che la vista debba essere una funzione dello stato. Infatti, Redux mantiene lo stato dell’applicazione
fino a quando un componente non genera una azione. A quel punto lo stato successivo viene calcolato dal
reducer e il VirtualDom, essendo globalmente una funzione dello stato, può essere aggiornato di
conseguenza. La connessione tra Redux e React è implementata da un apposito plugin: react-redux. Questo
plugin si occupa di generare automaticamente componenti ReactJS connessi con lo stato mantenuto da
Redux attraverso due funzioni configurabili dall’utente e dedicate, rispettivamente, ad estrarre il
sottoinsieme dello stato di interesse per l’albero DOM radicato nel componente connesso stesso e a
lanciare azioni sullo stato.
Nell’ecosistema di Redux, di particolare interesse per Protocode è il plugin redux-persist, che si occupa di
mantenere lo stato nel database del browser (Local Storage) in modo completamente automatico. Lo stato
viene persistito dopo ogni azione, e ripristinato ad ogni riavvio dell’applicazione.
Un altro plugin di interesse per Protocode è redux-logger, che consente di registrare ogni azione inviata al
reducer nella console del browser, ottenendo così una traccia riproducibile.
4.2.4 Bootstrap
Bootstrap [29] è uno tra i più popolari framework HTML e CSS disponibili sul mercato. Il principale focus del
progetto è fornire strumenti per descrivere interfacce utente adattabili a qualsiasi tipo di schermo. Inoltre,
Bootstrap mette a disposizione componenti HTML di uso comune, tra cui cards, barre di navigazione, layout
a schede e molto altro.
Sul mercato si affianca ad altri framework quali PureCSS, Foundation, Skeleton e molti altri. La scelta di
Bootstrap deriva dalla sua indiscussa popolarità, che si traduce poi in una community di sviluppatori attiva e
in grado di fornire un ottimo supporto, e dalla sua completezza e semplicità di uso.
In Protocode la parte puramente CSS di Bootstrap viene utilizzata direttamente, mentre per quanto
riguarda i vari componenti che richiedono un supporto JavaScript è stata utilizzato il plugin React-Bootstrap
che fornisce un’implementazione dichiarativa compatibile con React di tali componenti.
4.2.5 Altre librerie
ReactRouter [30] viene utilizzato per implementare, in modo dichiarativo, la navigazione all’interno
dell’applicazione.
FontAwesome [31] consente di utilizzare una vasta libreria di icone in modo semplice e diretto. Il
vantaggio di utilizzare icone largamente diffuse e conosciute sul web è nella loro immediata
riconoscibilità da parte di un utente
FileSaver [32] viene utilizzata per implementare in modo indipendente dal browser il download del
file contenente il modello strutturato dell’applicazione progettata utilizzando Protocode.
54
XmlBeautifier [33] viene utilizzato per migliorare l’aspetto grafico del file XML e renderlo più
leggibile da un utente.
Uniqid [34] viene utilizzata per generare un id univoco per ogni dato di interesse per l’applicazione.
Babel [35] viene utilizzata per convertire il codice JSX in JavaScript compatibile con la maggior parte
dei browser
55
4.3 Architettura dell’applicazione In questo capitolo viene descritta l’architettura dell’applicazione Protocode 5.0 nei diversi aspetti di
Architettura software
Design dei dati
Connessione tra dati e vista
Design della navigazione
Design dell’interfaccia utente
4.3.1 Architettura software
In questa sezione viene presentata l’architettura software dell’applicazione Protocode. L’architettura
dell’applicazione è fortemente basata sull’integrazione tra Redux e React. Come mostrato nel seguente
diagramma, è chiaramente possibile suddividere l’applicazione in tre livelli logici: i dati (indicati nel riquadro
blu), i componenti di accesso ai dati (riquadro rosso) e i componenti di visualizzazione dei dati e interazione
con l’utente (riquadro verde).
Figura 4.4 Architettura di alto livello di Protocode
56
Ciascuno di questi livelli è gestito da una libreria apposita: Redux per i dati, React per l’interazione con
l’utente e la visualizzazione dei dati, e React-Redux per i componenti di connessione.
L’architettura comprende anche i componenti dedicati alla modellazione di applicazioni per smartwatch,
che non verranno descritti in questo documento in quanto non rilevanti ai fini della tesi.
Gestione dei dati: Redux
In Protocode lo stato dell’applicazione è interamente gestito da Redux. Questa sezione presenta le
principali scelte architetturali inerenti la definizione della forma dello stato e delle regole per il suo
aggiornamento, implementate attraverso i reducers. La forma dello stato e le azioni supportate dai reducers
sono definiti nel paragrafo §4.3.2 Design dei dati.
Lo stato dell’applicazione, come supportato da Redux, consiste di un singolo oggetto JavaScript. Al suo
interno, ogni chiave corrisponde ad una precisa classe di oggetti. La relazione di composizione tra oggetti di
classi diverse viene eliminata e sostituita da riferimenti basati su identificativi univoci, inspirandosi al
concetto di chiave esterna tipico dei database relazionali. Questo consente di ottenere un oggetto con un
basso livello di nesting, più maneggevole per il reducer.
Ogni chiave dell’oggetto che rappresenta lo stato non contiene semplicemente una lista di tutti gli oggetti
appartenenti alla classe di riferimento, ma una struttura dati che ne faciliti il riferimento, che chiameremo
DataStore. Un DataStore memorizza gli oggetti in base ad un campo che funge da chiave primaria per il loro
riferimento, ma mantiene anche una serie di indici, configurabili a seconda del caso, per facilitare
operazioni di query. L’unico vincolo implementativo è che tutti i campi indicizzati siano immutabili. Ciò
tuttavia non costituisce una limitazione sensibile nel caso di Protocode, dato che tipicamente i campi
indicizzati di ogni oggetto contengono quei riferimenti introdotti per semplificare le relazioni di
composizione, e quindi sono immutabili per loro stessa natura. Questo rende estremamente efficienti le
operazioni di ricerca di tutti i figli di un certo elemento, ad esempio tutti i vincoli associati ad un controllo o
tutti i controlli di una vista applicativa.
In modo analogo, anche il reducer è definito attraverso la composizione di più funzioni, una per ciascuna
chiave dello stato. Ciascuna di queste funzioni, chiamata con un abuso di linguaggio reducer nel linguaggio
di Redux, riceve quindi solo il corrispondente segmento dello stato complessivo, e produce in output per
ogni azione la versione prossima del segmento stesso. I segmenti sono poi automaticamente riaggregati da
Redux a formare lo stato prossimo. Non tutte le azioni sono di interesse per tutti i segmenti dello stato, e in
questo caso i reducers ignorano le azioni non rilevanti per i dati che gestiscono ritornando, come stato
prossimo, lo stato precedente stesso.
Accesso ai dati: React-redux
I componenti di accesso ai dati si occupano di colmare il divario tra lo stato amministrato da Redux e la vista
implementata con React. La libreria utilizzata per la loro creazione è react-redux, che consente la
generazione automatica delle connessioni tra questi componenti e lo stato.
La progettazione di tali componenti si basa su tre elementi: la definizione di un componente React
“tradizionale” e la definizione di due funzioni chiamate mapStateToProps e mapDispatchToProps. Queste
due funzioni si occupano entrambe di creare proprietà che saranno poi iniettate nel componente come se
gli fossero passate dal componente padre nel VirtualDom. mapStateToProps riceve come parametro lo
stato corrente, e ne seleziona alcuni elementi, mentre mapDispatchToProps riceve come parametro la
funzione dispatch, che consente di inviare azioni allo stato, e ne crea dei wrapper semanticamente
57
significativi per il componente: è possibile creare un wrapper senza parametri che chiami dispatch con
parametri predefiniti, o semplicemente iniettare la funzione nel componente con un nome più significativo
di dispatch. Tipicamente viene creato un wrapper per ogni tipologia di azione da inviare allo stato. Grazie a
queste proprietà i componenti di connessione tra stato e vista possono accedere ai dati e inviare azioni, e
possono passare, sempre per mezzo di proprietà, dati e funzioni di emissione di azioni ai propri figli, agendo
così da punto di connessione per un intero sottoalbero.
La definizione delle funzioni mapStateToProps dei vari componenti di connessione con lo stato in Protocode
è supportata da un insieme di funzioni dette selettori. È definito un selettore per ogni classe di componenti.
Il ruolo dei selettori, oltre a quello di astrarre dalle modalità di estrazione dai DataStore che compongono lo
stato corrente, è quello di denormalizzare gli oggetti stessi sostituendo i riferimenti tramite identificatori
con l’oggetto referenziato, e quindi ricostruendo le relazioni, soprattutto di composizione. Ogni selettore
quindi sfrutta selettori di altre classi al fine di produrre un oggetto denormalizzato e più facilmente
utilizzabile dalla view.
Visualizzazione dei dati e interazione con l’utente: React
I componenti di visualizzazione dei dati ed interazione con l’utente sono definiti a partire dal framework
ReactJS.
Ciascuno degli editor precedentemente descritti è caratterizzato da un set di componenti, che ne vanno a
definire l’aspetto grafico e la logica di funzionamento. Nei seguenti paragrafi verranno descritti, per ogni
editor, i componenti principali.
Model Editor
Il model editor è composto da un componente padre che permette la navigazione tra le diverse modalità di
archiviazione dei dati, e da quattro componenti figli, uno per ogni modalità di archiviazione:
SharedPreferences permette la definizione di ciascun record da archiviare tramite
sharedPreferences. Per ogni record, è possibile definire chiave, tipo e valore di default.
File Storage permette la definizione dei files di interesse per l’applicazione, che dovranno essere
gestiti e archiviati nel file system del dispositivo mobile. Per ogni file, è possibile definire nome,
estensione, e marcare se sia un file temporaneo.
Relational database permette la definizione delle entità che verranno archiviate nel database. Per
ogni entità è poi disponibile un apposito editor dove è possibile configurare il nome della chiave
primaria, eventuali attributi (definiti da nome e tipo) e relazioni (definite da nome, cardinalità e
entità correlata)
Cloud database permette la definizione degli oggetti che saranno archiviati nell’albero JSON di
Firebase Realtime Database. E’ sempre disponibile l’oggetto RootObject, che mappa la radice
dell’albero DOM e può essere acceduto direttamente. Ogni altro oggetto può essere acceduto
attraverso la specifica del suo percorso a partire dal RootObject.
Ciascun editor permette inoltre di abilitare o disabilitare una specifica modalità di storage per l’applicazione
che si sta sviluppando, in modo da eliminare modalità che siano non di interesse e ridurre la dimensione
della codebase generata.
58
Smartphone & Tablet Editor
Lo smartphone & tablet editor consente di definire le varie schermate di un’applicazione mobile. Questo
componente è molto complesso, data dalla ricchezza di controlli e di opzioni per il layout, e delle dinamiche
di costituzione delle scene come composizione di viste.
Come definito nel modello di riferimento (§3.1.1), la gerarchia di elementi che costituiscono la view è la
seguente:
Scena: corrisponde al concetto di schermata di un’applicazione. È l’elemento di primo livello nella
gerarchia, non è contenuto in nessun altro elemento.
View controller: corrisponde al concetto di vista. Le viste vengono aggregate a formare scene. Ogni
scena può contenere una sola vista, più viste accessibili tramite un menù a schede, o più viste
disposte sullo schermo simultaneamente in modo configurabile dall’utente.
Control chain: è un elemento opzionale nella catena, come descritto al punto successivo. Le control
chains implementano meccanismi di distribuzione di controlli sullo schermo lungo un dato asse.
Ui Controls: ogni elemento rappresenta un controllo, sia esso un testo, un pulsante o un video
player. I controlli possono essere inseriti, per motivi di posizionamento, in una control chain,
oppure possono essere inseriti direttamente come figli di un view controller.
I componenti fondamentali per questo editor sono:
Navigator permette di selezionare quale vista o scena modificare
Canvas mostra la vista o la scena corrente all’interno di una cornice che rappresenta uno
smartphone o un tablet. Tipologia di device, produttore e modello sono selezionabili tramite un
apposito selettore. L’immagine mostrata è dotata della funzione di zoom.
Palette offre una selezione di controlli che possono essere aggiunti alla vista corrente tramite drag
and drop.
Inspector permette di modificare le proprietà di ogni singolo componente inserito tramite la
palette. In base all’elemento selezionato sulla canvas, l’inspector modifica il suo aspetto e le opzioni
disponibili. È sempre possibile modificare il nome di un componente, i suoi vincoli di posizione, le
dimensioni e le spaziature (padding e margine). Altre proprietà sono disponibili in base al
componente: per le caselle di testo è disponibile il testo contenuto, per i pulsanti è disponibile un
menù che consente di selezionare a quale vista o a quale scena passare quando il tasto viene
premuto. Lo stesso componente viene anche utilizzato per la modifica del menù dell’applicazione,
anche se questo non è un controllo nel senso proprio del termine.
Scene Model Editor
Il scene model editor permette di selezionare dati provenienti dai vari meccanismi di persistenza perché
siano resi disponibili ai controlli inseriti nella view. Chiameremo questi dati “elementi connessi”. Questo
editor si compone di un selettore che consente di selezionare di volta in volta su quale scena andare a
lavorare, e di un componente che, ricevuta in ingresso la scena, mostra gli elementi attualmente connessi e
gli elementi disponibili per una connessione.
Manifest Editor
Il manifest editor è un componente molto semplice, costituito da un solo form, che consente la modifica
delle proprietà globali dell’applicazione che si sta progettando, che nella versione attuale sono soltanto
due: il nome dell’applicazione e il prefisso che identifica lo sviluppatore.
59
4.3.2 Design dei dati
Questa sezione presenta il modello dei dati utilizzato da Protocode e gestito tramite Redux. Dato che
l’obiettivo finale di Protocode è quello di generare un modello dell’applicazione compatibile con le
definizioni di cui al capitolo §3, lo stato mantenuto in Protocode deve contenere tutte le informazioni
necessarie allo scopo. Conseguentemente, ciascuna classe di oggetti definita dal modello di riferimento
corrisponde ad una o più classi definite nel modello dei dati di Protocode, in modo che le istanze delle
seconde possano essere utilizzate per definire istanze delle prime. È possibile quindi pensare che il modello
dei dati utilizzato da Protocode possa essere ottenuto applicando una serie di trasformazioni al modello di
riferimento.
La trasformazione più significativa riguarda le relazioni, soprattutto di composizione. Ogni relazione infatti
viene espressa inserendo in ciascuno degli oggetti coinvolti riferimenti non equivoci alle altre parti
coinvolte. Questo tuttavia non è sufficiente per gestire le relazioni di composizione: quando infatti un
elemento viene eliminato, anche tutti gli elementi in esso contenuti devono essere eliminati
conseguentemente, e questo vale ricorsivamente. Al fine di implementare in modo efficiente queste
eliminazioni, è necessario che ogni oggetto contenuto possieda riferimenti a tutti gli oggetti che lo
contengono, ricorsivamente. Se quindi C è contenuto in B e B è contenuto in A, C dovrà possedere il
riferimento sia a B che ad A. Questo semplifica la gestione delle operazioni di cancellazione: il reducer che si
occupa di gestire le entità di tipo C può reagire alle azioni di cancellazione di A e di B in modo opportuno,
poiché possiede tutti gli elementi per determinare su quali oggetti da lui gestiti tali azioni producano un
effetto. Questa trasformazione, negli ambienti che usano Redux, è nota come normalizzazione.
A questo punto è possibile definire lo stato di Protocode come un’oggetto JavaScript che contiene, per ogni
chiave, un DataStore dedicato ad una (o più) classi di oggetti, ciascuna delle quali può essere collegata al
modello di riferimento. Lo stato di Protocode ha la seguente forma:
{
application: applicationObject,
scenes: DataStore<Scene>,
viewControllers: DataStore<ViewController>,
alertDialogs: DataStore<AlertDialog>,
progressDialogs: DataStore<ProgressDialog>,
asyncTasks: DataStore<AsyncTask>,
uiPhoneControls: DataStore<UiPhoneControl>,
constraints: DataStore<Constraint>,
listViewCells: DataStore<ListViewCell>,
gridViewCells: DataStore<GridViewCell>,
sourceTypes: DataStore<SourceType>,
controlChains: DataStore<ControlChain>,
containers: DataStore<Container>,
navigations: DataStore<Navigation>,
menu: DataStore<MenuItem>,
dataHandlers: data handlers object,
preferenceRecords: DataStore<PreferenceRecord>,
fileStorageRecords: DataStore<StorageRecord>,
entities: DataStore<Entity>,
entityAttributes: DataStore<EntityAttribute>,
entityRelations: DataStore<EntityRelation>,
cloudObjects: DataStore<CloudObject>,
cloudObjectAttributes: DataStore<CloudObjectAttribute>,
adapterBindings: DataStore<AdapterBinding>,
modelConnectors: DataStore<ModelConnector>
}
Questo schema mostra le chiavi che compongono lo stato di Protocode e il contenuto di ciascuna di esse.
Fatta eccezione per due chiavi, tutte le chiavi contengono un DataStore (descritto in Gestione dei dati:
60
Redux) dedicato ad una o più classi di oggetti del modello di riferimento. In generale ogni classe del
modello ha una propria controparte in Protocode, e tale controparte possiede un proprio DataStore.
L’eccezione più notevole a questa politica riguarda i controlli utente, che sono tutti memorizzati in un unico
DataStore dedicato ad UiPhoneControl (si può vedere questa operazione come una riduzione alla
sovraclasse, tipica della progettazione dei database).
Nei prossimi paragrafi verranno presentate le varie chiavi (suddivise per area di riferimento: Model, View o
Controller), la struttura degli oggetti in esse memorizzati e come questi corrispondono alle definizioni del
modello di riferimento, e le azioni a cui reagiscono i rispettivi reducers. Ogni DataStore infatti evolve in base
ad azioni diverse, che vengono interpretate dai reducer e convertite in opportuni aggiornamenti dello stato.
Design dei dati riguardanti il model
dataHandlers
Questa chiave costituisce un caso eccezionale rispetto alle altre chiavi in quanto non contiene un DataStore
ma un singolo semplice oggetto JavaScript che possiede le seguenti proprietà
Proprietà Descrizione
preferences Contiene lo stato di attivazione della persistenza tramite SharedPreferences
files Contiene lo stato di attivazione della persistenza tramite FileStorage
database Contiene lo stato di attivazione della persistenza tramite database relazionale
cloud Contiene lo stato di attivazione della persistenza tramite cloud Tabella 4.1 Proprietà di "data handler object"
Questo oggetto contiene quindi l’indicazione di quali strategie di persistenza il modellatore vuole includere
nell’applicazione. Questo oggetto non ha una controparte nel modello di riferimento, ma contiene
informazioni necessarie alla sua generazione: il modello esportato conterrà informazioni relative alle sole
modalità di persistenza il cui stato di attivazione (contenuto in questo oggetto) è vero.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
ENABLE_PREFERENCES: abilita la persistenza tramite SharedPreferences
DISABLE_PREFERENCES: disabilita la persistenza tramite SharedPreferences
ENABLE_FILE_STORAGE: abilita la persistenza tramite files
DISABLE_FILE_STORAGE: disabilita la persistenza tramite files
ENABLE_DATABASE: abilita la persistenza tramite database relazionale
DISABLE_DATABASE: disabilita la persistenza tramite database relazionale
ENABLE_CLOUD_DATABASE: abilita la persistenza tramite database cloud
DISABLE_CLOUD_DATABASE: disabilita la persistenza tramite database cloud
preferenceRecords
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe PreferenceRecord del modello di riferimento (§3.2.3). Gli oggetti contenuti in questo DataStore
possiedono le seguenti proprietà
61
Proprietà Descrizione Indicizzata
id Identificativo univoco si
key La chiave associata alla preferenza all’interno del dizionario delle sharedPreferences
type Il tipo del valore associato alla chiave. Può essere uno tra string, int, long, boolean, float e double
value Il valore di default associato alla chiave specificata Tabella 4.2 Proprietà di oggetti di tipo PreferenceRecord
Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento ad eccezione della
proprietà id, necessaria in Protocode per esprimere relazioni. Tale proprietà non viene esportata nel
modello.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_PREFERENCE_RECORD: crea un oggetto in questo DataStore, inizializzando tutte le
proprietà con valori contenuti nell’azione
DELETE_PREFERENCE_RECORD: cancella un oggetto da questo DataStore, dato il suo id
DISABLE_PREFERENCES: cancella tutti gli oggetti da questo DataStore
fileStorageRecords
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe StorageRecord del modello di riferimento (§3.2.2). Gli oggetti contenuti in questo DataStore
possiedono le seguenti proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
name Il nome del file
extension L’estensione del file. Può essere testo (txt), immagine (png) o altro
isTemp Indica se il file sia temporaneo. Questo influenza la gestione del file da parte del sistema operativo: i files temporanei possono essere eliminati in caso di necessità di recuperare spazio su disco
Tabella 4.3 Proprietà di oggetti di tipo FileStorageRecord
Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento ad eccezione della
proprietà id, necessaria in Protocode per esprimere relazioni. Tale proprietà non viene esportata nel
modello.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_FILE_STORAGE_RECORD: crea un oggetto in questo DataStore, inizializzando tutte le
proprietà con valori contenuti nell’azione
DELETE_FILE_STORAGE_RECORD: cancella un oggetto da questo DataStore, dato il suo id
DISABLE_FILE_STORAGE: cancella tutti gli oggetti da questo DataStore
entities
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe Entity del modello di riferimento (§3.2.1). Gli oggetti contenuti in questo DataStore possiedono le
seguenti proprietà
62
Proprietà Descrizione Indicizzata
id Identificativo univoco si
name Il nome dell’entità. Deve essere univoco all’interno del database.
primaryKey Il nome del campo che svolge il ruolo di chiave primaria. E’ implicitamente di tipo string.
Tabella 4.4 Proprietà di oggetti di tipo Entity
Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento ad eccezione della
proprietà id, necessaria in Protocode per esprimere relazioni. Tale proprietà non viene esportata nel
modello. Le relazioni entityAttributes e entityRelations sono modellate mediante attributi associati alle
entità EntityAttribute e EntityRelation contenute in altre chiavi dello stato di Protocode.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_ENTITY: crea un oggetto in questo DataStore, inizializzando tutte le proprietà con valori
contenuti nell’azione
EDIT_ENTITY: modifica un oggetto di questo DataStore, dato il suo id. Le modifiche sono specificate
nell’azione
DELETE_ENTITY: cancella un oggetto da questo DataStore, dato il suo id
DISABLE_DATABASE: cancella tutti gli oggetti da questo DataStore
entityAttributes
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe EntityAttribute del modello di riferimento (§3.2.1). Gli oggetti contenuti in questo DataStore
possiedono le seguenti proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
entityId Identificativo dell’entità relazionale (§3.2.4) che possiede l’attributo si
name Il nome dell’attributo
type Il tipo dell’attributo: string, int, date, boolean, float oppure double Tabella 4.5 Proprietà di oggetti di tipo EntityAttribute
Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento ad eccezione della
proprietà id, necessaria in Protocode per esprimere relazioni e omessa poi in fase di generazione del
modello, e della proprietà entityId necessaria per normalizzare la relazione entityAttributes di Entity
(§3.2.1).
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_ENTITY_ATTRIBUTE: crea un oggetto in questo DataStore, inizializzando tutte le proprietà
con valori contenuti nell’azione
DELETE_ENTITY_ATTRIBUTE: elimina un oggetto da questo DataStore, dato il suo id.
DELETE_ENTITY: cancella tutti gli oggetti in relazione con l’oggetto Entity eliminato, il cui id è
contenuto nell’azione
DISABLE_DATABASE: cancella tutti gli oggetti da questo DataStore
63
entityRelations
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe EntityRelation del modello di riferimento (§3.2.1). Gli oggetti contenuti in questo DataStore
possiedono le seguenti proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
fromEntityId Identificativo dell’entità relazionale (§3.2.4) che possiede l’attributo si
name Il nome della relazione
cardinality La cardinalità della relazione, a scelta tra 1:1, 1:N, N:1.
fromEntity L’entità sorgente della relazione
toEntity L’entità destinazione della relazione si Tabella 4.6 Proprietà di oggetti di tipo EntityRelation
Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento ad eccezione della
proprietà id, necessaria in Protocode per esprimere relazioni e omessa poi in fase di generazione del
modello, e della proprietà fromEntityId necessaria per normalizzare la relazione entityRelations di Entity
(§3.2.1).
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_ENTITY_RELATION: crea un oggetto in questo DataStore, inizializzando tutte le proprietà
con valori contenuti nell’azione
DELETE_ENTITY_RELATION: elimina un oggetto da questo DataStore, dato il suo id.
DELETE_ENTITY: cancella tutti gli oggetti in relazione con l’oggetto Entity eliminato, il cui id è
contenuto nell’azione
DISABLE_DATABASE: cancella tutti gli oggetti da questo DataStore
cloudObjects
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe CloudObject del modello di riferimento (§3.2.4). Gli oggetti contenuti in questo DataStore
possiedono le seguenti proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
name Il nome di questa classe di oggetti. Deve essere univoco nel contesto del database cloud
Tabella 4.7 Proprietà di oggetti di tipo CloudObject
Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento ad eccezione della
proprietà id, necessaria in Protocode per esprimere relazioni. Tale proprietà non viene esportata nel
modello. La relazione attributes è modellata mediante un attributo associato all’entità
CloudObjectAttribute, le cui istanze sono contenute in altre chiavi dello stato di Protocode.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_CLOUD_OBJECT: crea un oggetto in questo DataStore, inizializzando tutte le proprietà con
valori contenuti nell’azione
EDIT_CLOUD_OBJECT: modifica un oggetto di questo DataStore, dato il suo id. Le modifiche sono
specificate nell’azione
64
DELETE_CLOUD_OBJECT: cancella un oggetto da questo DataStore, dato il suo id
DISABLE_CLOUD_DATABASE: cancella tutti gli oggetti da questo DataStore
ENABLE_CLOUD_DATABASE: crea l’oggetto radice dell’albero in questo DataStore con l’id fisso (il
cui valore è “__root__”) e nome fisso (il cui valore è “Root Object”)
cloudObjectAttributes
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe EntityAttribute del modello di riferimento (§3.2.4). Gli oggetti contenuti in questo DataStore
possiedono le seguenti proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
objectId Identificativo dell’oggetto cloud (si veda Archiviazione tramite database cloud) che possiede l’attributo
si
name Il nome dell’attributo
type Il tipo dell’attributo, a scelta tra string, int, float, double, boolean, ref, ref_list. I tipi ref e ref_list indicano rispettivamente che questo attributo contiene un oggetto e una lista omogenea di oggetti
object Valido solamente per attributi di tipo ref e ref_list, contiene l’identificativo dell’oggetto cloud (si veda Archiviazione tramite database cloud) che descrive la tipologia di oggetti memorizzati in questo attributo
si
Tabella 4.8 Proprietà di oggetti di tipo CloudObjectAttribute
Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento ad eccezione della
proprietà id, necessaria in Protocode per esprimere relazioni e omessa poi in fase di generazione del
modello, e della proprietà objectId necessaria per normalizzare la relazione attributes di CloudObject
(§3.2.4).
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_CLOUD_OBJECT_ATTRIBUTE: crea un oggetto in questo DataStore, inizializzando tutte le
proprietà con valori contenuti nell’azione
DELETE_ CLOUD_OBJECT _ATTRIBUTE: elimina un oggetto da questo DataStore, dato il suo id.
DELETE_ CLOUD_OBJECT: cancella tutti gli oggetti in relazione con l’oggetto CloudObject eliminato,
il cui id è contenuto nell’azione
DISABLE_DATABASE: cancella tutti gli oggetti da questo DataStore
65
Design dei dati riguardanti la view
application
Questa chiave costituisce un caso eccezionale rispetto alle altre chiavi in quanto non contiene un DataStore
ma un singolo semplice oggetto JavaScript che possiede le seguenti proprietà
Proprietà Descrizione
name Il nome dell’applicazione
companyIdentifier L’identificativo dello sviluppatore, segue le convenzioni dei packages Java Tabella 4.9 Proprietà dell'oggetto Application
Le relazioni scenes, viewControllers e dataHandler non sono esplicitamente modellate, in quando la
relazione con dataHandler è banale data l’unicità del dataHandler stesso (come descritto in dataHandlers),
e le relazioni scenes e viewControllers contengono tutte le scene e tutti i viewController definiti, e possono
essere quindi banalmente dedotte dai DataStore dedicati a tali classi di dati.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_APPLICATION: inizializza name a “newApp” e companyIdentifier a “it.polimi”
CHANGE_NAME: modifica la proprietà name impostando un valore contenuto nell’azione
CHANGE_COMPANY_IDENTIFIER: modifica la proprietà companyIdentifier impostando un valore
contenuto nell’azione
La mancanza dell’azione DELETE_APPLICATION è intenzionale: tale azione ha effetti sull’intero stato, e viene
gestita separatamente eseguendo un hard set dell’intero stato ad un valore predefinito in attesa della
creazione di una nuova applicazione. Se così non fosse, ogni segmento dello stato dovrebbe contenere
un’azione di reset del suo contenuto associata ad azioni di questa tipologia. Tale soluzione sarebbe tuttavia
troppo pesante da implementare.
scenes
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe Scene del modello di riferimento. Gli oggetti contenuti in questo DataStore possiedono le seguenti
proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
name Il nome della scena
launcher Indica se la scena viene visualizzata all’avvio dell’applicazione. Solo una scena può essere contrassegnata come launcher
Tabella 4.10 Proprietà di oggetti di tipo Scene
Tutte le proprietà definite corrispondono a proprietà definite sul modello di riferimento. Le proprietà
typeSmartphone, typeTablet e le relazioni childViewControllers e parentViewControllers sono dedotte a
partire da informazioni memorizzate in altre chiavi. In particolare le relazioni con altri viewController sono
memorizzate utilizzando attributi dei viewController stessi, e le proprietà typeSmartphone e typeTablet
sono rispettivamente allocate ai parentViewControllers corrispondenti.
66
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_SCENE: crea un oggetto in questo DataStore, inizializzando tutte le proprietà con valori
contenuti nell’azione
DELETE_ SCENE: elimina un oggetto da questo DataStore, dato il suo id.
EDIT_SCENE: modifica un oggetto in questo DataStore dato il suo id e le modifiche da applicare.
Qualora le modifiche coinvolgano l’attributo launcher, viene assicurato che solo l’ultima scena che
abbia richiesto di essere launcher abbia tale attributo impostato a vero.
viewControllers
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe ViewController del modello di riferimento. Differentemente dal modello di riferimento, tuttavia, i
viewController che svolgono il ruolo di parentViewController contengono anche la definizione del tipo di
layout che essi utilizzano (SINGLE_VC, SINGLE_VC_TAB, MULTI_VC). Gli oggetti contenuti in questo
DataStore possiedono le seguenti proprietà
ViewController Descrive un frammento utilizzato per comporre una scena Indicizzata
id Identificativo univoco si
name Il nome del viewController.
backgroundColor Il colore di sfondo del viewController, in formato RGB esadecimale
backgroundImage Il path dell’immagine di sfondo del viewController. Ha priorità sul colore.
type Valido solo per i parentViewControllers. Rappresenta il tipo di layout della scena, può essere SingleVC, SingleVCTab oppure MultiVC.
scene Valido solo per i parentViewControllers. Indica il riferimento alla scena di cui questo viewController definisce il layout
si
Tabella 4.11 Proprietà di oggetti di tipo ViewController
Gli attributi id, name, backgroundColor e backgroundImage ricalcano gli omonimi del modello di
riferimento. Gli attributi scene e type sono stati introdotti per semplificare la gestione dei
parentViewControllers. Differentemente dal modello di riferimento, nel modello dei dati di Protocode ogni
scena possiede sempre due parentViewController, uno dedicato al layout per cellulare e l’altro al layout per
tablet. È quindi naturale associare la definizione del tipo di layout al relativo parentViewController. Questa
struttura può comunque essere ricondotta al modello di riferimento, esportando solamente quei
parentViewController il cui tipo sia MULTI_VC. Tutte le relazioni di contenimento in cui la classe
ViewController è coinvolta nel modello di riferimento con ruolo di contenitore sono implementate in
Protocode attraverso attributi delle entità figlie che contengono l’id del viewController contenitore.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_SCENE: crea due oggetti in questo DataStore, che costituiscono i due parentViewController
della scena creata con questa azione. I campi vengono inizializzati nel seguente modo
o backgroundColor e backgroundImage sono inizializzati ai valori di default
o scene viene impostato all’id della scena appena creata (contenuto nell’azione)
o type assume il valore predefinito SINGLE_VC
o id viene ottenuto concatenando l’id della scena con le parole chiave SMARTPHONE o
TABLET, a seconda del tipo di device su cui il viewController agisca da parentViewController
o name assume il valore predefinito “Parent ViewController smartphone” o
“parentViewController tablet” con le stesse regole del campo id
67
DELETE_ SCENE: elimina i parentViewController relativi alla scena eliminata, il cui id è contenuto
nell’azione stessa e nel campo scene degli elementi da eliminare
CREATE_VIEWCONTROLLER: crea un nuovo viewController (mai un parentViewController). I campi
sono inizializzati nel seguente modo
o id viene ottenuto dall’azione
o name assume il valore “ViewController{N}” dove {N} viene sostituito da un numero
progressivo
o backgroundColor viene inizializzato a “#ffffff” (bianco)
o backgroundImage viene inizializzato alla stringa vuota (nessuna immagine)
o scene assume il valore NULL
o type non è rilevante, assume convenzionalmente il valore SINGLE_VC
EDIT_VIEW_CONTROLLER: modifica un elemento di questo DataStore dato il suo id e le modifiche
da applicare
DELETE_VIEW_CONTROLLER: elimina un elemento di questo DataStore, dato il suo id (contenuto
nell’azione)
controlChains
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe ControlChain del modello di riferimento. Gli oggetti contenuti in questo DataStore possiedono le
seguenti proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
viewControllerId Identificativo dell’oggetto di tipo ViewController a cui questa catena appartiene logicamente
si
type Il tipo di catena, ovvero il modo in cui viene calcolato lo spazio tra i vari controlli. Può essere spread, spread_inside, packed o weighted.
axis L’asse lungo cui i controlli vengono distributi, può essere orizzontale o verticale
spacing Lo spazio minimo tra mantenere tra i vari controlli inseriti nella catena, espresso in punti. Lo spazio effettivamente mantenuto varia in base al tipo di catena
bias Valido solo per catene packed, definisce il rapporto tra la dimensione dello spazio lasciato prima dei componenti e lo spazio totale disponibile al netto della dimensione dei componenti lungo l’asse della catena.
members Collezione degli identificativi degli oggetti appartenenti alla catena Tabella 4.12 Proprietà di oggetti di tipo ControlChain
Ad eccezione della proprietà viewControllerId, utilizzata per definire a quale viewController la catena
appartiene, consentendo così di definire la relazione controlChains della classe ViewController del modello
di riferimento, tutte le altre proprietà possiedono un’omonima corrispondente nel modello di riferimento.
La proprietà nControls del modello di riferimento è calcolata al momento dell’esportazione del modello,
mentre la relazione controls è definita da attributi impostati sui controlli stessi.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_CONTROL_CHAIN: crea una nuova catena di controlli (vuota) in questo DataStore. La
catena così creata non è valida (in quanto deve contenere almeno due controlli utente), e a meno
di modifiche successive non viene esportata.
68
CREATE_UI_PHONE_CONTROL: nel caso in cui il controllo creato con questa azione sia marcato
come appartenente ad una catena di controlli, il suo id viene aggiunto alla collezione members di
tale catena
DELETE_UI_PHONE_CONTROL: nel caso in cui l’identificativo del controllo eliminato con questa
azione sia contenuto nella collezione members di qualche catena, viene rimosso
EDIT_CONTROL_CHAIN: modifica i parametri di un elemento di questo DataStore, dato il suo id e le
modifiche da apportare (entrambi contenuti nell’azione)
DELETE_CONTROL_CHAIN: rimuove un oggetto da questo DataStore, dato il suo id (contenuto
nell’azione)
DELETE_VIEW_CONTROLLER: elimina tutte le catene appartenenti al viewController eliminato, il cui
id è contenuto sia nell’azione che nell’attributo viewControllerId degli elementi da eliminare
Al momento della creazione, i campi di questo oggetto sono inizializzati come segue
id assume il valore specificato nell’azione
viewControllerId assume il valore specificato nell’azione
type viene inizializzato a spread
axis viene inizializzato a horizontal
spacing viene inizializzato a 0
bias viene inizializzato a 0.5
members viene inizializzata ad una collezione vuota
menu
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe MenuItem (descritta in Menu e MenuItem) del modello di riferimento. Gli oggetti contenuti in questo
DataStore possiedono le seguenti proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
name Il nome dell’elemento, utilizzato dall’utente per distinguerlo da altri elementi
title Il testo visualizzato nel menu per questo elemento Tabella 4.13 Proprietà di oggetti di tipo MenuItem
Tutte le proprietà elencate corrispondono a proprietà del modello di riferimento. Questo DataStore inoltre
consente di esportare nel modello la classe Menu, che possiede solo la relazione menuItems, che a sua
volta contiene tutti gli elementi di menù definiti per l’applicazione, ovvero tutti gli elementi di questo
DataStore.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_MENU_ITEM: crea un nuovo elemento in questo DataStore, i cui campi sono inizializzati a
partire da valori contenuti nell’azione stessa
EDIT_MENU_ITEM: modifica un elemento di questo DataStore, dato il suo id e le modifiche da
applicare (entrambi contenuti nell’azione)
DELETE_MENU_ITEM: elimina un elemento da questo DataStore, dato il suo id (contenuto
nell’azione)
69
uiPhoneControl
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze di tutte
le classi che estendono UiPhoneControl nel modello di riferimento, ad eccezione della classe Container i cui
elementi sono gestiti separatamente. Tutti gli elementi possiedono quindi le proprietà definite su
UiPhoneControl e UiControl nel modello di riferimento, più alcune proprietà distintive in base alla loro
classe specifica, più alcune proprietà dimensionali di competenza della classe DimensionConstraint nel
modello di riferimento. Questo è possibile grazie all’ereditarietà prototipica del linguaggio JavaScript. Gli
oggetti contenuti in questo DataStore possiedono le seguenti proprietà comuni
Proprietà Descrizione Indicizzata
id Identificativo univoco si
viewControllerId Identificativo univoco del ViewController a cui questo elemento appartiene
si
uiPhoneControlType Tipologia del controllo (necessaria per capire a quale sottoclasse di UiPhoneControl afferisce)
si
controlChainId Eventuale identificativo della catena di controlli (ControlChain) in cui il controllo è inserito
si
name Il nome del controllo, utilizzato per distinguerlo da altri controlli
title Il testo contenuto nel controllo (valido solo nei controlli che contengono testo)
minWidth La larghezza minima del controllo, non può essere modificata dall’utente, viene utilizzata come limite inferiore per l’attributo width quando la larghezza viene impostata dall’utente in modo assoluto
minHeight Come per minWidth, ma per l’altezza
defaultWidth La larghezza predefinita del componente. Non è modificabile dall’utente, prende il posto della larghezza nel caso in cui essa non sia espressa né in modo assoluto, né in modo percentuale, né sia vincolata dall’aspect ratio
defaultHeight Come per defaultWidth, ma per l’altezza
marginTop Lo spazio da lasciare sopra il componente nel caso in cui il componente presenti un vincolo valido per il lato superiore
marginBottom Lo spazio da lasciare sopra il componente nel caso in cui il componente presenti un vincolo valido per il lato inferiore
marginRight Lo spazio da lasciare sopra il componente nel caso in cui il componente presenti un vincolo valido per il lato destro
marginLeft Lo spazio da lasciare sopra il componente nel caso in cui il componente presenti un vincolo valido per il lato sinistro
paddingTop Lo spazio da lasciare all’interno del componente tra il lato superiore e il contenuto del componente
paddingRight Lo spazio da lasciare all’interno del componente tra il lato destro e il contenuto del componente
paddingBottom Lo spazio da lasciare all’interno del componente tra il lato inferiore e il contenuto del componente
paddingLeft Lo spazio da lasciare all’interno del componente tra il lato sinistro e il contenuto del componente
width La larghezza del componente. Può essere interpretata come un valore assoluto, una percentuale (nel range [0, 100]), o essere ignorato, in base al valore di widthMode
70
Proprietà Descrizione Indicizzata
height L’altezza del componente. Può essere interpretata come un valore assoluto, una percentuale (nel range [0, 100]), o essere ignorato, in base al valore di heightMode
ratioWidth Il numeratore dell’aspect ratio espresso come numero razionale
ratioHeight Il denominatore dell’aspect ratio espresso come numero razionale
posX La distanza, in punti, dell’estremo sinistro del componente dal bordo sinistro del viewController. Valida solo in assenza di vincoli lungo l’asse orizzontale
posY La distanza, in punti, del lato superiore del componente dal bordo superiore del viewController. Valida solo in assenza di vincoli lungo l’asse verticale
widthMode La modalità di espressione della larghezza. Può essere
exact: width contiene la larghezza in dip del componente
percent: width contiene la larghezza percentuale [0, 100] del componente rispetto alla larghezza del viewController
auto: la larghezza è calcolata automaticamente a partire dagli altri attributi dimensionali, l’attributo width viene ignorato
heightMode Come widthMode, ma per l’altezza
ratioMode La modalità di espressione dell’aspect ratio. Può essere
exact: ratioWidth e ratioHeight contengono il numeratore e il denominatore dell’aspect ratio espressi come numero razionale
auto: l’aspect ratio è calcolato automaticamente a partire dagli altri attributi dimensionali
controlChainPosition La posizione relativa di questo elemento rispetto agli altri elementi della controlChain a cui questo elemento appartiene. I componenti di una control chain sono ordinati in base a questo attributo, anche con valori non consecutivi. Non valido se il componente non appartiene ad una control chain
controlChainWeight Il peso relativo di questo componente rispetto agli altri componenti in una catena di tipo weighted. Non valido in altri contesti.
Tabella 4.14 Proprietà di oggetti di tipo UiPhoneControl
La trasformazione di questo oggetto in entità del modello di riferimento è piuttosto complessa. Gli attributi
id, posX, posY, margin* e padding* vengono mappati sugli attributi omonimi della classe UiControl. Gli
attributi defaultWidth, defaultHeight e controlChainWeight vengono mappati su attributi omonimi (tranne
controlChainWeight mappato su weight) della classe UiPhoneControl. Tale classe richiede anche altri
attributi, computati come segue
controlChain è la catena di controlli a cui l’elemento appartiene, e può essere ricavata tramite
controlChainId con una ricerca nel DataStore relativo alle catene di controlli
indexInChain, precedentInChain e followingInChain vengono calcolati inserendo in una lista ordinata
per controlChainPosition tutti gli elementi appartenenti alla stessa catena di controlli
positionConstraint è calcolabile a partire da attributi impostati sugli oggetti della collezione
constraints
dimensionConstraint è calcolabile a partire da attributi presenti in questa definizione
71
Il calcolo di un eventuale dimensionConstraint associato è eseguito nel seguente modo
Se widthMode = heightMode = ratioMode = auto, nessun vincolo viene generato
Se widthMode = exact l’attributo fixedWidth del vincolo è impostato al valore di width di questo
oggetto, e l’attributo widthPercent ad un valore nullo
Se widthMode = percent l’attributo widthPercent del vincolo è impostato al valore di width di
questo oggetto diviso per 100, e l’attributo fixedWidth ad un valore nullo
Se heightMode = exact l’attributo fixedHeight del vincolo è impostato al valore di height di questo
oggetto, e l’attributo heightPercent ad un valore nullo
Se heightMode = percent l’attributo heightPercent del vincolo è impostato al valore di height di
questo oggetto diviso per 100, e l’attributo fixedHeight ad un valore nullo
Se ratioMode = exact l’attributo fixedRatio del vincolo è impostato alla stringa
ratioWidth + “:” + ratioHeight
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_UI_PHONE_CONTROL: crea un nuovo elemento in questo DataStore, inizializzando tutti gli
attributi ai valori predefiniti tranne i seguenti, che vengono letti dall’azione di creazione
o uiPhoneControlType che contiene la definizione del tipo di controllo
o viewControllerId
o controlChainId (se presente)
o id
DELETE_UI_PHONE_CONTROL: elimina un elemento da questo DataStore, dato il suo id (contenuto
nell’azione)
EDIT_UI_PHONE_CONTROL: modifica un elemento di questo DataStore, dato il suo id e le
modifiche da applicare (contenuti nell’azione)
DELETE_VIEW_CONTROLLER: elimina tutti gli elementi da questo DataStore il cui attributo
viewControllerId contenga l’id del viewController eliminato da questa azione
DELETE_CONTROL_CHAIN: elimina tutti gli elementi da questo DataStore il cui attributo
controlChainId contenga l’id del viewController eliminato da questa azione
In base al valore di uiPhoneControlType, altre proprietà possono essere aggiunte a quelle predefinite. Tali
proprietà servono per istanziare la sottoclasse corrispondente alla tipologia di controlli nel modello di
riferimento. I controlli che necessitano di proprietà specifiche sono i seguenti.
AudioRecorder
backgroundType Indica se il controllo contiene la scritta rec (in questo caso ha valore “normal”) o il simbolo del microfono (valore “icon”)
preview Indica quale audioPlayer riproduce l’ultima registrazione effettuata da questo controllo. E’ opzionale, e contiene l’id di tale controllo.
Tabella 4.15 Proprietà aggiuntive di controlli AudioRecorder
Button
textColor Il colore del testo scritto all’interno del pulsante
backgroundColor Il colore di sfondo del pulsante
clickColor Il colore di sfondo del pulsante durante la pressione dell’utente
borderRadius Il raggio dei bordi arrotondati del pulsante Tabella 4.16 Proprietà aggiuntive di controlli Button
72
Card
title Il titolo della card
subtitle Il sottotitolo della card
numActions Il numero di azioni da inserire in calce alla card Tabella 4.17 Proprietà aggiuntive di controlli Card
EditText
textColor Il colore del testo mostrato nel controllo
placeHolder Il testo di default mostrato nel controllo in caso di assenza di testo inserito dall’utente
textSize La dimensione del testo del controllo Tabella 4.18 Proprietà aggiuntive di controlli EditText
GridView
gridType Definisce il tipo di celle inserite nella griglia
simple: ogni cella contiene solo testo
image: ogni cella contiene un’immagine
detailed: ogni cella contiene un’immagine con del testo posizionato come didascalia
Tabella 4.19 Proprietà aggiuntive di controlli GridView
Label
textAlign L’allineamento del testo: a sinistra, a destra o centrato
textColor Il colore del testo
textDecoration Può essere grassetto, corsivo o normale
textSize La dimensione del testo in dip Tabella 4.20 Proprietà aggiuntive di controlli Label
ListView
listViewType Il tipo di celle contenute
simple: solo testo
image: testo con un’immagine
detailed: immagine, titolo e sottotitolo Tabella 4.21 Proprietà aggiuntive di controlli ListView
Map
latitude La latitudine del punto su cui la mappa è centrata
longitude La longitudine del punto su cui la mappa è centrata Tabella 4.22 Proprietà aggiuntive di controlli Map
PhotocameraController
backgroundType Il tipo di contenuto dell’elemento, può essere la scritta Photocamera (valore “normal”) o il simbolo della macchina fotografica (valore “icon”)
preview Opzionale, contiene il valore dell’attributo id del controllo che mostra l’ultima immagine scattata tramite questo controllo
Tabella 4.23 Proprietà aggiuntive di controlli PhotocameraController
VideocameraController
backgroundType Il contenuto del controllo: può essere il testo “videocamera” (valore “normal”) o l’immagine di una videocamera (valore “icon”)
preview Opzionale, contiene il valore dell’attributo id del controllo che mostra l’ultimo video acquisito tramite questo controllo
Tabella 4.24 Proprietà aggiuntive di controlli VideocameraController
73
WebView
htmlFileUri Il percorso (eventualmente di rete) del file HTML da mostrare Tabella 4.25 Proprietà aggiuntive di controlli WebView
containers
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe Container del modello di riferimento. Pur essendo anch’essi istanze di UiPhoneControl, il loro
trattamento è nettamente diverso in quanto essi non sono inseribili o eliminabili direttamente da un
utente, ma vengono generati e distrutti quando un viewController viene inserito come componente di una
scena o rimosso da una scena in cui era stato incluso. Gli oggetti contenuti in questo DataStore, oltre alle
proprietà comuni a tutte le istanze di UiPhoneControl, possiedono le seguenti proprietà particolari
Proprietà Descrizione Indicizzata
linkId Identificativo univoco si
containedViewControllerId Identificativo del viewController contenuto in questo container si
sceneId Identificativo della scena che possiede il parentViewController in cui questo container è inserito
si
type Contiene il tipo di device a cui il parentViewController in cui questo container è inserito
Tabella 4.26 Proprietà aggiuntive di controlli Container
Oltre agli attributi specificati in questa tabella, solo il campo ereditato id è indicizzato per questa classe di
componenti, nessun altro campo è indicizzato. La descrizione di questo tipo di controlli consente
l’esportazione della relazione childViewControllers di scene nel modello di riferimento (Scene).
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
LINK_VIEWCONTROLLER: quando un viewController viene inserito come figlio in una scena, questa
azione viene inviata al modello dei dati. In questo DataStore vengono generati due nuovi container
per la coppia scena – viewController, uno per ciascun parentViewController della scena
UNLINK_VIEWCONTROLLER: quando un viewController viene rimosso dai figli di una scena, i
container relativi alla coppia scena – viewController vengono eliminati da questo DataStore
DELETE_VIEWCONTROLLER: elimina tutti i container associati al viewController eliminato, il cui id è
contenuto sia nell’azione che nel campo containedViewControllerId degli elementi da eliminare da
questo DataStore
DELETE_SCENE: elimina tutti i container associati a parentViewController della scena eliminata, il
cui id è contenuto sia nell’azione che nel campo sceneId degli elementi da eliminare da questo
DataStore
EDIT_CONTAINER: permette la modifica delle proprietà di un container, dato il suo id e l’elenco
delle modifiche da applicare (entrambi letti dall’azione)
sourceTypes
Questa chiave contiene un DataStore dedicato a memorizzare oggetti di supporto alla descrizione di
controlli di tipo AudioPlayer, ImageView e VideoView, ovvero tutti quei controlli dediti alla riproduzione di
elementi multimediali. Gli oggetti di questo DataStore modellano una generica sorgente multimediale. Gli
attributi di questi oggetti nel modello di riferimento sono allocati agli stessi controlli utente. Gli oggetti
contenuti in questo DataStore possiedono le seguenti proprietà
74
Proprietà Descrizione Indicizzata
id Identificativo univoco del controllo a cui questo oggetto è associato si
viewControllerId Identificativo univoco del viewController a cui appartiene il controllo associato a questo oggetto tramite l’attributo id
type Tipologia della sorgente: hardware file, resourceFile o remoteFile
fileUri Il percorso della sorgente, valido solo per resourceFile e remoteFile Tabella 4.27 Proprietà di oggetti di tipo SourceType
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_UI_PHONE_CONTROL: crea un nuovo oggetto in questo DataStore quando il controllo
creato con l’azione è di uno dei tipi che possiedono un elemento multimediale
DELETE_UI_PHONE_CONTROL: elimina da questo DataStore l’oggetto associato al controllo
eliminato con questa azione, se presente
EDIT_SOURCE_TYPE: modifica un oggetto di questo DataStore, dati il suo id e le modifiche da
applicare (lette dall’azione)
DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli elementi associati al
viewController eliminato, il cui id è contenuto sia nell’azione che nel campo viewControllerId degli
oggetti da eliminare da questo DataStore
gridViewCells
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe GridViewCell (descritta in GridView e GridViewCell) del modello di riferimento. Gli oggetti contenuti
in questo DataStore possiedono le seguenti proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
name Nome distintivo della cella
title Il testo mostrato nella cella se la griglia è di tipo simple o detailed
gridViewId Identificativo del controllo di tipo gridView a cui la cella appartiene si
viewControllerId Identificativo del viewController a cui appartiene il controllo di cui alla proprietà gridViewId
si
Tabella 4.28 Proprietà di oggetti di tipo GridViewCell
Il campo title corrisponde all’omonimo campo del modello di riferimento. Il campo id è necessario in
Protocode per stabilire relazioni, ma non viene incluso nel modello. Il campo name è necessario per dare
all’utente un modo facile ed efficace di distinguere diverse istanze di questo oggetto, ma non viene
esportato nel modello. Il campo gridViewId consente di esportare la relazione cells degli oggetti di classe
GridView nel modello di riferimento.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_GRID_VIEW_CELL: crea un nuovo elemento in questo DataStore. I campi sono inizializzati
a partire da valori contenuti nell’azione stessa.
EDIT_GRID_VIEW_CELL: modifica un elemento di questo DataStore, dati il suo id e le modifiche da
applicare (entrambi contenuti nell’azione stessa)
DELETE_GRID_VIEW_CELL: elimina un elemento da questo DataStore, dato il suo id (letto
dall’azione stessa)
75
DELETE_UI_PHONE_CONTROL: elimina tutti gli elementi associati al controllo che è stato eliminato
con questa azione, se presenti. Gli elementi da eliminare hanno l’attributo gridViewId impostato
allo stesso valore dell’id del controllo eliminato
DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli elementi associati al
viewController eliminato (ovvero associati a controlli contenuti nel viewController eliminato), il cui
id è contenuto sia nell’azione che nel campo viewControllerId degli oggetti da eliminare da questo
DataStore
listViewCells
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe ListViewCell del modello di riferimento (descritta in ListView e ListViewCell). Gli oggetti contenuti in
questo DataStore possiedono le seguenti proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
name Nome distintivo della cella
title Titolo della cella
subtitle Sottotitolo della cella (utilizzato solo in liste di tipo detailed)
listViewId Identificativo del controllo di tipo listView a cui l’elemento appartiene si
viewControllerId Identificativo del viewController a cui appartiene il controllo di cui alla proprietà listViewId
si
Tabella 4.29 Proprietà di oggetti di tipo ListViewCell
I campi title e subtitle corrispondono agli omonimi campi del modello di riferimento. Il campo id è
necessario in Protocode per stabilire relazioni, ma non viene incluso nel modello. Il campo name è
necessario per dare all’utente un modo facile ed efficace di distinguere diverse istanze di questo oggetto,
ma non viene esportato nel modello. Il campo listViewId consente di esportare la relazione cells degli
oggetti di classe ListView nel modello di riferimento.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_LIST_VIEW_CELL: crea un nuovo elemento in questo DataStore. I campi sono inizializzati a
partire da valori contenuti nell’azione stessa.
EDIT_LIST_VIEW_CELL: modifica un elemento di questo DataStore, dati il suo id e le modifiche da
applicare (entrambi contenuti nell’azione stessa)
DELETE_LIST_VIEW_CELL: elimina un elemento da questo DataStore, dato il suo id (letto dall’azione
stessa)
DELETE_UI_PHONE_CONTROL: elimina tutti gli elementi associati al controllo che è stato eliminato
con questa azione, se presenti. Gli elementi da eliminare hanno l’attributo listViewId impostato allo
stesso valore dell’id del controllo eliminato
DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli elementi associati al
viewController eliminato (ovvero associati a controlli contenuti nel viewController eliminato), il cui
id è contenuto sia nell’azione che nel campo viewControllerId degli oggetti da eliminare da questo
DataStore
constraints
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe PositionConstraint del modello di riferimento. Il modello di vincoli supportato da Protocode è
76
esattamente quello definito nel modello di riferimento, in modo da garantire omogeneità a livello di
capacità espressiva. Gli oggetti contenuti in questo DataStore possiedono le seguenti proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
viewControllerId Identificativo univoco del viewController a cui appartiene il controllo proprietario del vincolo
si
name Nome distintivo del vincolo per essere riconosciuto dall’utente
targetId Identificativo univoco del controllo proprietario del vincolo si
side L’elemento notevole del controllo proprietario a cui è applicato il vincolo
refId Identificativo univoco del controllo di appoggio del vincolo, oppure null se il vincolo si appoggia al padre
refSide L’elemento notevole del controllo di appoggio coinvolto nel vincolo
valid Lo stato di validità del vincolo
valid se il vincolo è valido
invalid_incomplete se qualche parametro del vincolo non è ben definito
invalid_loop se il vincolo è coinvolto in un riferimento circolare
Tabella 4.30 Proprietà di oggetti di tipo Constraint
Questa definizione è facilmente utilizzabile per ricavare un oggetto compatibile con la definizione di
Constraint data nel modello di riferimento. In particolare, se l’attributo valid denota una condizione di non
validità del vincolo, il vincolo non viene esportato nel modello di riferimento. Altrimenti:
la proprietà targetId viene utilizzata per capire a quale controllo il vincolo appartiene
se la proprietà targetId ha valore null, si imposta il flag withParent definito nel modello di
riferimento a true, altrimenti a false
la proprietà referenceElement del modello di riferimento è inizializzata a partire da refId
le proprietà referenceLayoutEdge e layoutEdge sono inizializzate rispettivamente ai valori di refSide
e side
la proprietà name è utilizzata solo per identificare il vincolo all’utente, e non viene esportata
la proprietà id viene mappata sulla proprietà omonima
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_CONSTRAINT: crea un nuovo elemento in questo DataStore, inizializzando le proprietà id,
targetId e viewControllerId con valori contenuti nell’azione, tutte le altre proprietà a valori nulli
tranne valid che viene inizializzata a invalid_incomplete
EDIT_COSTRAINT: modifica un elemento in questo DataStore, noti il suo id e le modifiche da
applicare (entrambi contenuti nell’azione). Ad ogni modifica viene ripetuto il controllo di
completezza per il vincolo modificato e il controllo di ciclicità sull’intero grafo dei vincoli, e lo stato
di validità del controllo modificato viene impostato conseguentemente.
DELETE_CONSTRAINT: elimina un elemento di questo DataStore, noto il suo id (contenuto
nell’azione)
DELETE_UI_PHONE_CONTROL: elimina tutti gli elementi di questo DataStore associati al controllo
utente eliminato, il cui id è contenuto sia nell’azione che nell’attributo refId o targetId degli
elementi da rimuovere
77
DELETE_VIEW_CONTROLLER: elimina tutti gli elementi di questo DataStore associati al
viewController eliminato, il cui id è contenuto sia nell’azione che nell’attributo viewControllerId
degli elementi da rimuovere
alertDialogs
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe AlertDialog del modello di riferimento. Gli oggetti contenuti in questo DataStore possiedono le
seguenti proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
name Nome distintivo per essere identificato dall’utente
title Titolo del messaggio mostrato nella finestra di dialogo
message Contenuto del messaggio mostrato nella finestra di dialogo
viewControllerId Identificativo univoco del viewController in cui la finestra di dialogo è definita
si
Tabella 4.31 Proprietà di oggetti di tipo AlertDialog
Le proprietà id, title e message corrispondono alle omonime del modello di riferimento. La proprietà name
viene utilizzata solo per fornire al modellatore un modo semplice di identificare le diverse istanze di questa
definizione, e non viene quindi esportata nel modello. La proprietà viewControllerId consente di ricostruire
la relazione alertDialogs della classe ViewController del modello di riferimento.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_ALERT_DIALOG: crea un nuovo elemento in questo DataStore. Tutti i campi (tranne id)
sono inizializzati alla stringa vuota.
EDIT_ALERT_DIALOG: modifica un elemento di questo DataStore, dati il suo id e le modifiche da
applicare (entrambi contenuti nell’azione)
DELETE_ALERT_DIALOG: elimina un elemento di questo DataStore, dato il suo id (contenuto
nell’azione)
DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli elementi collegati al
viewController eliminato, il cui id è contenuto sia nell’azione che nell’attributo viewControllerId
delle istanze da eliminare.
progressDialogs
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe ProgressDialog del modello di riferimento. Gli oggetti contenuti in questo DataStore possiedono le
seguenti proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
name Nome distintivo per essere identificato dall’utente
title Titolo del messaggio mostrato nella finestra di dialogo
message Contenuto del messaggio mostrato nella finestra di dialogo
spinner Flag che indica se viene mostrata l’animazione dell’attesa
viewControllerId Identificativo univoco del viewController in cui la finestra di dialogo è definita
si
Tabella 4.32 Proprietà di oggetti di tipo ProgressDialog
78
Le proprietà id, title, message e spinner corrispondono alle omonime del modello di riferimento. La
proprietà name viene utilizzata solo per fornire al modellatore un modo semplice di identificare le diverse
istanze di questa definizione, e non viene quindi esportata nel modello. La proprietà viewControllerId
consente di ricostruire la relazione progressDialogs della classe ViewController del modello di riferimento.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_PROGRESS_DIALOG: crea un nuovo elemento in questo DataStore. Tutti i campi (tranne
id) sono inizializzati alla stringa vuota, il campo spinner è inizializzato al valore logico false.
EDIT_PROGRESS_DIALOG: modifica un elemento di questo DataStore, dati il suo id e le modifiche
da applicare (entrambi contenuti nell’azione)
DELETE_PROGRESS_DIALOG: elimina un elemento di questo DataStore, dato il suo id (contenuto
nell’azione)
DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli elementi collegati al
viewController eliminato, il cui id è contenuto sia nell’azione che nell’attributo viewControllerId
delle istanze da eliminare.
asyncTasks
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe AsyncTask del modello di riferimento. Gli oggetti contenuti in questo DataStore possiedono le
seguenti proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
name Nome distintivo per essere identificato dall’utente
viewControllerId Identificativo univoco del viewController in cui il task asincrono è definito
si
Tabella 4.33 Proprietà di oggetti di tipo AsyncTask
La proprietà name corrisponde all’omonima nel modello di riferimento. La proprietà id è necessaria in
Protocode per esprimere relazioni, ma non appartiene al modello di riferimento e non viene quindi
esportata. La proprietà viewControllerId consente di ricostruire la relazione asyncTasks della classe
ViewController del modello di riferimento.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_ASYNC_TASK: crea un nuovo elemento in questo DataStore. I campi id e viewControllerId
sono valorizzati a partire dall’azione, il campo name contiene un nome basato su un codice
progressivo.
EDIT_ASYNC_TASK: modifica un elemento di questo DataStore, dati il suo id e le modifiche da
applicare (entrambi contenuti nell’azione)
DELETE_PROGRESS_DIALOG: elimina un elemento di questo DataStore, dato il suo id (contenuto
nell’azione)
DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli elementi collegati al
viewController eliminato, il cui id è contenuto sia nell’azione che nell’attributo viewControllerId
delle istanze da eliminare.
79
navigations
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe Navigation del modello di riferimento, fondamentale per la modellazione della navigazione. Gli
oggetti contenuti in questo DataStore possiedono le seguenti proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
fromSceneId Identificativo dell’oggetto scene da cui origina l’azione di navigazione, null in caso di navigazione a partire dal menù
si
fromViewControllerId Identificativo dell’oggetto viewController da cui origina l’attività di navigazione, null in caso di navigazione a partire dal menù
si
fromControlId Identificativo univoco dell’elemento da cui origina la navigazione, sia esso una voce di menù o un controllo utente
si
toSceneId Identificativo univoco della scene di destinazione dell’azione di navigazione
toViewControllerId Identificativo univoco del viewController di destinazione dell’azione di navigazione, null se l’azione di navigazione è diretta ad una scena e non ad un viewController specifico
Tabella 4.34 Proprietà di oggetti di tipo Navigation
La proprietà id corrisponde all’omonima del modello di riferimento. La proprietà fromControlId consente di
ricostruire la relazione navigations dei vari elementi dotati di capacità di navigazione. La proprietà
fromSceneId consente di ricostruire la relazione contextScene, mentre toSceneId e toViewControllerId
consentono di ricostruire le relazioni destinationScene e destinationViewController.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_NAVIGATION: crea un nuovo oggetto in questo DataStore. Tutte le proprietà del nuovo
oggetto sono inizializzate a partire da parametri contenuti nell’azione stessa
EDIT_NAVIGATION: modifica un oggetto in questo DataStore, noti il suo id e le modifiche da
applicare (entrambi contenuti nell’azione stessa)
DELETE_NAVIGATION: elimina un oggetto da questo DataStore, noto il suo id (contenuto
nell’azione stessa)
DELETE_SCENE: elimina da questo DataStore tutti gli oggetti associati alla scena eliminata con
questa azione, il cui identificativo univoco è contenuto sia nell’azione che nel parametro
fromSceneId o toSceneId degli elementi da eliminare
DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli oggetti associati al
viewController eliminato con questa azione, il cui identificativo univoco è contenuto sia nell’azione
che nel parametro fromViewControllerId o toViewControllerId degli elementi da eliminare
DELETE_UI_PHONE_CONTROL: elimina da questo DataStore tutti gli oggetti associati al controllo
utente eliminato con questa azione, il cui identificativo univoco è contenuto sia nell’azione che nel
parametro fromControlId degli elementi da eliminare
80
Design dei dati riguardanti il controller
adapterBindings
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe AdapterBinding del modello di riferimento (§3.3.1). Gli oggetti contenuti in questo DataStore
possiedono le seguenti proprietà
Proprietà Descrizione Indicizzata
id Identificativo univoco si
sceneId Identificativo univoco dell’oggetto scene a cui appartiene logicamente l’istanza di adapterBinding
si
name Nome distintivo per l’utente di Protocode
adapterClass La strategia di persistenza a cui l’elemento del model associato a questo oggetto appartiene
isList Indica se questo collegamento punta ad un singolo elemento o ad una lista di elementi
fileId L’identificativo univoco dell’oggetto fileStorageRecord associato a questo adapterBinding, se l’adapterClass è file; null altrimenti
entityId L’identificativo univoco dell’oggetto entity associato a questo adapterBinding, se l’adapterClass è entity; null altrimenti
preferenceId L’identificativo univoco dell’oggetto preferenceRecord associato a questo adapterBinding, se l’adapterClass è preference; null altrimenti
cloudObjectId L’identificativo univoco dell’oggetto cloudObject associato a questo adapterBinding, se l’adapterClass è cloud; null altrimenti
cloudRefPath Significativo solo se adapterClass è cloud, indica il path dell’oggetto puntato nel JSON tree del database
Tabella 4.35 Proprietà di oggetti di tipo AdapterBinding
Gli attributi id, isList, name e cloudRefPath corrispondono alle omonime proprietà definite nel modello di
riferimento. Gli attributi fileId, entityId, preferenceId e cloudObjectId traducono rispettivamente le relazioni
di file, entity, preference e object del modello di riferimento. L’attributo adapterClass corrisponde
all’attributo type nel modello di riferimento, mentre sceneId viene utilizzato per ricostruire la relazione
adapterBindings definita su Scene (§3.3.1).
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_ADAPTER_BINDING: crea un nuovo oggetto in questo DataStore. Tutti gli attributi del
nuovo oggetto sono inizializzati con dati ottenuti dall’azione stessa
DELETE_ADAPTER_BINDING: elimina un oggetto da questo DataStore, dato il suo id (contenuto
nell’azione)
DELETE_SCENE: elimina da questo DataStore tutti gli oggetti associati alla scena eliminata, il cui
identificativo univoco è contenuto nell’azione e nell’attributo sceneId degli elementi da eliminare
modelConnectors
Questa chiave contiene un DataStore dedicato a memorizzare oggetti che corrispondono ad istanze della
classe ModelConnector del modello di riferimento (§3.3.2). Gli oggetti contenuti in questo DataStore
possiedono le seguenti proprietà
81
Proprietà Descrizione Indicizzata
id Identificativo univoco si
sceneId Identificativo univoco dell’oggetto scene per cui viene definita l’istanza di modelConnector
si
viewControllerId Identificativo univoco dell’oggetto viewController a cui appartiene il controllo utente a cui l’istanza corrente di modelConnector appartiene logicamente
si
controlId Identificativo univoco dell’oggetto uiPhoneControl a cui appartiene l’istanza corrente di modelConnector
si
adapterId Identificativo univoco dell’oggetto adapterBinding da cui l’istanza corrente di modelConnector riceve i dati
si
keypath La proprietà dell’elemento associato all’adapter che va mappata sulla proprietà del controllo. È valido solo per quegli adapter che connettono un oggetto (un’entità relazionale o un oggetto cloud), altrimenti il suo valore è la stringa “.”.
property La proprietà del controllo connessa al model tramite questo connettore Tabella 4.36 Proprietà di oggetti di tipo ModelConnector
Le proprietà id, property e keypath corrispondono agli omonimi attributi definiti nel modello di riferimento,
mentre la relazione adapter del modello di riferimento è normalizzata utilizzando l’attributo adapterId, e la
relazione modelConnectors di UiPhoneControl (§3.3) è normalizzata con l’attributo controlId. Tutti gli altri
attributi sono necessari per l’implementazione delle azioni di cancellazione ricorsiva.
Il reducer di competenza per questo segmento dello stato reagisce alle seguenti tipologie di azioni
CREATE_MODEL_CONNECTOR: crea un nuovo oggetto in questo DataStore. Tutti gli attributi del
nuovo oggetto sono inizializzati con dati contenuti nell’azione
DELETE_MODEL_CONNECTOR: elimina un oggetto da questo DataStore, dato il suo id (contenuto
nell’azione)
DELETE_UI_PHONE_CONTROL: elimina da questo DataStore tutti gli oggetti associati con il
controllo utente eliminato con l’azione, il cui identificativo è contenuto sia nell’azione stessa che
nell’attributo controlId degli elementi da eliminare
DELETE_SCENE: elimina da questo DataStore tutti gli oggetti associati con la scene eliminata con
l’azione, il cui identificativo è contenuto sia nell’azione stessa che nell’attributo sceneId degli
elementi da eliminare
DELETE_VIEW_CONTROLLER: elimina da questo DataStore tutti gli oggetti associati con il
viewController eliminato con l’azione, il cui identificativo è contenuto sia nell’azione stessa che
nell’attributo viewControllerId degli elementi da eliminare
DELETE_ADAPTER_BINDING: elimina da questo DataStore tutti gli oggetti associati con l’istanza di
adapterBinding eliminata con l’azione, il cui identificativo è contenuto sia nell’azione stessa che
nell’attributo adapterId degli elementi da eliminare
82
4.3.3 Connessione tra dati e vista
L’evoluzione dello stato nel contesto di software basati su Redux avviene per mezzo di azioni processate dai
reducer, che computano lo stato successivo a partire dall’azione e dallo stato precedente. Al fine di
collegare l’evoluzione dello stato con le azioni dell’utente, è necessario che le azioni siano inviate in
risposta ad un evento, ovvero ad un’interazione tra utente e applicazione. Questo è possibile grazie ai
componenti di connessione tra stato e vista creati tramite react-redux, che espongono al proprio
sottoalbero funzioni (passate come proprietà) che consentono l’invio di azioni allo stato. I componenti della
vista possono quindi ascoltare gli eventi provenienti dall’utente e utilizzare tali funzioni come risposta ad
essi.
In questo capitolo verranno presentate le diverse tipologie di azioni a cui lo stato di Protocode reagisce, il
loro significato, la loro struttura e i componenti che necessitano della possibilità di utilizzarle.
Dal punto di vista strutturale, un’azione è un oggetto JavaScript puro (senza particolari prototipi se non
Object) con un attributo obbligatorio: l’attributo type che contiene il tipo dell’azione. È possibile definire
poi in modo totalmente arbitrario la struttura dell’oggetto, mantenendo questi due vincoli. Nei capitoli
seguenti, tutti gli attributi definiti sulle azioni sono da considerare obbligatori, a meno che non siano
contrassegnati da un asterisco.
Azioni riguardanti il model
EnablePreferences
Questa azione indica la volontà dell’utente di abilitare la persistenza tramite SharedPreferences
nell’applicazione che sta modellando.
Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto
semplice.
L’unico componente che utilizza questa azione è DefaultPreferencesEditor
EnableFileStorage
Questa azione indica la volontà dell’utente di abilitare la persistenza tramite files nell’applicazione che sta
modellando.
Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto
semplice.
L’unico componente che utilizza questa azione è FileStorageEditor
EnableDatabase
Questa azione indica la volontà dell’utente di abilitare la persistenza tramite database relazionale
nell’applicazione che sta modellando.
Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto
semplice.
L’unico componente che utilizza questa azione è Relational database editor
83
EnableCloudDatabase
Questa azione indica la volontà dell’utente di abilitare la persistenza tramite database cloud
nell’applicazione che sta modellando.
Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto
semplice.
L’unico componente che utilizza questa azione è Cloud database editor
DisablePreferences
Questa azione indica la volontà dell’utente di disabilitare la persistenza tramite SharedPreferences
nell’applicazione che sta modellando.
Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto
semplice.
L’unico componente che utilizza questa azione è DefaultPreferencesEditor
DisableFileStorage
Questa azione indica la volontà dell’utente di disabilitare la persistenza tramite files nell’applicazione che
sta modellando.
Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto
semplice.
L’unico componente che utilizza questa azione è FileStorageEditor
DisableDatabase
Questa azione indica la volontà dell’utente di disabilitare la persistenza tramite database relazionale
nell’applicazione che sta modellando.
Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto
semplice.
L’unico componente che utilizza questa azione è Relational database editor
DisableCloudDatabase
Questa azione indica la volontà dell’utente di disabilitare la persistenza tramite database cloud
nell’applicazione che sta modellando.
Questa azione non supporta altre proprietà oltre a type, in quanto il concetto che esprime è molto
semplice.
L’unico componente che utilizza questa azione è Cloud database editor
CreatePreferenceRecord
Questa azione indica la volontà dell’utente di creare una nuova chiave nel dizionario delle
SharedPreferences.
Questa azione supporta i seguenti attributi particolari
id L’identificativo univoco che sarà associato al nuovo record
key La chiave da associare al nuovo record
value Il valore predefinito del nuovo record
type Il tipo del valore contenuto nel nuovo record Tabella 4.37 Payload dell'azione CreatePreferenceRecord
84
L’unico componente che utilizza questa azione è DefaultPreferences Editor
DeletePreferenceRecord
Questa azione indica la volontà dell’utente di eliminare una chiave dal dizionario delle SharedPreferences.
Questa azione supporta i seguenti attributi particolari
recordId L’identificativo univoco dell’elemento da eliminare Tabella 4.38 Payload dell'azione DeletePreferenceRecord
L’unico componente che utilizza questa azione è DefaultPreferences Editor
CreateFileStorageRecord
Questa azione indica la volontà dell’utente di creare una definizione di un nuovo file nel filesystem
dell’applicazione.
Questa azione supporta i seguenti attributi particolari
id L’identificativo univoco che sarà associato al nuovo file
name Il nome del file
extension L’estensione da associare al nome del file
isTemp Marcatore che indica se il file è temporaneo Tabella 4.39 Payload dell'azione CreateFileStorageRecord
L’unico componente che utilizza questa azione è FileStorage Editor
DeleteFileStorageRecord
Questa azione indica la volontà dell’utente di eliminare la definizione di un file nel filesystem
dell’applicazione.
Questa azione supporta i seguenti attributi particolari
recordId L’identificativo univoco dell’elemento da eliminare Tabella 4.40 Payload dell'azione DeleteFileStorageRecord
L’unico componente che utilizza questa azione è FileStorage Editor
CreateEntity
Questa azione indica la volontà dell’utente di creare una definizione di una nuova entità nel database
relazionale
Questa azione supporta i seguenti attributi particolari
id L’identificativo univoco che sarà associato alla nuova entità
name Il nome distintivo della nuova entità
primaryKey Il nome dell’attributo che espleta il ruolo di chiave primaria Tabella 4.41 Payload dell'azione CreateEntity
L’unico componente che utilizza questa azione è RelationalDatabase Editor
EditEntity
Questa azione indica la volontà dell’utente di modificare la definizione di una entità nel database
relazionale
85
Questa azione supporta i seguenti attributi particolari
entityId L’identificativo univoco dell’entità da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo Entity
Tabella 4.42 Payload dell'azione EditEntity
L’unico componente che utilizza questa azione è Entity Editor
DeleteEntity
Questa azione indica la volontà dell’utente di eliminare la definizione di una entità nel database relazionale
Questa azione supporta i seguenti attributi particolari
entityId L’identificativo univoco dell’entità da eliminare Tabella 4.43 Payload dell'azione DeleteEntity
L’unico componente che utilizza questa azione è Entity Editor
CreateEntityAttribute
Questa azione indica la volontà dell’utente di creare una definizione di un nuovo attributo per un’entità nel
database relazionale
Questa azione supporta i seguenti attributi particolari
attributeId L’identificativo univoco che sarà associato al nuovo attributo
entityId L’identificativo univoco dell’entità a cui associare l’attributo
attributeName Il nome distintivo del nuovo attributo
attributeType Il tipo dei dati contenuti nell’attributo Tabella 4.44 Payload dell'azione CreateEntityAttribute
L’unico componente che utilizza questa azione è Entity Editor
DeleteEntityAttribute
Questa azione indica la volontà dell’utente di eliminare la definizione di un attributo di un’entità nel
database relazionale
Questa azione supporta i seguenti attributi particolari
attributeId L’identificativo univoco dell’attributo da eliminare Tabella 4.45 Payload dell'azione DeleteEntityAttribute
L’unico componente che utilizza questa azione è Entity Editor
CreateEntityRelation
Questa azione indica la volontà dell’utente di creare una definizione di una nuova relazione tra due entità
nel database relazionale
Questa azione supporta i seguenti attributi particolari
relationId L’identificativo univoco che sarà associato alla nuova relazione
fromEntityId L’identificativo univoco dell’entità da cui origina la relazione
toEntityId L’identificativo univoco dell’entità di destinazione della relazione
relationName Il nome distintivo della nuova relazione
relationCardinality La cardinalità della nuova relazione Tabella 4.46 Payload dell'azione CreateEntityRelation
86
L’unico componente che utilizza questa azione è Entity Editor
DeleteEntityRelation
Questa azione indica la volontà dell’utente di eliminare la definizione di una relazione tra due entità nel
database relazionale
Questa azione supporta i seguenti attributi particolari
relationId L’identificativo univoco della relazione da eliminare Tabella 4.47 Payload dell'azione DeleteEntityRelation
L’unico componente che utilizza questa azione è Entity Editor
CreateCloudObject
Questa azione indica la volontà dell’utente di creare una definizione di un nuovo oggetto da inserire nel
database cloud
Questa azione supporta i seguenti attributi particolari
objectId L’identificativo univoco che sarà associato alla nuova tipologia di oggetti
objectName Il nome distintivo della nuova tipologia di oggetti Tabella 4.48 Payload dell'azione CreateCloudObject
L’unico componente che utilizza questa azione è CloudDatabase Editor
EditCloudObject
Questa azione indica la volontà dell’utente di modificare la definizione di una tipologia di oggetti nel
database relazionale
Questa azione supporta i seguenti attributi particolari
objectId L’identificativo univoco dell’oggetto da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo CloudObject
Tabella 4.49 Payload dell'azione EditCloudObject
L’unico componente che utilizza questa azione è CloudObject Editor
DeleteCloudObject
Questa azione indica la volontà dell’utente di eliminare la definizione di una tipologia di oggetti dal
database relazionale
Questa azione supporta i seguenti attributi particolari
objectId L’identificativo univoco dell’oggetto da eliminare Tabella 4.50 Payload dell'azione DeleteCloudObject
L’unico componente che utilizza questa azione è CloudObject Editor
CreateCloudObjectAttribute
Questa azione indica la volontà dell’utente di creare una definizione di un nuovo attributo per un’entità nel
database relazionale
87
Questa azione supporta i seguenti attributi particolari
attributeId L’identificativo univoco che sarà associato al nuovo attributo
objectId L’identificativo univoco della tipologia di oggetti a cui associare l’attributo
attributeName Il nome distintivo del nuovo attributo
attributeType Il tipo dei dati contenuti nell’attributo
attributeObject L’identificativo univoco della tipologia di oggetti contenuta nell’attributo, se la tipologia è ref o ref_list
Tabella 4.51 Payload dell'azione CreateCloudObjectAttribute
L’unico componente che utilizza questa azione è CloudObject Editor
DeleteCloudObjectAttribute
Questa azione indica la volontà dell’utente di eliminare la definizione di un attributo da una tipologia di
oggetti nel database relazionale
Questa azione supporta i seguenti attributi particolari
attributeId L’identificativo univoco dell’attributo da eliminare Tabella 4.52 Payload dell'azione DeleteCloudObjectAttribute
L’unico componente che utilizza questa azione è CloudObject Editor
Azioni riguardanti la view
CreateScene
Questa azione indica la volontà dell’utente di creare una nuova scena
Questa azione supporta i seguenti attributi particolari
sceneId L’identificativo univoco che sarà associato alla nuova scena Tabella 4.53 Payload dell'azione CreateScene
L’unico componente che utilizza questa azione è Smartphone & Tablet Editor
EditScene
Questa azione indica la volontà dell’utente di modificare gli attributi di una scena
Questa azione supporta i seguenti attributi particolari
sceneId L’identificativo univoco della scena da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo Scene
Tabella 4.54 Payload dell'azione EditScene
L’unico componente che utilizza questa azione è Scene Inspector
DeleteScene
Questa azione indica la volontà dell’utente di eliminare una scena
Questa azione supporta i seguenti attributi particolari
sceneId L’identificativo univoco della scena da eliminare Tabella 4.55 Payload dell'azione DeleteScene
88
L’unico componente che utilizza questa azione è Scene Inspector
LinkViewController
Questa azione indica la volontà dell’utente di utilizzare un viewController nella formazione di una scena
Questa azione supporta i seguenti attributi particolari
linkId L’identificativo univoco che sarà associato alla connessione tra scena e viewController
sceneId Identificativo univoco della scena coinvolta nell’operazione
viewControllerId Identificativo univoco del viewController coinvolto nell’operazione Tabella 4.56 Payload dell'azione LinkViewController
L’unico componente che utilizza questa azione è Scene Inspector
UnlinkViewController
Questa azione indica la volontà dell’utente di smettere di utilizzare un viewController nella formazione di
una scena
Questa azione supporta i seguenti attributi particolari
linkid L’identificativo univoco della connessione tra scena e viewController che deve essere interrotta Tabella 4.57 Payload dell'azione UnlinkViewController
L’unico componente che utilizza questa azione è Scene Inspector
EditContainer
Questa azione indica la volontà dell’utente di modificare gli attributi di una oggetto di tipo container
Questa azione supporta i seguenti attributi particolari
containerId L’identificativo univoco del container da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo Container
Tabella 4.58 Payload dell'azione EditContainer
L’unico componente che utilizza questa azione è Container Inspector
CreateViewController
Questa azione indica la volontà dell’utente di creare un nuovo viewController
Questa azione supporta i seguenti attributi particolari
vcId L’identificativo univoco che sarà associato al nuovo viewController Tabella 4.59 Payload dell'azione CreateViewController
L’unico componente che utilizza questa azione è Smartphone & Tablet Editor
EditViewController
Questa azione indica la volontà dell’utente di modificare gli attributi di un viewController
89
Questa azione supporta i seguenti attributi particolari
vcId L’identificativo univoco del viewController da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo ViewController
Tabella 4.60 Payload dell'azione EditViewController
L’unico componente che utilizza questa azione è ViewController Inspector
DeleteViewController
Questa azione indica la volontà dell’utente di eliminare un viewController
Questa azione supporta i seguenti attributi particolari
vcId L’identificativo univoco del viewController da eliminare Tabella 4.61 Payload dell'azione DeleteViewController
L’unico componente che utilizza questa azione è ViewController Inspector
CreateUiPhoneControl
Questa azione indica la volontà dell’utente di creare un nuovo controllo utente
Questa azione supporta i seguenti attributi particolari
controlId L’identificativo univoco che sarà associato al nuovo controllo utente
uiPhoneControlType La tipologia di controllo
viewController L’identificativo univoco del viewController in cui il controllo utente è inserito
controlChainId* L’identificativo univoco della control chain in cui il controllo utente è inserito
controlChainPosition* La posizione relativa dell’elemento nella catena Tabella 4.62 Payload dell'azione CreateUiPhoneControl
Componenti che utilizzano questa azione
Scene Editor
ViewController Editor
ControlChain Inspector
EditUiPhoneControl
Questa azione indica la volontà dell’utente di modificare gli attributi di un controllo utente
Questa azione supporta i seguenti attributi particolari
controlId L’identificativo univoco del viewController da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo UiPhoneControl Tabella 4.63 Payload dell'azione EditUiPhoneControl
Componenti che utilizzano questa azione
AudioPlayer Inspector
AudioRecorder Inspector
90
Button Inspector
Card Inspector
DatePicker Inspector
GridView Inspector
ImageView Inspector
Label Inspector
Map Inspector
ListView Inspector
PhotocameraController Inspector
Slider Inspector
Spinner Inspector
Switch Inspector
TextEdit Inspector
TimePicker Inspector
VideoCamera Controller Inspector
VideoView Inspector
WebView Inspector
DeleteUiPhoneControl
Questa azione indica la volontà dell’utente di eliminare un controllo utente
Questa azione supporta i seguenti attributi particolari
controlId L’identificativo univoco del controllo utente da eliminare Tabella 4.64 Payload dell'azione DeleteUiPhoneControl
Componenti che utilizzano questa azione
AudioPlayer Inspector
AudioRecorder Inspector
Button Inspector
Card Inspector
DatePicker Inspector
GridView Inspector
ImageView Inspector
Label Inspector
Map Inspector
ListView Inspector
PhotocameraController Inspector
Slider Inspector
Spinner Inspector
Switch Inspector
TextEdit Inspector
TimePicker Inspector
VideoCamera Controller Inspector
VideoView Inspector
WebView Inspector
91
CreateControlChain
Questa azione indica la volontà dell’utente di creare una nuova control chain
Questa azione supporta i seguenti attributi particolari
chainid L’identificativo univoco che sarà associato alla nuova control chain
viewControllerId L’identificativo univoco del viewController in cui la control chain è inserita Tabella 4.65 Payload dell'azione CreateControlChain
L’unico componente che utilizza questa azione è ViewController Editor
EditControlChain
Questa azione indica la volontà dell’utente di modificare gli attributi di una control chain
Questa azione supporta i seguenti attributi particolari
chainId L’identificativo univoco della control chain da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo ControlChain
Tabella 4.66 Payload dell'azione EditControlChain
L’unico componente che utilizza questa azione è ControlChain Inspector
DeleteControlChain
Questa azione indica la volontà dell’utente di eliminare una control chain
Questa azione supporta i seguenti attributi particolari
chainId L’identificativo univoco della control chain da eliminare Tabella 4.67 Payload dell'azione DeleteControlChain
L’unico componente che utilizza questa azione è ControlChain Inspector
CreateConstraint
Questa azione indica la volontà dell’utente di definire un vincolo di posizione applicato ad un componente
Questa azione supporta i seguenti attributi particolari
constraintId L’identificativo univoco che sarà associato al nuovo constraint
viewControllerId L’identificativo univoco del viewController in cui la control chain è inserita
targetId L’identificativo univoco del controllo utente che possiede il vincolo Tabella 4.68 Payload dell'azione CreateConstraint
Componenti che utilizzano questa azione
AudioPlayer Inspector
AudioRecorder Inspector
Button Inspector
DatePicker Inspector
GridView Inspector
ImageView Inspector
Label Inspector
92
Map Inspector
ListView Inspector
PhotocameraController Inspector
Slider Inspector
Spinner Inspector
Switch Inspector
TextEdit Inspector
TimePicker Inspector
VideoCamera Controller Inspector
VideoView Inspector
WebView Inspector
EditConstraint
Questa azione indica la volontà dell’utente di modificare gli attributi di un constraint definito su un
controllo utente
Questa azione supporta i seguenti attributi particolari
constraintId L’identificativo univoco del constraint da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo Constraint, ad eccezione di valid
Tabella 4.69 Payload dell'azione EditConstraint
L’unico componente che utilizza questa azione è Constraint Inspector
DeleteConstraint
Questa azione indica la volontà dell’utente di eliminare un vincolo di posizione applicato ad un controllo
utente
Questa azione supporta i seguenti attributi particolari
constraintId L’identificativo univoco del vincolo da eliminare Tabella 4.70 Payload dell'azione DeleteConstraint
L’unico componente che utilizza questa azione è Constraint Inspector
CreateMenuItem
Questa azione indica la volontà dell’utente di definire un inserire una nuova voce di menu
Questa azione supporta i seguenti attributi particolari
menuItemId L’identificativo univoco che sarà associato al nuovo elemento del menu
title Il titolo dell’elemento di menu Tabella 4.71 Payload dell'azione CreateMenuItem
L’unico componente che utilizza questa azione è Menu Inspector
EditMenuItem
Questa azione indica la volontà dell’utente di modificare gli attributi di una voce di menu
93
Questa azione supporta i seguenti attributi particolari
menuItemId L’identificativo univoco della voce di menu da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo MenuItem
Tabella 4.72 Payload dell'azione EditMenuItem
L’unico componente che utilizza questa azione è MenuItem Inspector
DeleteMenuItem
Questa azione indica la volontà dell’utente di eliminare una voce di menu
Questa azione supporta i seguenti attributi particolari
menuItemId L’identificativo univoco della voce di menu da eliminare Tabella 4.73 Payload dell'azione DeleteMenuItem
L’unico componente che utilizza questa azione è MenuItem Inspector
CreateGridViewCell
Questa azione indica la volontà dell’utente di definire un inserire una nuova cella in un controllo utente di
tipo griglia
Questa azione supporta i seguenti attributi particolari
cellId L’identificativo univoco che sarà associato alla nuova cella
name Il nome distintivo della cella
gridViewId L’identificativo univoco del controllo utente a cui appartiene la cella
viewControllerId L’identificativo univoco del viewController a cui appartiene il controllo utente che contiene la cella
Tabella 4.74 Payload dell'azione CreateGridViewCell
L’unico componente che utilizza questa azione è GridView Inspector
EditGridViewCell
Questa azione indica la volontà dell’utente di modificare gli attributi di una cella di una griglia
Questa azione supporta i seguenti attributi particolari
cellId L’identificativo univoco della cella da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo GridViewCell
Tabella 4.75 Payload dell'azione EditGridViewCell
L’unico componente che utilizza questa azione è GridViewCell Inspector
DeleteGridViewCell
Questa azione indica la volontà dell’utente di eliminare una cella di una griglia
Questa azione supporta i seguenti attributi particolari
cellId L’identificativo univoco della cella da eliminare Tabella 4.76 Payload dell'azione DeleteGridViewCell
94
L’unico componente che utilizza questa azione è GridViewCell Inspector
CreateListViewCell
Questa azione indica la volontà dell’utente di definire un inserire una nuova cella in un controllo utente di
tipo lista
Questa azione supporta i seguenti attributi particolari
cellId L’identificativo univoco che sarà associato alla nuova cella
name Il nome distintivo della cella
gridViewId L’identificativo univoco del controllo utente a cui appartiene la cella
viewControllerId L’identificativo univoco del viewController a cui appartiene il controllo utente che contiene la cella
Tabella 4.77 Payload dell'azione CreateListViewCell
L’unico componente che utilizza questa azione è ListView Inspector
EditListViewCell
Questa azione indica la volontà dell’utente di modificare gli attributi di una cella di una lista
Questa azione supporta i seguenti attributi particolari
cellId L’identificativo univoco della cella da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo ListViewCell
Tabella 4.78 Payload dell'azione EditListViewCell
L’unico componente che utilizza questa azione è ListViewCell Inspector
DeleteListViewCell
Questa azione indica la volontà dell’utente di eliminare una cella di una lista
Questa azione supporta i seguenti attributi particolari
cellId L’identificativo univoco della cella da eliminare Tabella 4.79 Payload dell'azione DeleteListViewCell
L’unico componente che utilizza questa azione è ListViewCell Inspector
CreateNavigation
Questa azione indica la volontà dell’utente di definire un’azione di navigazione per un controllo utente nel
contesto di una scena
Questa azione supporta i seguenti attributi particolari
navigationId L’identificativo univoco che sarà associato all’azione di navigazione
fromSceneId L’identificativo univoco della scena da cui origina la navigazione, se esiste
fromViewControllerId L’identificativo univoco del viewController da cui origina la navigazione, se esiste
fromControlId L’identificativo univoco del controllo utente o della voce di menu a cui è associata l’azione di navigazione
toSceneId L’identificativo univoco della scena di destinazione dell’azione di navigazione
toViewControllerId L’identificativo univoco del viewController di destinazione dell’azione di navigazione, se esiste
Tabella 4.80 Payload dell'azione CreateNavigation
95
Componenti che utilizzano questa azione
Button Inspector
GridView Inspector
ListView Inspector
MenuItem Inspector
EditNavigation
Questa azione indica la volontà dell’utente di modificare gli attributi di un’azione di navigazione definita su
un controllo utente nel contesto di una scena
Questa azione supporta i seguenti attributi particolari
navigationId L’identificativo univoco dell’azione di navigazione da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo Navigation
Tabella 4.81 Payload dell'azione EditNavigation
Componenti che utilizzano questa azione
Button Inspector
GridView Inspector
ListView Inspector
MenuItem Inspector
DeleteNavigation
Questa azione indica la volontà dell’utente di eliminare un’azione di navigazione associata ad un controllo
utente nel contesto di una scena
Questa azione supporta i seguenti attributi particolari
navigationId L’identificativo univoco dell’azione di navigazione da eliminare Tabella 4.82 Payload dell'azione DeleteNavigation
Componenti che utilizzano questa azione
Button Inspector
GridView Inspector
ListView Inspector
MenuItem Inspector
EditSourceType
Questa azione indica la volontà dell’utente di modificare gli attributi di una sorgente multimediale associata
ad un controllo utente
Questa azione supporta i seguenti attributi particolari
navigationId L’identificativo univoco dell’azione di navigazione da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo SourceType
Tabella 4.83 Payload dell'azione EditSourceType
96
Componenti che utilizzano questa azione
AudioPlayer Inspector
ImageView Inspector
VideoView Inspector
CreateAlertDialog
Questa azione indica la volontà dell’utente di definire un alert dialog in un viewController
Questa azione supporta i seguenti attributi particolari
dialogId L’identificativo univoco che sarà associato all’alert dialog
viewControllerId L’identificativo univoco del viewController in cui l’alert dialog viene definito Tabella 4.84 Payload dell'azione CreateAlertDialog
L’unico componente che utilizza questa azione è ViewController Inspector
EditAlertDialog
Questa azione indica la volontà dell’utente di modificare gli attributi di un alert dialog
Questa azione supporta i seguenti attributi particolari
dialogId L’identificativo univoco dell’alert dialog da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo AlertDialog
Tabella 4.85 Payload dell'azione EditAlertDialog
L’unico componente che utilizza questa azione è AlertDialog Inspector
DeleteAlertDialog
Questa azione indica la volontà dell’utente di eliminare un alert dialog
Questa azione supporta i seguenti attributi particolari
dialogId L’identificativo univoco dell’alert dialog da eliminare Tabella 4.86 Payload dell'azione DeleteAlertDialog
L’unico componente che utilizza questa azione è AlertDialog Inspector
CreateProgressDialog
Questa azione indica la volontà dell’utente di definire un progress dialog in un viewController
Questa azione supporta i seguenti attributi particolari
dialogId L’identificativo univoco che sarà associato al progress dialog
viewControllerId L’identificativo univoco del viewController in cui il progress dialog viene definito Tabella 4.87 Payload dell'azione CreateProgressDialog
L’unico componente che utilizza questa azione è ViewController Inspector
EditProgressDialog
Questa azione indica la volontà dell’utente di modificare gli attributi di un progress dialog
97
Questa azione supporta i seguenti attributi particolari
dialogId L’identificativo univoco del progress dialog da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo ProgressDialog
Tabella 4.88 Payload dell'azione EditProgressDialog
L’unico componente che utilizza questa azione è ProgressDialog Inspector
DeleteProgressDialog
Questa azione indica la volontà dell’utente di eliminare un progress dialog
Questa azione supporta i seguenti attributi particolari
dialogId L’identificativo univoco del progress dialog da eliminare Tabella 4.89 Payload dell'azione DeleteProgressDialog
L’unico componente che utilizza questa azione è ProgressDialog Inspector
CreateAsyncTask
Questa azione indica la volontà dell’utente di definire un async task in un viewController
Questa azione supporta i seguenti attributi particolari
taskId L’identificativo univoco che sarà associato all’async task
viewControllerId L’identificativo univoco del viewController in cui l’async task viene definito Tabella 4.90 Payload dell'azione CreateAsyncTask
L’unico componente che utilizza questa azione è ViewController Inspector
EditAsyncTask
Questa azione indica la volontà dell’utente di modificare gli attributi di un async task
Questa azione supporta i seguenti attributi particolari
taskId L’identificativo univoco dell’async task da modificare
edits Oggetto che contiene le modifiche da applicare. Sono modificabili tutti gli attributi non indicizzati definiti nel DataStore che contiene oggetti di tipo AsyncTask
Tabella 4.91 Payload dell'azione EditAsyncTask
L’unico componente che utilizza questa azione è AsyncTask Inspector
DeleteAsyncTask
Questa azione indica la volontà dell’utente di eliminare un async task
Questa azione supporta i seguenti attributi particolari
taskId L’identificativo univoco dell’alert dialog da eliminare Tabella 4.92 Payload dell'azione DeleteAsyncTask
L’unico componente che utilizza questa azione è AsyncTask Inspector
98
CreateApplication
Questa azione indica la volontà dell’utente di creare una nuova applicazione.
Questa azione non necessita di particolari attributi
L’unico componente che utilizza questa azione è Navbar
ChangeName
Questa azione indica la volontà dell’utente di rinominare l’applicazione corrente.
name Il nuovo nome dell’applicazione Tabella 4.93 Payload dell'azione ChangeName
L’unico componente che utilizza questa azione è ManifestEditor
ChangeCompanyIdentifier
Questa azione indica la volontà dell’utente di cambiare l’identificativo del produttore per l’applicazione
corrente.
companyIdentifier Il nuovo nome identificativo del produttore per l’applicazione corrente. Tabella 4.94 Payload dell'azione ChangeCompanyIdentifier
L’unico componente che utilizza questa azione è ManifestEditor
DeleteApplication
Questa azione indica la volontà dell’utente di eliminare l’applicazione che si sta sviluppando
Questa azione non necessita di particolari attributi
L’unico componente che utilizza questa azione è Navbar
Azioni riguardanti il controller
CreateAdapterBinding
Questa azione indica la volontà dell’utente di creare un adapterBinding per una scena
Questa azione supporta i seguenti attributi particolari
adapterId L’identificativo univoco che sarà associato all’adapterBinding
scene L’identificativo univoco della scena per cui l’adapterBinding viene definito
adapterClass Il tipo di persistenza con cui l’adapterBinding si interfaccia
file L’identificativo univoco del file da cui i dati vengono letti, se adapterClass è file
entity L’identificativo univoco dell’entità di cui vengono estratte una o più istanze, se adapterClass è entity
preference L’identificativo univoco del record del dizionario delle preferenze da cui i dati vengono estratti, se adapterClass è preference
cloudObject L’identificativo univoco della tipologia di oggetti cloud di cui vengono estratte una o più istanze se adapterClass è cloud
isList Indica se l’adapterBinding estrae una sola entità o una lista di esse
name Il nome distintivo dell’adapterBinding
cloudRefPath Il percorso delle entità da estrarre, nel caso in cui adapterClass sia cloud Tabella 4.95 Payload dell'azione CreateAdapterBinding
99
L’unico componente che utilizza questa azione è Scene Model Editor
DeleteAdapterBinding
Questa azione indica la volontà dell’utente di eliminare un adapterBinding
Questa azione supporta i seguenti attributi particolari
adapterId L’identificativo univoco dell’adapterBinding da eliminare Tabella 4.96 Payload dell'azione DeleteAdapterBinding
L’unico componente che utilizza questa azione è Scene Model Editor
CreateModelConnector
Questa azione indica la volontà dell’utente di creare un modelConnector tra una proprietà di un controllo
utente in una scena e un adapterBinding
Questa azione supporta i seguenti attributi particolari
connectorId L’identificativo univoco che sarà associato al modelConnector
sceneId L’identificativo univoco della scena in cui il modelConnector è valido
viewControllerId L’identificativo univoco del viewController a cui appartiene il controllo a cui è associato il modelConnector
controlId L’identificativo univoco del controllo utente a cui appartiene il modelConnector
adapterId L’identificativo univoco dell’adapterBinding da cui il modelConnector ottiene i dati
keypath Il percorso all’interno della struttura esposta dall’adapterBinding da cui vengono estratti i dati esposti al controllo utente tramite il modelConnector
property La proprietà del controllo che viene collegata al modello tramite il modelConnector
Tabella 4.97 Payload dell'azione CreateModelConnector
Componenti che utilizzano questa azione
AudioPlayer Inspector
Button Inspector
Card Inspector
DatePicker Inspector
GridView Inspector
ImageView Inspector
Label Inspector
Map Inspector
ListView Inspector
Slider Inspector
Spinner Inspector
Switch Inspector
TextEdit Inspector
TimePicker Inspector
VideoView Inspector
WebView Inspector
100
DeleteModelConnector
Questa azione indica la volontà dell’utente di eliminare un modelConnector
Questa azione supporta i seguenti attributi particolari
connectorId L’identificativo univoco del modelConnector da eliminare Tabella 4.98 Payload dell'azione DeleteModelConnector
Componenti che utilizzano questa azione
AudioPlayer Inspector
Button Inspector
Card Inspector
DatePicker Inspector
GridView Inspector
ImageView Inspector
Label Inspector
Map Inspector
ListView Inspector
Slider Inspector
Spinner Inspector
Switch Inspector
TextEdit Inspector
TimePicker Inspector
VideoView Inspector
WebView Inspector
101
4.3.4 Design della navigazione
Questa sezione presenta la struttura di Protocode dal punto di vista della navigazione, dettagliando le
pagine disponibili e le modalità per accedervi
Gli editor di primo livello sono immediatamente accessibili dalla barra di navigazione dell’applicazione, e
riflettono la categorizzazione concettuale del modello di Protocode: editor del modello, editor della vista,
editor del manifesto dell’applicazione e editor dei modelli delle scene. È inoltre presente una schermata di
presentazione di Protocode, con una breve descrizione e i link ai repository di tutte le versioni con i nomi
dei rispettivi autori.
L’editor delle scene è a sua volta diviso, tramite un layout a schede, in editor per cellulari e tablet ed editor
per smartwatch. L’editor per smartwatch è incluso nello schema per completezza, ma non verrà discusso in
quanto non rilevante ai fini di questo lavoro di tesi.
I seguenti paragrafi dettagliano la struttura degli editor più complessi. Manifest Editor e Scene Model Editor
sono costituiti da una sola schermata e non verranno ulteriormente dettagliati in questa sezione.
Smartphone & Tablet Editor
Figura 4.6 Navigazione dell'editor dedicato alla View
Figura 4.5 Navigazione di alto livello
102
L’editor per la parte di view per smartphone e tablet è caratterizzato da varie schermate. Procedendo
gerarchicamente, troviamo gli editor per scene e viewControllers. Le scene sono gli elementi di primo livello
nel modello dell’applicazione mobile alla base di Protocode, mentre i viewController, pur essendo
componenti delle scene, hanno una dignità propria in quanto definibili a priori rispetto alle scene
(l’aggregazione è solo un passo successivo).
L’editor delle scene permette la modifica della struttura delle scene, intesa come la selezione dei
componenti da aggregare per la definizione della scena stessa. Inoltre, esso permette l’accesso all’editor
del menu principale, all’editor di ciascuno dei view controller uniti a comporre la scena e ai view controller
responsabili dell’organizzazione dei precedenti sullo schermo (in caso di scena MultiVC).
L’editor del menù è riducibile ad una lista di elementi, ciascuno modificabile con un editor dedicato agli
elementi del menù.
L’editor per view controller, oltre a permettere di modificare le proprietà di un view controller, permette di
accedere ad un editor diverso per ciascuna tipologia di controlli utente. I controlli inseriti sono elencati nel
prospetto di struttura del view controller e mostrati nella preview dell’applicazione: selezionando un
controllo è possibile accedere all’editor dedicato. Da ogni editor riservato ad un controllo è possibile
accedere all’editor dei vincoli per quel controllo (tale editor è inserito in una scheda dell’editor
precedente).
L’editor delle controlChain permette di personalizzare i parametri di ciascuna controlChain, di aggiungere o
rimuovere componenti della control chain e di impostare i pesi dei diversi componenti (per catene
weighted). Esso inoltre da accesso, esattamente come l’editor per view controller, agli editor per i vari
controlli inseriti.
Tra i vari editor per controlli utente, l’editor per liste e l’editor per griglie danno accesso ciascuno ad un
ulteriore editor: nel caso in cui le celle non siano dinamicamente create a partire da valori del modello, ma
definite staticamente, è possibile personalizzarne titolo e contenuto in tali editor accessori.
Model Editor
Figura 4.7 Navigazione dell'editor dedicato al model
Il model editor è costituito da quattro editor, uno per ogni strategia di persistenza. La navigazione tra i vari
editor avviene tramite un layout master-detail.
Gli editor dedicati al database relazionale e al database cloud si avvantaggiano ciascuno di una ulteriore
schermata. Tali schermate sono dedicate rispettivamente alle entità del database relazionale a agli oggetti
cloud. La navigazione in entrambi i casi si basa nuovamente sul pattern master-detail, dove il master
103
contiene un indice degli oggetti (nella fattispecie entità e oggetti cloud) e il detail permette la modifica
dell’oggetto selezionato
4.3.5 Design dell’interfaccia utente
Questa sezione descrive la struttura dell’interfaccia utente di Protocode. Essendo un’applicazione basata su
React e sulla libreria ReactRouter per la navigazione, l’interfaccia utente è costruita per una gran parte in
modo gerarchico: ogni componente riserva una porzione della propria superficie sullo schermo per inserire
un componente figlio. Questo permette di ottenere una mappatura delle URL delle varie schermate su un
albero di componenti da visualizzare: ogni segmento dell’URL è un componente (o un parametro da passare
al componente precedente), e la parte dell’URL che segue ciascun segmento descrive i componenti figli.
Il contenuto di ogni schermata varia molto in base alla funzione della schermata stessa. L’unico elemento
sempre visibile è la barra di navigazione che consente di spostarsi tra i vari editor, e di accedere a
funzionalità di impatto globale sull’applicazione quali la creazione, la cancellazione o il download di
un’applicazione.
I seguenti capitoli presentano le interfacce utente destinate a Model Editor, Scene Model Editor e Scene
Editor. L’interfaccia relativa al manifest editor è costituita da un solo form, e non verrà ulteriormente
discussa. L’interfaccia destinata allo Smartwatch Editor è costruita esattamente come quella destinata al
Scene Editor, e verrà discussa insieme a quest’ultima in questa sezione.
Model Editor
Figura 4.8 Interfaccia utente per l'editor del modello
L’interfaccia relativa al model editor, oltre alla barra di navigazione, si basa sul pattern master-detail: il
master permette la selezione della strategia di storage su cui si desidera lavorare, il detail consente la
configurazione della strategia selezionata.
104
Scene Editor
Figura 4.9 Interfaccia utente per l'editor della View
Questa interfaccia presenta alcuni componenti fissi e altri variabili in base all’URL corrente, ovvero al
componente selezionato.
Lo screen navigator è un elemento costante che consente la navigazione tra la varie scene e tra i vari view
controller attraverso un menu a tendina. La selezione di una scena o di un view controller dal menù causa
un cambio dell’URL e in conseguenza della schermata.
Il report mostra eventuali problemi relativi a posizionamento o definizione di elementi legati al
componente selezionato. Nel caso si stia lavorando su una scena, vengono esaminati tutti gli elementi
inclusi, dai view controller ai controlli utente.
La device preview mostra un’anteprima dell’applicazione nella cornice di un device selezionabile da una
lista (il device selector), permette l’aggiunta di componenti trascinati dalla palette e la navigazione tra gli
editor dei vari componenti. Il componente selezionato è evidenziato da un bordo e i vincoli di posizione o di
dimensione sono evidenziati attraverso punti o segmenti disegnati intorno al componente stesso. Per i
vincoli di posizione (constraints) vengono mostrati anche i punti di riferimento.
L’inspector è il componente più dinamico, e varia molto in base all’elemento attualmente selezionato. Le
schermate destinate all’editing dei vari controlli si ottengono combinando l’inspector progettato per la
tipologia di controlli stessa e i componenti descritti precedentemente in questo capitolo. Questo garantisce
un look and feel consistente e mantiene sempre disponibile l’anteprima della vista sul video, in modo da
garantire un feedback istantaneo sul lavoro che si sta facendo. Generalmente presenta un layout a schede,
ciascuna delle quali assolve un compito particolare. Le schede nella maggior parte dei casi sono
proprietà specifiche
vincoli di posizione
dimensioni
margini e padding
connessioni con il model
Alcune di esse possono essere omesse per alcuni componenti.
105
Anche le interfacce destinate all’editing di constraints, control chains, celle appartenenti a liste o griglie e
del menù stesso sono gestite da questo componente attraverso inspector adatti alla tipologia di elemento
che si va a modificare.
Scene Model Editor
Figura 4.10 Interfaccia utente per il scene model editor
La schermata destinata al Scene Model Editor presenta una navigazione master – detail: il master permette
la selezione di una scena, mentre il detail bipartito mostra gli elementi attualmente inclusi nel modello
della stessa e gli elementi disponibili per l’inclusione. La scelta di questo tipo di interfaccia deriva dalla sua
larga diffusione: applicazioni come Microsoft Office utilizzano proprio questa modalità per gestire
operazioni di definizioni di sottoinsiemi.
106
4.4 Output della prototipazione Uno dei punti che rendono unica l’applicazione Protocode è la possibilità di esportare il modello
dell’applicazione in formato strutturato basato su XML. Questo abilita la possibilità di utilizzare il prototipo
come base per la generazione automatica del codice dell’applicazione.
Il file che viene esportato da Protocode è costruito attraverso la mappatura di ciascun elemento su un tag
XML che identifica la tipologia di elemento stesso. Gli attributi dell’elemento vengono inseriti come
attributi dell’elemento XML, mentre per quanto riguarda le relazioni vi sono due diverse modalità di
traduzione
inclusione gerarchica: generalmente utilizzata per le relazioni di composizione, prevede che gli
elementi XML che traducono elementi logicamente contenuti in un padre vengano inseriti nel file
come elementi XML figli dell’elemento XML relativo al padre
riferimento: generalmente utilizzata per le relazioni di associazione, prevedono la traduzione della
relazione con un attributo, dal lato da cui la relazione è accessibile, che contiene il percorso
all’interno dell’albero XML dell’elemento associato
La struttura XML viene prodotta utilizzando le regole di corrispondenza tra modello dei dati di Protocode e
modello di riferimento che sono state dettagliate nel paragrafo 4.3.2 Design dei dati, in modo che il
risultato finale sia conforme alle definizione date dal modello di riferimento stesso.
4.5 Struttura del codice sorgente Questa sezione descrive l’organizzazione, in termini di directory, del codice sorgente di Protocode
Le cartelle esterne alla cartella src sono gestite
dall’ambiente React e dal gestore di pacchetti,
l’intera implementazione si trova nella cartella src.
Il file relativi a Redux contengono le azioni, i
reducers e gli strumenti per denormalizzare lo stato,
contenuti nella directory selectors.
Gli editor sono così mappati
Model Editor dataModel/ Manifest Editor manifest/ Scene Model Editor modelAdapter/ Scene Editor editor/scene/ Smartwatch Editor editor/watch
I file nella cartella common sono condivisi tra Scene
Editor e Smartwatch Editor (non trattato in questo
lavoro di tesi).
La logica di generazione del file XML di output è
contenuta nella cartella xml.
La definizione dei diversi tipi di device supportati è
contenuta nella cartella devices Figura 4.11 Organizzazione del codice di Protocode
107
5 MobileCodeGenerator
5.1 Introduzione MobileCodeGenerator è un generatore di codice per applicazioni mobili. Esso consente di generare, almeno
in parte, il codice sorgente di un’applicazione mobile a partire da un modello dell’applicazione stessa. Il
modello di riferimento adottato nell’implementazione sviluppata per questo lavoro di tesi è stato descritto
nel capitolo §3. Ne consegue che l’applicazione generata segue il pattern architetturale MVC che è stato
adottato come base nella definizione del modello stesso, e che il codice che è possibile generare ha la
stessa capacità espressiva del modello. Si evidenziano quindi da subito alcune limitazioni che il modello
stesso pone a questo strumento, prima delle quali l’impossibilità di generare automaticamente i
componenti incaricati di interagire con un eventuale backend. Al fine di ovviare a queste limitazioni e
favorire la possibilità per uno sviluppatore di integrare il codice generato per supplire alle stesse, la
generazione di codice è stata ingegnerizzata in modo da essere il più flessibile possibile.
Dato che il modello di riferimento di MobileCodeGenerator coincide con quello adottato nel design di
Protocode, è possibile affermare che MobileCodeGenerator costituisce la conseguenza logica di Protocode.
Questo permette ai due prodotti di costituire una pipeline per la generazione automatica di applicazioni
mobili basata su modelli.
Come per Protocode, anche MobileCodeGenerator non è interamente frutto di questo lavoro di tesi: la
prima versione è nata nel 2012 dal lavoro di Gregorio Perego e Stefania Pezzetti [3], ed è stata
successivamente migliorata da Mattia Natali nel 2013 [4]. Nell’A.A. 2015/2016 Aldo Pintus [5] ha aggiunto
la possibilità di modellare l’interfaccia per la componente smartwatch (che non tratteremo in questo lavoro
di tesi), mentre nell’A.A. 2017/2018 Alessio Rossotti [6] ha introdotto la modellazione e la generazione del
codice relativi alla parte Model, portando il tool alla versione 4.0. In sintesi, prima di questo lavoro di tesi
MobileCodeGenerator permetteva la generazione automatica delle componenti model e view per
applicazioni native Android e iOS, rispettivamente generate nei linguaggi Java e Swift.
Con questo lavoro di tesi è stata sviluppata la versione 5.0 di MobileCodeGenerator, con due importanti
interventi: l’implementazione di un generatore per applicazioni cross–platform e l’implementazione di un
approccio alla generazione della componente controller. Data l’importanza assunta negli ultimi anni da
ReactNative come strumento per la realizzazione di applicazioni mobili cross – platform, questo lavoro di
tesi investiga sulle potenzialità di tale framework e su come sia possibile sfruttarlo per una generazione
automatica di codice basata su un modello. Inoltre, al fine di mantenere la coerenza con i miglioramenti
apportati al modello di riferimento, è stata introdotta, nel contesto della generazione di codice cross-
platform, la generazione anche delle parti del controller che il modello permette di descrivere. La validità di
questo approccio verrà investigata nel capitolo §6, mentre nel seguito di questo capitolo verrà descritta
l’implementazione di MobileCodeGenerator.
5.2 Software e framework utilizzati MobileCodeGenerator è un generatore di codice basato su Eclipse Modeling Framework e su
OpenArchitectureWare. In quanto tale, la generazione del codice procede attraverso l’espansione ricorsiva
di template.
108
5.2.1 Eclipse Modeling Framework
L’Eclipse Modeling Framework [36] contiene alcuni strumenti utilizzati indirettamente dal generatore di
codice in quanto destinati alla definizione formale del modello di riferimento dell’applicazione che viene
generata.
Lo scopo principale di questo framework è fornire strumenti per la creazione di meta-modelli, ovvero
strumenti destinati a descrivere in modo formale e non ambiguo la struttura di un modello. Tale definizione
può essere progettata con una duplice finalità: di validazione e di deserializzazione di un modello. La
funzionalità di validazione è intuitiva: se il meta-modello descrive la struttura del modello, è possibile
verificare se un’istanza del modello sia conforme alla struttura stessa, e quindi se il modello sia valido
almeno da un punto di vista strutturale. Qualora la validazione strutturale non fosse sufficiente, è possibile
integrare questo framework con strumenti di validazione più attenti alla semantica, come ad esempio lo
strumento Check fornito da OpenArchitectureWare e descritto nel paragrafo successivo. La funzionalità di
deserializzazione invece è estremamente interessante: un modello valido è rappresentato da un albero,
codificato all’interno di un file utilizzando il linguaggio XML. Tale rappresentazione è tuttavia poco pratica
per un generatore di codice. Grazie alla definizione del meta-modello è possibile trasformare ciascun
oggetto definito nel modello in un’istanza di una classe Java i cui attributi sono istanziati a partire dal
modello stesso. Il framework supporta anche la risoluzione di relazioni tra le classi, che sono di
fondamentale importanza nel modello descritto. Sfruttando questa possibilità è possibile implementare il
generatore di codice come un motore basato sull’espansione di templates il cui contesto è l’oggetto che
rappresenta la radice dell’albero descritto tramite XML di cui sopra. Ogni template può, quindi, accedere a
tutti i dati del modello, permettendo così la realizzazione di componenti complessi.
5.2.2 OpenArchitectureWare
OpenArchitectureWare è nato come progetto open source su SourceForge.net e conteneva gli strumenti
Xtext, Xpand e Modeling Workflow Engine (MWE), destinati a costituire una toolbox per l’implementazione
di trasformazioni di modelli in codice eseguibile, anche a supporto della definizione di linguaggi specifici di
un dominio; successivamente, è stato spostato su Eclipse.org e lo sviluppo di ciascun progetto è poi
proseguito in modo indipendente. Per la realizzazione di MobileCodeGenerator sono stati utilizzati Xpand e
MWE: il primo contiene un motore di espansione di templates, necessario per la definizione della logica di
espansione del modello in codice; il secondo permette la definizione e la successiva esecuzione di un file di
workflow che specifica l’elenco ordinato delle operazioni che il motore di traduzione deve eseguire per
generare il codice. Questo consente di realizzare trasformazioni complesse sfruttando un approccio
dichiarativo e modulare: diversi componenti possono essere condivisi tra diversi workflows, favorendo così
un riuso di componenti poco accoppiati. Inoltre, MWE consente l’integrazione con strumenti di post
processing, destinati, ad esempio, alla formattazione del codice generato per una più agevole lettura e
modifica da parte di uno sviluppatore. XPand contiene a sua volta diversi strumenti:
XPand language: è lo strumento principale, serve a definire le regole da applicare per eseguire la
traduzione del modello in codice.
Xtend: dal momento che Xpand language ha un’espressività basilare e limitata, attraverso Xtend è
possibile definire classi e metodi aggiuntivi sfruttando tutta la potenza del linguaggio Java e poi
riutilizzarli nei templates per eseguire operazioni complesse
Check: questo strumento permette di definire controlli semantici da eseguire sul file di modello,
dopo che questo ha passato la validazione strutturale del meta-modello. Come XPand language, le
sue capacità espressive sono piuttosto limitate ma possono essere integrate ed estese con lo
strumento XTend
109
5.3 Struttura del generatore
5.3.1 Struttura preesistente
La versione 4.0 di MobileCodeGenerator supporta la generazione di applicazioni native per iOS e Android.
La struttura del generatore riflette questa dualità, e si presenta costituita da due set di templates, da un
orchestratore che implementa il workflow di generazione, e da alcuni componenti comuni. Di seguito
vengono presentate le varie categorie di templates.
iOS
ios_viewcontrollers_templates: contiene i templates per la generazione del codice relativo ai
controlli utente che poi vengono assemblati a formare i diversi view controllers componenti
l’applicazione
ios_watchcontrollers_templates: contiene i templates per la generazione del codice relativo ai
controlli utente che poi vengono assemblati a formare le diverse schermate di una applicazione per
smartwatch
ios_templates: contiene i templates necessari alla generazione del modello e della persistenza, i
templates che generano la StoryBoard e i templates destinati alla generazione dei files di
descrizione del progetto, come ad esempio il file .xcodeproj necessario all’importazione del
progetto in XCode per la compilazione
ios_extensions: contiene le estensioni scritte in XTend e utilizzate dai templates dei precedenti
gruppi
Android
android_activities_templates: contiene i templates per la generazione del codice relativo ai
controlli utente che poi vengono assemblati a formare i diversi view controllers componenti
l’applicazione
androidwear_activities_templates: contiene i templates per la generazione del codice relativo ai
controlli utente che poi vengono assemblati a formare le diverse schermate di una applicazione per
smartwatch
android_templates: contiene i templates necessari alla generazione del modello e della
persistenza, i templates che generano i file XML relativi al layout e i templates destinati alla
generazione dei files di descrizione del progetto tra cui il file manifest.json
android_extensions: contiene le estensioni scritte in XTend e utilizzate dai templates dei
precedenti gruppi
Parti comuni
app_extensions: contiene le estensioni scritte in XTend e utilizzate da templates di entrambe le
piattaforme target
metamodel: contiene il meta–modello di riferimento
model_checks: contiene la logica che effettua controlli non strutturali (e quindi non effettuati dal
parser nel confronto con il meta–modello) scritti in linguaggio Check
Workflow
AndroidGenerator: genera tutto il codice Android
iOSGenerator: genera tutto il codice Swift
iOSAndAndroidGenerator: combinazione dei due precedenti
110
I workflow per iOS e per Android seguono lo stesso schema
Caricamento del file di input
Istanziazione del meta–modello
Lettura del modello, controllo strutturale, conversione in oggetti Java
Controllo dei vincoli non strutturali
Generazione del codice
o Generazione dei files di default, come ad esempio i file di descrizione del progetto
o Generazione dei files di default per smartwatch
o Generazione delle scene dell’applicazione per smartphone e tablet
o Generazione delle scene dell’applicazione per smartwatch
o Generazione dei files relativi al modello e alla persistenza
Conclusione
5.3.2 Design della generazione di applicazioni cross–platform
Selezione del framework
Il mercato attuale è dominato da due alternative per la produzione di applicazioni mobili cross–platform:
Xamarin e ReactNative.
Il primo prodotto è basato sul linguaggio C# per la definizione della logica applicativa e sul linguaggio XAML
per la definizione delle interfacce grafiche. XAML è dotato di binding a due vie per il collegamento tra UI e
dati e di eventi per la gestione dell’interazione con l’utente. La produzione cross–platform viene ottenuta in
modo molto diverso su iOS e su Android. Su iOS il codice sorgente viene compilato tramite ahead-of-time
compilation, la libreria .NET viene inclusa (le parti non utilizzate vengono eliminate dal linker). Su Android il
codice viene convertito in un linguaggio intermedio che viene poi viene eseguito tramite just in time
compilation sulla MonoVM, che viene eseguita in parallelo ad ART. Questi passaggi, oltre ad aumentare lo
spazio richiesto dall’app specialmente su Android, appesantiscono il processo di sviluppo: la compilazione è
lunga, il live editing impossibile. Inoltre, la limitazione maggiore di Xamarin consiste nella necessità di
definire molte parti dell’applicazione in modo autonomo per Android e per iOS a causa di divergenze nei
rispettivi sistemi operativi.
Il secondo prodotto è basato su React con JSX per il design sia delle interfacce che della logica, con la
differenza che i controlli utilizzati tramite JSX non sono controlli HTML come in React ma controlli nativi
della piattaforma su cui l’applicazione viene eseguita. L’applicazione ReactNative quindi rende viste native
orchestrate da logica definita in Javascript. Il collegamento tra le due parti viene realizzato dal framework
stesso in modo automatico. I maggiori punti di forza di ReactNative rispetto a Xamarin sono la possibilità di
avere live editing durante la scrittura del codice, una compilazione agile, una maggior qualità e reperibilità
della documentazione e la possibilità di utilizzare la maggior parte dei componenti disponibili nella libreria
di npmjs.
Per questo progetto è stato quindi scelto il framework ReactNative.
Modifiche al workflow
Dato che ReactNative non supporta le applicazioni per smartwatch, il workflow di generazione del codice
deve essere, seppur in minima parte, riadattato, anche per includere la novità di questo lavoro di tesi,
ovvero la generazione degli elementi relativi al controller
111
Caricamento del file di input
Istanziazione del meta–modello
Lettura del modello, controllo strutturale, conversione in oggetti Java
Controllo dei vincoli non strutturali
Generazione del codice
o Generazione dei files di default, come ad esempio i file di descrizione del progetto
o Generazione delle scene dell’applicazione per smartphone e tablet
o Generazione dei files relativi al controller
o Generazione dei files relativi al modello e alla persistenza
Conclusione
Architettura di destinazione
L’applicazione generata si basa su ReactNative per l’interfaccia e l’interazione con l’utente, e su Redux per
la gestione dello stato e l’interazione con la persistenza.
L’architettura di destinazione segue un pattern a livelli per favorire la soluzione di diversi problemi in
isolamento.
Figura 5.1 Architettura di riferimento per l'applicazione generata
Data layer
Si occupa della persistenza ed è implementato utilizzando librerie ibride dell’ecosistema di
ReactNative in modo da ottenere performance native ed accesso alle risorse tramite JavaScript.
Data access layer
Un layer implementato puramente in Javascript che fornisce un insieme di facades utilizzate per
astrarre i dettagli della persistenza.
State layer
Questo layer è gestito da Redux, e contiene lo stato attuale dell’applicazione. Gli oggetti sono
caricati dalla persistenza all’occorrenza e sono riscritti in caso di modifiche. L’introduzione di questo
layer presenta diversi benefici. In primo luogo, astrae dalla persistenza e presenta al layer
successivo un oggetto Javascript che contiene l’intero stato dell’applicazione. In secondo luogo,
connette il dominio di React, basato su un’implementazione dichiarativa, con il dominio delle
librerie di storage, basato su un’implementazione imperativa. Da ultimo, esso semplifica
l’interfaccia di accesso ai dati, che ora avviene tramite azioni codificabili con semplici oggetti
Javascript.
Scene model layer
Questo layer implementa l’operazione di restrizione del modello dell’applicazione al modello della
scena attualmente visualizzata. Inoltre, esso invia al layer precedente le azioni per caricare oggetti
dalla persistenza e per modificare tali oggetti. Infine, esso inietta i dati nel layer successivo
passandoli come proprietà React ai componenti che lo implementano.
112
Scene layer
Questo layer è utilizzato per aggregare diversi view controller in scene e per fornire funzionalità di
navigazione tra le diverse scene e tra i diversi view controller appartenenti alla scena
correntemente visualizzata. Inoltre, esso converte il scene model ricevuto dal layer precedente in
proprietà che verranno passate direttamente ai controlli utente, implementando così
un’operazione di decomposizione degli oggetti a singoli valori che possono poi essere utilizzati dai
componenti nativi.
View controller layer
Questo layer è utilizzato per mostrare contenuto all’utente. I controlli si collegano alle proprietà
ricevute dal layer precedente e vengono disposti sullo schermo dal solutore dei vincoli, emulando il
noto ConstraintLayout della libreria Android. I controlli utilizzati provengono dalla libreria di
ReactNative così come da librerie di terze parti. In ogni caso, essi sono resi come controlli nativi.
Solo l’orchestratore continua ad essere puramente Javascript. Questo layer è inoltre responsabile
di intercettare le azioni dell’utente e convertirle in modifiche ai dati o in azioni di navigazione da
inviare al layer precedente.
Principali sfide implementative
La struttura e le caratteristiche peculiari di ReactNative ne fanno uno strumento estremamente potente e
innovativo, ma pongono alcune limitazioni che rendono difficile la conciliazione con strumenti di sviluppo
più tradizionali, come Java o Swift. In questo capitolo verranno presentate le principali sfide incontrate in
questo lavoro di tesi, e verrà discusso il modo in cui sono state aggirate o risolte, o eventualmente il motivo
per cui sono state lasciate come “problemi aperti”, sempre con l’obiettivo di mantenere il generatore di
codice omogeneo quanto a capacità espressiva rispetto alle diverse piattaforme.
Task in background
Il tradizionale modello di concorrenza di una applicazione mobile prevede la coesistenza di un thread
principale supportato da diversi worker threads, ciascuno dedicato ad un task specifico che non è possibile
eseguire sul thread principale senza rendere la vista poco responsive. Il modello di riferimento prevede la
possibilità di modellare la necessità di tali worker threads per ciascun view controller (§3.1.7). In
ReactNative il supporto all’esecuzione di thread in background si basa sulla libreria HeadLess JS, che allo
stato attuale è supportata solo su Android. La migliore soluzione veramente cross-platform, e non basata
quindi su HeadLess JS, prevede la possibilità di avviare un solo worker thread, e presenta comunque
notevoli limitazioni. La community di sviluppatori ReactNative sta tuttavia chiedendo in modo importante
la realizzazione di un’estensione di HeadLess JS anche ai dispositivi iOS. In attesa di chiarimenti in merito al
futuro di HeadLess JS e in generale della concorrenza in ReactNative, la versione 5.0 di
MobileCodeGenerator limita il supporto per i task in background alle applicazioni native e non cross-
platform.
Layout model
I produttori di entrambi i sistemi operativi mobili maggiori (Android e iOS) stanno fortemente
incoraggiando l’utilizzo di sistemi di layout basati su vincoli, con una duplice finalità: poter modellare una
vista con una struttura piatta di controlli e ottenere un layout il più portabile e adattabile rispetto a schermi
diversi per dimensioni o risoluzione. Il modello alla base di ogni layout in ReactNative è invece basato sul
paradigma delle FlexBox, che consentono di esprimere un layout utilizzando contenitori che distribuiscono i
propri figli all’interno dello spazio ad essi destinato con strategie che ricordano le catene di controlli. Il
modello di riferimento disegnato nei precedenti lavori di tesi era fortemente basato su layout a controlli, ed
è quindi stato necessario replicare tale meccanismo nella generazione di applicazioni cross-platform. La
113
traduzione di vincoli in strutture basate su FlexBox è estremamente complessa ed inefficiente, e richiede
una completa reingegnerizzazione del layout. Per questo motivo la versione 5.0 di MobileCodeGenerator
inserisce nel codice generato una libreria di componenti dedicati ad emulare il funzionamento di un classico
ConstraintLayout. Tale libreria è stata sviluppata nel contesto di questo lavoro di tesi, ed è ritagliata sul
modello dei vincoli presentato nel capitolo §3.1.2.
Varietà dei controlli utente
Il modello di riferimento integra una vasta gamma di controlli utente che è possibile utilizzare nella
costruzione delle viste di un’applicazione. Tali componenti sono nativamente disponibili su entrambi i
sistemi operativi Android e iOS, ma non tutti appartengono alla libreria standard di ReactNative. Per questo
motivo il codice generato si appoggia a librerie proprie o di terze parti per coprire le carenze della libreria
standard di ReactNative. In particolare, il componente Card così come utilizzato nel modello di riferimento
è implementato con una libreria propria, mentre AudioPlayer, AudioRecorder, PhotocameraController,
VideocameraController e VideoView sono implementati con una libreria propria supportata da librerie di
terze parti. Infine, i controlli Spinner, Map, DatePicker, TimePicker e GridView sono ottenuti in toto da
librerie di terze parti.
Strategie di persistenza
Il modello di persistenza fornito da ReactNative è molto elementare ed è basato su un repository di valori
ciascuno associato ad una chiave, sul modello del LocalStorage fornito in ambiente web. Tale modello è
tuttavia estremamente riduttivo e semplicistico per le necessità imposte dalla traduzione del modello, ed è
quindi stato necessario appoggiarsi a librerie di terze parti al fine di integrare tutte le strategie di
persistenza previste dal modello di riferimento (§3.2)
Inizializzazione dei scene model
Il modello di riferimento permette di specificare la struttura di ciascun scene model (§3.3) necessario al
funzionamento dell’applicazione, ma non sempre permette di definire la logica necessaria alla sua
inizializzazione. Per quanto riguarda la persistenza tramite Shared Preferences, il problema è semplice: la
definizione del record da importare nel modello contiene tutte le informazioni necessarie al suo
caricamento dalla memoria persistente: la chiave. Allo stesso modo, la persistenza tramite File Storage
utilizza solo il nome del file per ottenere tutti i dati necessari. Oltre che la limitatezza delle informazioni
necessarie all’importazione dalla memoria di massa, un altro aspetto accomuna queste strategie di
persistenza: l’assenza di relazioni strutturali tra diversi record. Questo non vale per il database relazionale,
che mette proprio al centro le relazioni tra entità, e per il database cloud, la cui struttura ad albero si basa
sulla possibilità che un oggetto contenga uno o più altri oggetti ricorsivamente. Tali relazioni possono
inoltre dare luogo a grafi di dipendenza ciclici. Per questo motivo, il codice che si occupa di importare nel
scene model un’entità relazionale o un oggetto cloud non si occupa di portare in memoria anche tutte le
entità correlate, inizializzando solamente gli attributi propri (che sono peraltro sempre di tipo scalare).
Qualora sia necessario ottenere un riferimento anche ad una collezione di entità correlate ad un elemento
del scene model è necessario modellarlo come un oggetto indipendente e andare poi a modificare
conseguentemente il codice generato. La modifica richiesta comunque è minima: viene generato il codice
per il caricamento di una collezione, si tratta solo di definire il predicato utilizzato per selezionarne gli
elementi (tipicamente, utilizzando il campo identificativo di un altro elemento appartenente al scene
model). Il database relazionale è soggetto ad una ulteriore limitazione: il modello di riferimento non
consente di definire la query SQL necessaria alla selezione dell’elemento/degli elementi da importare nel
scene model. Il codice incaricato di tale importazione andrà quindi arricchito opportunamente da uno
sviluppatore. Questa limitazione non sussiste per il database cloud, in quando un’entità è completamente
114
descritta dal suo percorso rispetto alla radice, e questo può essere efficacemente modellato da un utente.
Per questo motivo il campo cloudRefPath è incluso nel modello di un AdapterBinding (§3.3.1)
Implementazione delle finestre di dialogo
L’apertura e la chiusura di una finestra di dialogo (alertDialog o progressDialog) sono tradizionalmente
implementate in modo imperativo, mentre il paradigma imposto da ReactNative è dichiarativo. Inoltre, il
modello di riferimento impone che ogni view controller abbia visibilità sulle proprie finestre di dialogo, e
non su quelle di altri view controller. Questo impone una soluzione locale alla scene per l’apertura e la
chiusura delle finestre di dialogo dei view controller componenti. Questo può essere implementato in
ReactNative sfruttando il fatto che gli eventi percorrono l’albero dei controlli dalle foglie verso la radice:
ogniqualvolta un view controller viene istanziato, esso emette un evento contenente un dizionario in cui a
ciascuna chiave corrispondono due funzioni destinate rispettivamente ad aprire e chiudere la finestra di
dialogo. La scene che ha istanziato il view controller può così mettersi in ascolto dell’evento e ottenere il
controllo delle finestre di dialogo messe a disposizione dal view controller stesso. In caso una scene abbia
più figli, ciascuno di essi emetterà un evento e la scene procederà ad aggregare i dizionari ottenuti in un
unico dizionario delle finestre disponibili. Questo consente di implementare l’apertura e la chiusura di
finestre di dialogo in modo locale alle singole scene.
Implementazione del generatore di View e Model per ReactNative
Al fine di affiancare, alla generazione di codice Java per Android e Swift per iOS, anche la generazione di
codice ReactNative per le stesse funzionalità, sono stati aggiunti al generatore i seguenti elementi
reactnative_templates: contiene tutti i templates necessari a generare Scene Layer e View
Controller Layer. In particolare, esso contiene
o Scenes.xpt: template dedicato alla generazione delle scene e della navigazione sia tra scene
che all’interno della singola scena
o ViewController.xpt: template dedicato alla generazione dei viewController (compresi i
parentViewController). Questi template si appoggiano alle funzionalità offerte dalle scene
in cui i view controller sono inseriti e richiamano i templates di generazione dei controlli
utente. Il componente che implementa il solutore dei vincoli viene inserito a questo livello.
o Screen.xpt: template dedicato alla generazione di un componente di connessione tra una
scena e un viewController in essa contenuto
o Un template per ogni tipologia di controlli: questi template seguono una struttura
consistente generando i vari elementi che servono a mostrare un controllo: stili definiti
dall’utente, vincoli di posizione e di dimensione, stato del componente, eventi da gestire e
logica di rendering
reactnative_storage_templates: contiene tutti i templates destinati a generare il Data Access
Layer. In particolare, esso contiene un template per la generazione di uno o più adapter per ogni
strategia di persistenza. Per quanto riguarda sharedPreferences, fileStorage e cloudDatabase
l’adapter generato è soltanto uno (per tipologia), mentre per relationalDatabase viene generato un
adapter per ogni entità più un adapter globale che gestisce la connessione al database.
reactnative_project_structure: contiene tutti i templates destinati a generare file di struttura del
progetto e files statici, che costituiscono una libreria di riferimento per gli altri componenti. I files
che descrivono il progetto sono sempre generati, mentre i files di libreria vengono generati
solamente all’occorrenza.
o AndroidManifest.xpt: genera il file manifest.json necessario ad eseguire l’applicazione su
Android
115
o app_json: genera il file app.json che contiene metadati dell’applicazione utilizzati dal
runtime di ReactNative
o build_gradle.xpt: genera il file build.gradle necessario per la compilazione dell’applicazione
e l’inclusione delle dipendenze su Android
o index.xpt: genera il file index.js, entry point dell’applicazione ReactNative
o package_json.xpt: genera il file package.json che indica le dipendenze dell’applicazione da
installare tramite Yarn o NPM. Le dipendenze sono generate in base alle feature
effettivamente richieste dall’applicazione, in modo da ridurre la dimensione del bundle.
o settings_gradle.xpt: genera il file settings.gradle contente le impostazioni di compilazione
per Android
o Audio.xpt: genera gli strumenti propedeutici all’accesso a registrazione e riproduzione
audio
o CameraView.xpt: genera un componente capace di utilizzare la fotocamera del device per
scattare foto o registrare video. Questo componente viene inserito nell’applicazione
quando sono previste funzionalità di interazione con la fotocamera.
o Card.xpt: genera un componente capace di produrre un layout a card come definito da
Google Material UI.
o ConstraintLayout.xpt: genera il componente che implementa il ConstraintLayout e la
libreria di risoluzione dei constraints.
o ContentValues.xpt: genera una libreria di appoggio utile nell’interazione con il database al
momento dell’inserimento o della modifica di un’entità
o DatabaseStore.xpt: genera una libreria di astrazione sul database che mostra un generico
repository al posto di una tabella con i suoi record.
o VideoPlayer.xpt: genera un componente capace di riprodurre un video con i controlli
elementari per avviare o arrestare la riproduzione
Implementazione della generazione del controller
La generazione del controller è stata aggiunta nella versione 5.0 di MobileCodeGenerator solamente per la
generazione di codice ReactNative. Essa ha richiesto diverse modifiche e aggiunte al codice di
MobileCodeGenerator.
Le modifiche più importanti riguardano il meta–modello. È stato infatti necessario definire nuove strutture
per codificare le modifiche apportate nel modello di riferimento con l’introduzione di AdapterBinding e
ModelConnector.
Per implementare nell’applicazione generata lo State Layer è stato aggiunto un nuovo set di templates
chiamato reactnative_controller. I templates di tale gruppo si occupano della generazione delle azioni
necessarie ad operare con la persistenza e dei reducers in grado di eseguirle. La più grande sfida risolta da
questo layer riguarda l’asincronia delle operazioni con la persistenza: qualsiasi interazione, sia essa di
lettura o scrittura, richiede il disaccoppiamento temporale di richiesta e risposta. Per gestire questo fatto
ogni operazione è in realtà codificata da due azioni, che ne segnano l’inizio e la conclusione e consentono ai
reducer di operare in modo sincrono anche in presenza di operazioni asincrone: i reducer operano in modo
sincronizzato con inizio e fine dell’operazione asincrona. Tali azioni sono riconoscibili nel codice dai suffissi
_REQUEST e _COMPLETE applicati al tipo dell’azione. Tali azioni tuttavia sono opache al livello superiore
grazie al middleware redux-thunk per redux. A fianco della generazione di tali azioni vengono infatti
generati anche alcuni thunks necessari ad orchestrare l’invio delle azioni al reducer e l’operazione
116
asincrona stessa. Il livello superiore invia al runtime redux non una semplice azione, ma un thunk, in modo
da incapsulare completamente la gestione della comunicazione asincrona.
Figura 5.2 Utilizzo di un thunk nelle interazioni con la persistenza
Per implementare il Scene Model Layer è stata introdotta la generazione di componenti connessi allo stato
di redux che agiscono da incapsulatori per le scene. Tali componenti interagiscono con lo stato in modo da
caricare dalla persistenza gli elementi necessari a comporre il scene model. Quando tutti gli elementi sono
disponibili, ovvero quando la persistenza termina le operazioni asincrone, tali componenti eseguono
l’operazione di riduzione dello stato al scene model estraendo gli elementi di loro interesse e inoculandoli
nei componenti che implementano layers più vicini all’utente.
A livello di Scene Layer è stato necessario modificare la generazione degli screen. Tali componenti
costituiscono infatti il luogo naturale in cui implementare i Model Connectors. L’implementazione
comunque non richiede modifiche sostanziali se non l’aggiunta di qualche proprietà passata al view
controller che ciascuno di essi incapsula, in modo che le proprietà calcolate dai connettori raggiungano i
controlli finali.
A livello di View Controller Layer sono state modificate le proprietà dei componenti, in modo che possano
assumere un valore passato dai livelli inferiori o un valore di default (il valore che assumevano prima delle
modifiche descritte in questo capitolo).
5.3.3 Generazione del codice
In questa sezione vengono presentate le regole codificate nei templates del generatore di codice per varie
categorie di elementi, in modo da fornire una panoramica su come le considerazioni esposte nel capitolo
§5.3.2 siano state inserite nell’applicazione generata.
117
Generazione dei controlli utente
Ogni categoria di controlli utente definita nel modello di riferimento è associata ad una classe che definisce
un appropriato componente React. Ne consegue che per ogni definizione di un controllo utente nel
modello dato in input a MobileCodeGenerator viene generata un’istanza della corrispondente classe di
componenti, le cui proprietà vengono inizializzate a partire dalla definizione stessa del controllo utente.
Dato che React è fortemente basato sulla composizione, la relazione di ereditarietà tra UiPhoneControl e le
sue sottoclassi viene resa con una relazione di composizione: il componente caratteristico per la classe di
controlli viene inserito all’interno di un altro componente (generico rispetto alla tipologia di controlli) che si
occupa di tutti gli aspetti comuni tra le diverse tipologie di controlli. Questa relazione permette al
componente esterno di accedere alle proprietà di istanza, leggere quelle di suo interesse, ed inoltrare tutte
le altre alla classe caratteristica. Infine, per migliorare la leggibilità del codice, viene generato per ogni
istanza un oggetto contenente tutte le proprietà stilistiche, come margini, padding, colori e dimensioni del
testo. Tale oggetto viene poi passato all’istanza corrispondente tramite proprietà, la sua definizione non
inline serve solo a migliorare la leggibilità del codice.
A titolo di esempio di quanto esposto, viene presentata l’espansione di un componente di tipo Label, la cui
classe caratteristica è la classe Text che, una volta composta con la classe generica, assume il nome CText.
Tal componente ha le seguenti caratteristiche
un margine di 10 punti e un padding di 5 punti su tutti i lati
una larghezza fissa al 50% del padre e un’altezza fissa a 40 punti
un vincolo di posizione che allinea il lato superiore al bordo superiore del contenitore
un vincolo di posizione che allinea il lato sinistro al bordo destro di un altro elemento
testo centrato nello spazio disponibile
font in variante grassetto e di colore rosso alto 30 punti
<CText
id=”Id123456”
style={styles.eId123456}
marginTop={10}
marginStart={10}
marginEnd={10}
marginBottom={10}
defaultHeight={25}
defaultWidth={80}
posX={0}
posY={0}
constraintWidthPercent={0.50}
constraintHeight={40}
constraintTop={{ ref: null, side: ‘top’ }}
constraintStart={{ ref: ‘eId987654’, side: ‘end’ }}
>
{this.props.eId123456Title !== undefined ? this.props.eId123456Title : “Un contenuto statico”}
</CText>
// Nella collezione degli stili (che si trova nel file che implementa il viewController a cui la label appartiene) troviamo
{
// Altri stili…
eId123456: {
paddingTop: 5,
paddingBottom: 5,
paddingStart: 5,
paddingEnd: 5,
textAlign: ‘center’,
fontWeight: ‘bold’,
fontSize: 30,
color: ‘#FF0000’
}
}
118
Le proprietà in arancio sono gestite dalla classe generica e non vengono inoltrate alla classe Text, le
proprietà colorate in verde sono gestite direttamente dalla classe Text e le proprietà in bianco sono
trasversali rispetto alla tipologia di controllo utente e sono gestite direttamente dal framework. È evidente
come le proprietà gestite dalla classe generica corrispondano a proprietà definite per UiPhoneControl,
mentre le proprietà in verde corrispondano a proprietà definite sulla sottoclasse in questione. Questo
meccanismo è utilizzato per la definizione delle classi corrispondenti a qualsiasi controllo utente.
Nell’esempio sono assenti le control chains, che vengono comunque inserite come proprietà passate a
CText e gestite dalla classe generica in quanto parte del meccanismo di posizionamento. Nel caso in cui la
label di cui sopra avesse fatto parte di una catena di controlli di tipo weighted (cosa non possibile
nell’esempio in quanto il controllo è soggetto a vincoli sia per l’asse verticale che per quello orizzontale), la
definizione avrebbe portato i seguenti attributi
<CText
{/* Altri attributi */}
constraintChain=‘weighted’
constraintChainAxis=‘horizontal’,
constraintWeight={3}
constraintChainPrev=’eId135790’
>
{this.props.eId123456Title !== undefined ? this.props.eId123456Title : “Un contenuto statico”}
</CText>
Generazione dei view controller
Per ogni view controller definito nel modello di riferimento viene creata una classe di componenti, che sarà
poi istanziata al momento dell’inserimento del view controller stesso come figlio di una scena. Tale classe
ricopre due ruoli essenziali: contiene le definizioni di tutti i controlli inseriti nel view controller, occupandosi
quindi di istanziare il ConstraintLayout necessario per le operazioni di rendering, e agisce come proxy per
tutti gli eventi che provengono dai controlli utente e devono essere inoltrati alla scena. La gestione degli
eventi infatti difficilmente avviene localmente ad un view controller in quanto ogni view controller può
essere inserito in più di una scena, e in ciascuna di esse può operare in modo diverso. In altri termini, la
logica dell’applicazione è definita a livello di scena, e non di view controller. Ogni classe rappresentante un
view controller è inoltre corredata da una struttura dati accessoria, organizzata come un dizionario. Tale
struttura dati ha il compito di contenere la definizione di tutti gli stili applicati al view controller stesso
(come il colore di sfondo) e a tutti i controlli in esso contenuti. Gli stili sono indicizzati in base
all’identificativo univoco dei componenti a cui sono associati. Da ultimo, vi è un ulteriore compito
accessorio svolto dalla classe che rappresenta un view controller, ed è inoltrare alla scena in cui è inserito le
funzioni necessarie ad utilizzare le finestre di dialogo definite nel view controller stesso. Nel caso in cui si
tratti di un parent view controller, esso inoltra alla scena un dizionario contenente le definizioni delle
finestre di dialogo di tutti i child view controller in esso contenuti.
A titolo di esempio, presentiamo le parti più rilevanti di un semplice view controller che modella una
funzionalità di registrazione. Esso contiene tre componenti: due caselle di testo (CTextInput) e un pulsante
di conferma (CTouchableHighlight).
// Dizionario degli stili const styles = StyleSheet.create({ ejr7ufryu: { width: '100%', height: '100%', backgroundColor: '#ffffff' }, ejr7ugetx: { paddingTop: 0, paddingBottom: 0, paddingStart: 0,
119
paddingEnd: 0, fontSize: 16, }, ejr7uij59: { paddingTop: 0, paddingBottom: 0, paddingStart: 0, paddingEnd: 0, fontSize: 16, }, ejr7uk18g: { paddingTop: 0, paddingBottom: 0, paddingStart: 0, paddingEnd: 0, borderRadius: 10, backgroundColor: '#cecece', }, ejr7uk18g_wrapper: { flex: 1, justifyContent: 'center', alignItems: 'center' }, ejr7uk18g_inner: { textAlign: 'center', color: '#000000', }, }); // Classe che definisce il componente export default class Component_Registration extends React.Component { registerInterface(componentInterface) { // Metodo utilizzato per registrare gli AlertDialogs e i ProgressDialogs esposti da questo componente // O da uno dei figli, qualora questo sia un parent view controller // Quando tutti i figli si sono registrati, questo metodo richiama una funzione passata al componente // dalla scena per fornire le chiamate di apertura delle finestre di dialogo } // Proxy per gli eventi provenienti dai controlli // Ogni evento possiede due proxy, uno specifico e uno generico // - quello specifico viene eseguito solo per eventi provenienti dal componente // - quello generico viene eseguito per eventi provenienti da tutti i componenti della stessa tipologia // Ad esempio, un proxy generico è onTextChange, che reagisce a tutti gli eventi di modifica di tutte le textbox // mentre un proxy specifico è onE123456Change, che reagisce solo alle modifiche apportate alla textbox con id e123456 onjr7ugetxChange(text) { if (this.props.onEjr7ugetxValueChange) this.props.onEjr7ugetxValueChange(text); if (this.props.onTextChange) this.props.onTextChange('jr7ugetx', text); } onjr7uij59Change(text) { if (this.props.onEjr7uij59ValueChange) this.props.onEjr7uij59ValueChange(text); if (this.props.onTextChange) this.props.onTextChange('jr7uij59', text); } onjr7uk18gPress() { if (this.props.onEjr7uk18gPress) this.props.onEjr7uk18gPress(); if (this.props.onButtonPress) this.props.onButtonPress('jr7uk18g'); } // Metodo predefinito di ReactNative // Si occupa di definire come questo componente appare render() { const { style, onInterface, ...passProps } = this.props; return ( <> <ConstraintLayout style={styles.ejr7ufryu}> // Istanziazione del constraint layout // Tutti gli elementi contenuti nel constraint layout sono controlli utente // che vengono inseriti nel vista. Sono riportati in questo esempio solo gli attributi // relativi ai vincoli di posizione e dimensione e alla gestione degli eventi <CTextInput id="ejr7ugetx" marginTop={170} marginStart={30} marginEnd={30} constraintStart={{ ref: null, side: 'start' }} constraintEnd={{ ref: null, side: 'end' }} constraintTop={{ ref: null, side: 'top' }} style={styles.ejr7ugetx} onChangeText={this.onjr7ugetxChange} value={this.props.ejr7ugetxValue} placeholder="Name">
120
</CTextInput> <CTextInput id="ejr7uij59" marginTop={40} constraintStart={{ ref: 'ejr7ugetx', side: 'start' }} constraintEnd={{ ref: 'ejr7ugetx', side: 'end' }} constraintTop={{ ref: 'ejr7ugetx', side: 'bottom' }} style={styles.ejr7uij59} onChangeText={this.onjr7uij59Change} value={this.props.ejr7uij59Value} placeholder="Surname"> </CTextInput> <CTouchableHighlight id="ejr7uk18g" marginTop={60} constraintStart={{ ref: 'ejr7ugetx', side: 'start' }} constraintEnd={{ ref: 'ejr7ugetx', side: 'end' }} constraintTop={{ ref: 'ejr7uij59', side: 'bottom' }} onPress={this.onjr7uk18gPress} style={styles.ejr7uk18g} activeOpacity={1} underlayColor={'#cecece'} > <View style={styles.ejr7uk18g_wrapper}> <Text style={styles.ejr7uk18g_inner}>OK, let's go!</Text> </View> </CTouchableHighlight> </ConstraintLayout> </> ); } }
È importante notare che i controlli utente non hanno un valore definito all’interno del view controller, ma
che i valori sono acquisiti dalle proprietà: questo permette alla scena che istanzia il view controller di
fornire i valori per i controlli prendendoli dal scene model e personalizzando così il comportamento del view
controller.
La seguente figura mostra il risultato dell’esempio precedente
Figura 5.3 Esempio di vincoli in un ViewController
121
Generazione delle scene
La generazione delle scene è la parte più complessa del generatore, in quanto vi sono diverse logiche di
aggregazione dei view controller componenti, e le differenze tra di esse sono piuttosto profonde. Inoltre,
ciascuna scena viene definita due volte: la prima definizione viene utilizzata se l’applicazione è eseguita su
smartphone, la seconda in caso il device di esecuzione sia un tablet. Al fine di ridurre la complessità
mediante divisione in sottoproblemi, il generatore crea diversi componenti per ogni scena. Nel seguito di
questo paragrafo il termine scena denota il caso specifico della definizione di una scena per uno specifico
device: ogni scena del modello di riferimento corrisponde naturalmente a due scene specifiche ai device, in
base a quanto detto sopra.
Procedendo con un approccio bottom-up, il primo componente generato è il parent view controller, se
richiesto. In particolare, il parent view controller viene generato solo per scene la cui tipologia è MULTI_VC,
in quanto questo è l’unico caso in cui sia necessario un componente per definire la logica di disposizione dei
figli sullo schermo. In tutti gli altri casi, viene mostrato un unico view controller a tutto schermo.
Il secondo componente definito è chiamato Screen e serve a dare uniformità ai figli diretti di una scena
rispetto al componente successivo: ogni videata, sia essa un singolo view controller o un gruppo di view
controller incapsulati in un parent view controller, viene a sua volta inclusa in uno Screen. Il ruolo
fondamentale dello Screen è quello di applicare correttamente i Model Connectors validi per la scena per
cui sono definiti: ciascuno Screen ha accesso al scene model e conosce l’identità dei controlli utente inseriti
nei suoi figli, e può quindi applicare le logiche di conversione dati definite dai Model Connectors. In caso di
interazioni bidirezionali, è sempre compito dello Screen applicare le modifiche ottenute attraverso un
Model Connector al Scene Model: gli oggetti del Scene Model sono immutabili, e quindi ogni modifica
richiede una reistanziazione del frammento modificato (ogni adapter binding infatti è indipendente dagli
altri), che deve essere poi inviato alla persistenza attraverso un componente più in alto nella gerarchia.
Il terzo componente generato ha il ruolo essenziale di navigatore, in quanto permette la navigazione tra i
diversi screen facenti parte della scena. Questo componente ha il compito di fornire ai livelli più bassi le
primitive per effettuare azioni di navigazione imperativa all’interno della scena e di gestire l’eventuale barra
delle tabs (qualora la scena abbia tipologia SINGLE_VC_TAB). La navigazione tra scene diverse è
implementata sempre con un componente di questo tipo, ma definito globalmente nella radice
dell’applicazione. Il navigatore è generato con il supporto di una libreria esterna.
Il quarto e ultimo componente richiesto per il corretto funzionamento di una scena, chiamato scene model
master, è il componente responsabile del scene model. Tale componente non appartiene come tutti gli altri
al scene layer, ma al scene model layer. Ciononostante, è necessario definire questo componente per ogni
scena, in modo da mantenere l’indipendenza della scena stessa. Quando una scena viene visualizzata,
questo componente viene inserito nella vista e, sebbene non abbia alcun riscontro grafico visibile, esso
richiede allo state layer i dati necessari a formare il scene model. Quando il scene model è pronto, il
navigatore viene caricato sullo schermo. Questo viene fatto per evitare inconsistenze nei dati che poi
vengono inoltrati ai controlli utente. Ogni volta che si verificano modifiche a qualche dato, lo screen di
competenza inoltra la nuova versione dei dati a questo componente, che la persiste nello state layer. La
sincronizzazione tra state layer e memoria di massa viene gestita in altri livelli, e non è di competenza di
questo componente.
Il seguente diagramma riassume la struttura logica dei componenti generati per ogni scena, nel caso in cui
la scena sia di tipo SINGLE_VC.
122
Figura 5.4 Struttura di una scena di tipo SINGLE_VC
Il seguente diagramma riassume la struttura logica dei componenti generati per ogni scena, nel caso in cui
la scena sia di tipo SINGLE_VC_TAB.
Figura 5.5 Struttura di una scena di tipo SINGLE_VC_TAB
Il seguente diagramma riassume la struttura logica dei componenti generati per ogni scena, nel caso in cui
la scena sia di tipo MULTI_VC.
Figura 5.6 Struttura di una scena di tipo SINGLE_VC
Generazione delle definizioni del modello
Durante l’esecuzione dell’applicazione, è necessario che tutti i dati in uso da parte della vista corrente siano
memorizzati nello state layer, in modo da essere immediatamente accessibili quando la vista viene
mostrata. Il caricamento dei dati dalla memoria persistente avviene su richiesta da parte dei scene model
master, che deve quindi avere conoscenza della struttura del modello per poterlo interrogare nel momento
123
in cui vengono calcolati i valori associati agli adapter bindings. In questa sezione viene presentato come gli
elementi definiti per ogni strategia di persistenza vengono definiti nel codice generato per l’applicazione.
In merito alle Shared Preferences, il codice da generare è molto ridotto e consiste essenzialmente in una
sola classe, che agisce da proxy verso il servizio nativo di memorizzazione. Il servizio nativo è infatti
mancante di un elemento contenuto nel modello di riferimento: un valore predefinito per una data chiave.
Il proxy quindi ha il ruolo fondamentale di restituire al chiamante il valore di default nel caso in cui una
chiave non esista nella memoria di massa.
In merito alla memorizzazione tramite files, vengono generati due elementi: una classe che agisce come
proxy verso le API esposte da una libreria di terze parti, che a sua volta utilizza l’interfaccia del file system
nativo, e una tabella dei files disponibili, contenente tutte le informazioni necessarie per accedere ad un
dato file. Questo semplifica l’accesso ai files, che può avvenire semplicemente a partire dal loro nome, che
è utilizzato come chiave nell’indicizzazione della tabella dei files disponibili. Il proxy supporta tre operazioni
fondamentali: la lettura dell’intero contenuto di un file, la sovrascrittura dell’intero contenuto di un file e
l’eliminazione di un file. Ciascuna di esse richiede come parametro una riga della tabella dei files.
In merito alla memorizzazione tramite database relazionale, viene generate due classe per ogni entità, più
una classe che svolge funzioni di interazione con la persistenza. Delle due classi generate, la prima contiene
la definizione della tabella in cui i dati sono memorizzati e una serie di metodi di comodo per ottenere
istanze dell’entità dalla memoria persistente in base ad un predicato. La seconda classe svolge un ruolo di
meta-classe, in quanto contiene informazioni in merito ai valori predefiniti dei vari attributi al momento
della creazione di una nuova istanza dell’entità, piuttosto che la logica utilizzata per convertire un’entità in
una query SQL di aggiornamento del database (logica necessaria per la persistenza delle modifiche). Non
esiste alcuna classe che modelli l’entità: questo è necessario in quanto gli oggetti gestiti dallo state layer, in
base alla documentazione di Redux, devono essere oggetti JavaScript puri, senza ereditarietà prototipica. Il
proxy ha cura di mantenere la connessione con il database, inizializzare le tabelle al primo avvio
dell’applicazione e inviare al database stesso le query generate dalla logica applicativa.
In merito infine alla memorizzazione tramite database cloud, viene generata solo una classe generale, che
fornisce funzioni di interfaccia con la libreria (di terze parti) che gestisce la connessione con il database vero
e proprio. Infatti, il database cloud memorizza gli oggetti come un albero JSON, il cui contenuto è
nativamente utilizzabile in JavaScript. Per i motivi già esposti parlando del database relazionale, non è
possibile definire una classe per ogni categoria di oggetti memorizzati nel database cloud. Gli oggetti
ottenuti dal database vengono utilizzati così come sono, ovvero come puri oggetti JavaScript. L’accesso al
database cloud avviene in modo simile all’accesso alle default preferences, ovvero utilizzando una chiave
che, in questo caso, è il path del sottoalbero che la logica applicativa chiede di importare.
Dato che tutte le interazioni con la persistenza, sia essa di massa o in cloud, richiedono operazioni
asincrone, lo state layer fornisce un meccanismo di cache delle risposte: quando la logica applicativa
richiede dati ad una strategia di persistenza, viene dapprima controllata la cache locale. In caso di cache hit,
i valori vengono ritornati in modo sincrono, mentre in caso di cache miss viene effettuata una richiesta
asincrona alla memoria di massa o al cloud. Quando la memoria risponde con i dati, la cache viene
aggiornata e i dati vengono ritornati al chiamante. I dati copiati nella cache sono chiamati copia locale,
mentre quelli memorizzanti in memoria di massa o nel cloud sono chiamati copia persistente. Nel momento
in cui la logica applicativa prevede la modifica di qualche dato, la copia locale viene aggiornata in modo
sincrono, in modo da ottenere una user experience più fluida. Una volta aggiornata la copia locale, si
124
procede all’aggiornamento asincrono della copia persistente, in modo da mantenere la coerenza della
cache con il modello persistito. In caso di cancellazione di dati invece l’ordine delle operazioni è simile alle
operazioni di lettura: prima viene eliminata la copia persistente, e, una volta completata l’operazione
asincrona, viene eliminata la copia locale. Questo evita che vi siano glitch a livello di interfaccia utente o di
interazione tra state layer e scene model layer.
125
6 Valutazione Questa sezione contiene una valutazione dell’efficacia dell’approccio implementato dalla toolchain formata
da Protocode e MobileCodeGenerator alla generazione di applicazioni mobili cross-platform. Per questa
valutazione verrà prodotta una semplice applicazione effettuando alcune misurazioni sia a livello di
processo sia a livello di prodotto. In particolare, verranno esaminati la distribuzione del tempo necessario a
generare l’applicazione rispetto ai vari task necessari, la percentuale di codice generato rispetto al codice
completo, la qualità del codice generato e le principali limitazioni di questo approccio.
Pur essendo l’applicazione di test molto semplice, essa consente di esercitare le principali funzionalità della
toolchain e di evidenziarne i principali limiti in particolare per quanto riguarda la componente controller,
che costituisce la maggiore innovazione di questo lavoro di tesi.
Al fine di eseguire valutazioni il più possibile realistiche, il caso di test è stato selezionato considerando
alcune funzioni di un’applicazione precedentemente realizzata per un caso reale. In questo modo è
possibile evitare aberrazioni nei risultati che deriverebbero dall’utilizzo di una applicazione progettata ad
hoc per questo tipo di valutazioni.
Non verranno effettuati confronti tra l’applicazione ReactNative prodotta in questo test e le corrispondenti
applicazioni native in termini di dimensione della codebase in quanto le differenze tra i vari linguaggi
renderebbero tale confronto poco significativo.
6.1 Presentazione del caso di test Per questa valutazione andremo a produrre un’applicazione che assolva le funzioni di un libretto
universitario elettronico. Lo studente che utilizza l’applicazione può inserire diverse carriere universitarie
(una per ciascun titolo di studio conseguito più quella corrente), e per ciascuna carriera può registrare gli
esami che deve sostenere e gli esami che ha sostenuto con i rispettivi voti e pesi in CFU. Da ultimo, può
consultare alcune statistiche relative a ciascuna carriera, quali la media ponderata delle valutazioni, la base
di laurea, il numero di CFU non ancora acquisiti e le proiezioni della sua valutazione se, per i restanti CFU,
prendesse sempre 18 o sempre 30.
La schermata iniziale dell’applicazione simula un login, e chiede semplicemente all’utente di inserire nome
e cognome. Su tali campi è attiva una connessione con il model, che salva i dati inseriti come
sharedPreferences.
126
La prima schermata autenticata visualizzata permette all’utente di selezionare una carriera tra quelle in
corso, o di passare alla schermata per l’aggiunta di una nuova carriera. Gli elementi della lista sono generati
a partire dal model, nella fattispecie dalle entità rappresentanti carriere salvate nel database relazionale
locale. Nella schermata di creazione di una nuova carriera non vi è alcuna connessione con il model in
quanto la carriera in oggetto non è ancora stata inserita nel model stesso. Tale schermata richiede quindi la
scrittura manuale del codice necessario ad espletare la funzione di inserimento di una carriera nel
database. Chiaramente, tale codice si avvantaggia di azioni e reducer già generati allo scopo.
Selezionando una carriera si accede alla scena di dettaglio, che presenta un layout a schede. Una prima
scheda mostra le statistiche sopra citate, una seconda scheda mostra gli esami registrati in quella carriera
(siano essi sostenuti o no) e una terza schermata mostra le date di inizio e fine carriera.
La schermata relativa alle statistiche presenta alcune connessioni con il model, che però vanno rifinite
manualmente. Ad esempio la casella di testo che mostra la media è connessa all’intera lista di esami
registrati nella carriera, ma è necessario scrivere a mano il codice per ridurre tale lista ad un numero.
Questo vale in generale per il calcolo di qualsiasi aggregato presente in questa videata.
La schermata relativa alle informazioni sulla carriera presenta due connessioni con il model, una per
ciascuna data mostrata. Se la carriera è in corso, la data finale viene omessa e viene mostrato il pulsante
per terminare la carriera. La logica di funzionamento di tale pulsante non è generabile automaticamente.
127
La schermata relativa agli esami ha una connessione tra gli elementi della lista e il model: ogni cella è
generata a partire da un oggetto che rappresenta un esame nel database relazionale. Selezionando una
cella si accede alla schermata di dettaglio dell’esame, che mostra il titolo e l’eventuale voto o dà la
possibilità di inserire il voto stesso. Di nuovo, la logica di inserimento del voto richiede un intervento del
programmatore, ma può avvantaggiarsi per gran parte di codice automaticamente generato.
Da ultimo, è presente una schermata per la registrazione di un nuovo esame. Tale schermata è analoga a
quella per l’inserimento di una nuova carriera, e presenta quindi gli stessi problemi.
6.2 Produzione dell’applicazione A seguito della progettazione dell’applicazione in termini di struttura dell’interfaccia utente, modello di
persistenza dei dati e definizione della navigazione, è stato possibile procedere all’attività di
prototipizzazione vera e propria. Tale attività è stata attentamente cronometrata in modo da identificare i
task di cui essa è composta e la durata relativa di ciascuno di essi, in modo da poter evidenziare quali parti
sono più critiche di altre. Sono stati prototipizzati, nell’ordine, il model, la view e il controller. Da tale
conteggio sono esclusi la protipizzazione del manifest in quanto di durata trascurabile e la prototipizzazione
dell’applicazione per smartwatch in quando non è possibile generare applicazioni per smartwatch
utilizzando ReactNative, e quindi sarebbe stata un’attività poco significativa per il seguito dell’analisi.
128
Al termine dell’attività di prototipizzazione è stato scaricato il file XMI contenente il modello strutturato.
Tale file è stato utilizzato come input per MobileCodeGenerator in modo da produrre il codice sorgente
vero e proprio. A questo punto è stato effettuata una prima valutazione della dimensione della codebase
relativa a ciascun layer dell’applicazione ottenuta. Tale conteggio è stato omesso dall’analisi temporale. Lo
strumento utilizzato per il conteggio è cloc.
Il passaggio successivo è stato andare a completare le parti dell’applicazione che non è stato possibile
modellare in Protocode o generare automaticamente con MobileCodeGenerator. Tali parti riguardano il
passaggio di parametri durante la navigazione, la gestione delle scene inerenti l’inserimento di nuove entità
nel database, la computazione degli aggregati, la gestione del rendering condizionale di alcuni componenti.
Alla fine, l’applicazione è stata installata sul device e verificata nel suo funzionamento. Si è poi completa
l’analisi con un conteggio finale della dimensione della codebase relativa a ciascun layer.
Il processo è stato eseguito su un Asus F556UJ equipaggiato con processore Intel i5 di 6a generazione
@2.30GHz e 12GB RAM, sistema operativo Windows X.
Nell’analisi del processo va considerata l’esperienza nell’utilizzo di Protocode maturata durante il suo
sviluppo, che per certo riduce parzialmente i tempi di prototipizzazione.
6.3 Analisi dell’applicazione generata
6.3.1 Analisi del tempo impiegato
Figura 6.1 Analisi del tempo impiegato per lo sviluppo dell'app di valutazione
Questo diagramma mostra la distribuzione del tempo impiegato allocata ai vari task svolti. L’attività
preponderante da un punto di vista temporale è la prototipizzazione dell’applicazione, con un tempo
relativo del 44.59%, sottostimato a causa dell’esperienza acquisita nell’uso di Protocode durante lo
sviluppo. La seconda attività per importanza è il completamento manuale dell’applicazione, che richiede il
40.54% del tempo. Tale numero verrà giustificato in seguito dall’analisi qualitativa dell’applicazione
129
generata. L’efficienza di MobileCodeGenerator è dimostrata dall’ 1.35% richiesto per la sua attività. Il resto
del tempo è speso in attività automatiche di installazione di dipendenze, compilazione e installazione. La
durata di tali attività tuttavia è indipendente dall’approccio scelto per la produzione dell’applicazione, ed è
quindi poco significativa da analizzare.
6.3.2 Analisi quantitativa della generazione del codice
La seguente tabella mostra la dimensione della codebase relativa a ciascun layer dell’applicazione prodotta,
calcolata prima e dopo le modifiche effettuate manualmente.
Generated Final
Data access layer 291 291 State layer 1795 1795 Scene model and scene layers 1949 2024 View controller layer 1897 1973 TOTAL 5932 6083
Tabella 6.1 Dimensione della codebase dell'app di valutazione
In questo conteggio non è presente il Data Layer in quanto implementato con librerie native la cui
dimensione è invariante rispetto a questa analisi, e sono stati fusi Scene Model Layer e Scene Layer in
quanto intaccati, in questa analisi, per gli stessi motivi.
Un primo approccio di analisi black box rileva una differenza, nella codebase totale, di sole 150 righe
aggiuntive, pari a circa il 2.4% e imputabili solamente a modifiche relative ai layer più alti nella gerarchia.
Un secondo livello di analisi, più preciso, rileva grazie ad un algoritmo di calcolo delle differenze, modifiche
per un totale di circa 250 righe, pari al 4% del totale.
Si nota immediatamente che il Data Access Layer e lo State Layer non hanno subito modifiche. Se per Data
Access Layer questo è comprensibile data la definizione precisa e ristretta del ruolo di tale layer, per lo
State Layer non è scontato, ed è possibile solamente grazie al fatto che l’applicazione di test non si
interfaccia con alcuna API esterna ad essa (non è considerata API esterna l’interfaccia con il database
cloud). Qualora vi fosse la necessità di introdurre una interazione con un application server, tale interazione
andrebbe inserita a livello di State Layer.
La percentuale di codice generata è molto elevata, e ciò è possibile anche grazie alla semplicità
dell’applicazione di valutazione (si veda il capitolo relativo alle limitazioni). Tuttavia, è possibile concludere
che anche aggiungendo funzionalità critiche per la toolchain considerata, la percentuale di codice resta
elevata, superiore al 90%. Questo dimostra l’efficacia della toolchain presentata in questo lavoro di tesi.
6.3.3 Analisi qualitativa del codice generato
I template su cui si basa la generazione del codice sono stati redatti a partire dalle indicazioni rilasciate dai
produttori delle librerie su cui esso si appoggia, in primis Redux, React e ReactNative, e dall’esperienza della
community online (principali fonti sono il blog di Dan Abramov, il creatore di Redux, e stackoverflow.com, la
più grande community di sviluppatori online). Dal punto di vista della strutturazione la qualità del codice è
molto elevata. La problematica maggiore dal punto di vista della qualità del codice generato risiede nello
scarso contenuto semantico degli identificativi. Per evitare problemi dovuti a conflitti di nome, i nomi
utilizzati nel codice sono sempre creati a partire dall’identificativo univoco e casuale del controllo a cui si
riferiscono, a cui viene concatenato un prefisso o un suffisso a seconda dei casi. Questo fa sì che sia chiaro il
ruolo di una proprietà, ma non il controllo a cui essa è riferita. Nel caso in cui un ruolo sia valido per più
elementi di una interfaccia, la semantica povera dei nomi rende difficile modificare il codice manualmente
130
dopo la generazione. Ciò spiega, almeno in parte, la quantità di tempo necessaria alla modifica manuale del
codice. Di contro, i moderni ambienti di sviluppo offrono quasi sempre funzionalità di rename intelligente,
quindi non è difficile, per un programmatore, modificare gli identificativi automatici con nomi dal maggior
contenuto semantico. Questo problema quindi si presenta solo nel caso di view controller particolarmente
ricchi di controlli, e anche in tale caso è facilmente risolvibile grazie ai moderni IDE.
6.3.4 Limitazioni
La versione attuale di Protocode e MobileCodeGenerator presenta diverse limitazioni, alcune delle quali
potrebbero essere colmate in futuri lavori di tesi. In questa sezione vengono riportate le principali:
Calcolo di aggregati
Il calcolo di valori aggregati a partire da una lista di oggetti, siano essi oggetti cloud, entità o righe di
un file, non può essere modellato in Protocode. È possibile estendere l’interfaccia di definizione dei
ModelConnectors in modo da generare automaticamente alcuni aggregati. Tuttavia, tale approccio
sarebbe comunque limitante rispetto alle potenzialità di un codice scritto direttamente, e un design
grafico degli algoritmi finirebbe per essere poco pratico e poco conveniente da un punto di vista
temporale
Selezione di oggetti nei modelli
La versione attuale di Protocode permette di definire la forma di un scene model, ma non le
specifiche istruzioni per costruirlo. Non è quindi possibile specificare l’identificativo dell’entità di
interesse, o un criterio di filtro per la creazione di una lista di entità. Tali criteri vanno specificati
modificando apposite righe nel codice generato da MobileCodeGenerator. Tali righe sono
contrassegnate da un apposito commento nel codice generato al fine di favorire la loro
identificazione. È teoricamente possibile estendere Protocode e MobileCodeGenerator per
implementare i casi più semplici di tali predicati di selezione, per quanto la potenza espressiva di
tale implementazione sarebbe senza dubbio inferiore alla libertà della specifica, via codice, di tali
predicati.
Specifica dei parametri di navigazione
La versione attuale di Protocode si basa su un modello piuttosto elementare di navigazione. Tale
modello consente esclusivamente la specifica della destinazione di un’azione di navigazione dato il
contesto di partenza. Un modello migliore permetterebbe la specifica di parametri da passare al
destinatario dell’azione di navigazione. Tale modello permetterebbe ad esempio di passare alla
schermata di destinazione di un’azione di navigazione avente origine in un elemento di una lista
l’elemento del modello utilizzato per generare l’elemento della lista stessa. E’ possibile estendere
MobileCodeGenerator e Protocode in modo da permettere il passaggio di elementi del scene model
durante le azioni di navigazione. Tale estensione tuttavia esula dagli scopi di questo lavoro e viene
lasciata per estensione futura.
Interazione con API
Eccetto per l’interazione con le API di Firebase Realtime Database, la versione attuale di Protocode
e MobileCodeGenerator non permette la specifica dell’interazione con API esterne. L’estensione di
tali strumenti è possibile per quanto riguarda l’interazione con API di tipo REST: esattamente come
viene prototipizzata la forma del modello, è possibile introdurre una prototipizzazione delle risorse
gestite tramite REST API. Tale prototipizzazione, se include anche la definizione dell’URL di ciascuna
risorsa, consentirebbe la generazione automatica del codice di interfaccia con l’API REST grazie alla
struttura ben nota di tale schema. In generale tuttavia la generazione di codice di interazione con
un API di struttura non standard richiede uno sforzo di prototipizzazione complesso, che
renderebbe l’utilizzo di Protocode poco pratico ed efficiente.
131
La struttura a layer dell’applicazione e la modalità di definizione delle interfacce tra i vari layer sono state
progettate per essere facilmente adattabili alle esigenze delle diverse applicazioni, in particolare per
quanto riguarda il superamento di tali limitazioni.
132
7 Conclusioni Lo sviluppo degli strumenti presentati in questo lavoro di tesi e l’attività di valutazione svolta mostrano
pregi e limitazioni di uno sviluppo model - driven di applicazioni mobili cross - platform. Sicuramente, gli
strumenti precedentemente implementati costituivano un ottimo punto di partenza la cui efficacia nella
generazione delle componenti Model e View era già stata dimostrata dai lavori di tesi dei precedenti
sviluppatori. Questo progetto, oltre ad aggiornare ed attualizzare i due strumenti, ha aggiunto la possibilità
di modellare parte della componente Controller, ovvero parte della logica applicativa, ed introdotto la
generazione di codice cross-platform per ReactNative. Per quanto riguarda le estensioni al modello, esse
permettono allo sviluppatore di dedicare il proprio tempo a quegli aspetti della logica applicativa che
richiedono maggiori competenze grazie all’automazione della generazione delle funzionalità più semplici e
più standard da un punto di vista degli approcci adottati. Esempio principe di funzionalità di questa famiglia
è la generazione delle connessioni da View a Model, mediate dal Controller, per l’aggiornamento del model
stesso in base a informazioni introdotte dall’utente attraverso la View stessa. Per quanto riguarda la
generazione di codice cross-platform invece, l’adozione di un approccio non nativo consente di unificare lo
sviluppo per piattaforme diverse in un unico progetto, con conseguente miglioramento nell’utilizzo del
tempo e delle risorse.
L’approccio model driven alla base di entrambi gli strumenti è evidentemente vantaggioso, in quanto
consente una buona compressione dei tempi di sviluppo grazie all’efficace riuso dell’attività di design
dell’applicazione stessa. Rispetto ad uno strumento di prototipizzazione tradizionale, Protocode è
difficilmente utilizzabile al massimo delle sue potenzialità da un team puramente dedicato alla
progettazione di interfacce e interazioni con l’utente (e quindi dotato di competenze attinenti all’area del
design più che all’area dell’implementazione), ma in caso di sinergie tra team di design e team di sviluppo è
possibile utilizzare anche gli strumenti dedicati alla prototipizzazione di Model e Controller, con il fine di
produrre un modello il più ricco e completo possibile. La completezza del modello rispetto ai requisiti
comporta una maggior efficacia delle generazione del codice (efficacia misurabile come rapporto tra la
quantità di codice generato automaticamente e la quantità di codice totale dell’applicazione completa).
Certo è che l’efficacia dell’approccio basato su modelli non può raggiungere il 100% se non in casi
intenzionalmente semplici. Infatti non è possibile includere nella modellazione la logica applicativa, che
costituisce il cuore stesso dell’applicazione. L’inclusione della possibilità di modellare dettagliatamente la
logica applicativa, oltre a portare un significativo aumento della complessità d’uso degli strumenti
presentati, è un’estensione non migliorativa dal punto di vista dell’efficacia temporale: la definizione grafica
di un qualsiasi algoritmo della logica applicativa richiede infatti un tempo maggiore rispetto
all’implementazione da parte di un programmatore. Ciò non toglie tuttavia la possibilità di automatizzare in
parte la generazione della logica applicativa, almeno per quelle componenti che presentano una struttura
fissa e ripetitiva: le connessioni tra eventi di modifica di dati di tipi elementari nella vista e il
corrispondente aggiornamento del modello. Questo lavoro di tesi dimostra infatti la possibilità di generare
automaticamente il codice che implementa tali operazioni, utilizzando pattern largamente supportati dalla
community ed accuratamente analizzati. Da un punto di vista dell’efficacia dell’approccio model driven è
quindi possibile affermare che esso costituisce un’ottima soluzione per comprimere i tempi di sviluppo, ma
che non è possibile generare automaticamente un’applicazione completa, se non in casi molto semplici.
L’intervento dello sviluppatore è comunque richiesto solo per aspetti centrali della logica applicativa, e
questo consente un focus ottimale dello sviluppatore e un utilizzo ottimale del tempo.
La generazione di codice cross-platform rispetto alla generazione di codice nativo presenta aspetti positivi e
negativi. È noto e intuitivo che lo sviluppo di un unico progetto cross-platform, rispetto allo sviluppo
133
autonomo di due progetti nativi, sia vantaggioso sia da un punto di vista dei tempi e delle risorse
(soprattutto umane) richiesti, sia da un punto di vista delle skill che il team di sviluppo deve possedere: la
conoscenza del linguaggio JavaScript, strumento principe degli approcci cross-platform, è sicuramente più
diffusa rispetto alla conoscenza del linguaggio Swift, ad esempio. In questo lavoro di tesi è stato anche
dimostrato come la piattaforma utilizzata (React Native) non sia, attualmente, confrontabile con un
approccio nativo in termini di capacità espressiva: l’eccezione più evidente alla loro equivalenza è
rappresentata dall’impossibilità di utilizzare efficacemente il multithreading, fondamentale in molti contesti
per ragioni di performance e responsività dell’interfaccia grafica. In conclusione, un approccio cross-
platform è indicato, allo stato attuale di sviluppo degli strumenti, per applicazioni che non svolgono
operazioni costose da un punto di vista computazionale o di utilizzo della rete, quali, ad esempio, algoritmi
complessi (dal punto di vista della complessità astratta di un algoritmo, non della sua implementazione),
upload e download di file di dimensione considerevole. È possibile che in futuro una maturazione del
framework risolva questa disparità espressiva e consenta uno sviluppo più efficace.
La validità degli strumenti presentati è stata verificata con un singolo caso di test studiato per esercitare le
diverse funzionalità, ma comunque piuttosto semplice per avere un buon focus sulle feature introdotte da
questo lavoro di tesi. Questa attività di verifica andrebbe ampliata tramite l’utilizzo intensivo degli
strumenti presentati nello sviluppo di applicazioni diverse. Questo consentirebbe di evidenziare al meglio i
punti sia di forza che di debolezza dell’approccio presentato. È comunque evidente l’unicità dell’approccio
presentato da questo progetto, così come la sua utilità nella generazione di codice ripetitivo ed incline a
presentare difetti di implementazione (difetti che spesso nascono dalla superficialità di
un’implementazione ripetitiva). In un mercato in cui la competitività è molto alta, la rapidità e la precisione
garantite da questi strumenti possono rappresentare un punto di forza per un’azienda. Inoltre, tali
strumenti favoriscono l’avvicinamento al mondo delle mobile apps anche da parte di chi non ha una
preparazione completa nel settore. Una conoscenza di base, anche acquisita attraverso lo sviluppo web, è
sufficiente a poter utilizzare Protocode e MobileCodeGenerator, soprattutto utilizzando ReactNative come
framework di destinazione. Dal punto di vista invece delle performance, sebbene l’applicazione sia alla fine
prodotta utilizzando il linguaggio JavaScript, si può affermare che esse siano comparabili con le prestazioni
native grazie al motore di rendering fornito da ReactNative: la View, sebbene descritta utilizzando il
linguaggio JavaScript utilizza controlli nativi a tutti gli effetti, semplicemente composti da un orchestatore
non nativo. Questo consente di ottenere una maggiore fluidità nell’interazione con l’utente grazie alle
performance garantite dalle implementazioni native stesse.
Il bacino di utenza degli strumenti presentati contiene sia sviluppatori alle prime armi, che possono trovare
in Protocode e MobileCodeGenerator un valido aiuto nella realizzazione di pattern complessi o comunque
delicati, sia sviluppatori esperti, che possono trarne vantaggio generando uno scheletro dell’applicazione
completo di tutte le parti più ripetitive e concentrarsi così solamente sugli aspetti più critici della logica
applicativa. Per un’azienda, questo si traduce nel poter destinare personale con competenze critiche a
compiti per cui tali competenze siano effettivamente richieste, e a demandare la produzione del codice di
base ad uno strumento automatico utilizzabile anche dal team che si occupa di design, prototipizzazione e
studio della user experience, eventualmente coadiuvato da sviluppatori con meno esperienza.
7.1.1 Sviluppi futuri
In un settore come quello informatico, e ancor più nel campo delle applicazioni mobili, l’evoluzione degli
strumenti, degli standard e dei framework è estremamente rapida. Questo si traduce nella necessità di
produrre continui aggiornamenti sia per offrire nuove features che per adottare tecnologie nuove, più
efficaci e più performanti. Un fronte su cui certamente MobileCodeGenerator è chiamato ad evolvere è
134
quello dei linguaggi e dei framework di destinazione. Google ha già rilasciato il linguaggio Kotlin per
Android, e potrebbe essere sicuramente molto interessante valutare come l’adozione di questo linguaggio
possa modificare la performance della generazione del codice e le funzionalità generabili automaticamente.
Dal punto di vista delle funzionalità offerte, entrambi i tools potrebbero essere migliorati aggiungendo il
supporto a parti del controller che nella versione attuale sono solamente inserite in bozza, come ad
esempio il passaggio di parametri nella navigazione o la persistenza dei dati utilizzando API REST. Un
ulteriore step potrebbe essere l’introduzione della modellazione esplicita della navigazione, attraverso
l’aggiunta di un editor dedicato alla struttura della navigazione stessa.
Infine, per quanto riguarda l’architettura software, potrebbe essere interessante ridurre le dipendenze del
progetto integrando il generatore di codice nell’app Protocode. La tecnologia su cui si basa
MobileCodeGenerator è infatti datata e poco documentata nelle ultime revisioni di Eclipse, e lo stesso
Eclipse è una dipendenza piuttosto scomoda per il progetto. Esistono ad oggi diversi strumenti che
consentono l’espansione di templates direttamente in Javascript. Questo eliminerebbe la necessità di
esportare il modello in formato XML, in quanto il generatore potrebbe appoggiarsi direttamente allo stato
dell’applicazione Protocode, e sposterebbe l’esecuzione del workflow da Java al browser, meno efficiente
ma certamente meno pesante dal punto di vista delle risorse richieste, data la semplicità concettuale del
processo di espansione di un template.
135
8 Bibliografia
[1] We Are Social, «Global Digital Report 2019,» [Online]. Available: https://wearesocial.com/global-
digital-report-2019. [Consultato il giorno 21 03 2019].
[2] Gartner, «Newsroom,» 08 2018. [Online]. Available: https://www.gartner.com/en/newsroom/press-
releases/2018-08-28-gartner-says-huawei-secured-no-2-worldwide-smartphone-vendor-spot-
surpassing-apple-in-second-quarter. [Consultato il giorno 21 03 2019].
[3] G. Perego e S. Pezzetti, Un approccio model-driven per lo sviluppo di applicazioni mobili native, Master
thesis, Politecnico di Milano, 2013.
[4] M. Natali, Prototipizzazione rapida per applicazioni mobili multipiattaforma, Master thesis, Politecnico
di Milano, 2014.
[5] A. Pintus, Generazione automatica di applicazioni mobili native secondo un approccio model-driven,
Master thesis, Politecnico di Milano, 2016.
[6] A. Rossotti, Prototipizzazione e generazione automatica della componente Model per applicazioni
mobili multipiattaforma, Master thesis, Politecnico di Milano, 2017.
[7] «Proto.io,» [Online]. Available: https://proto.io/.
[8] «Sketch,» [Online]. Available: https://www.sketch.com/.
[9] «PhotoShop,» [Online]. Available: https://www.adobe.com/Photoshop.
[10] «MarvelApp,» [Online]. Available: https://marvelapp.com/.
[11] «Adobe Experience Design CC,» [Online]. Available: https://www.adobe.com/it/products/xd.html.
[12] «Xamarin,» [Online]. Available: https://visualstudio.microsoft.com/it/xamarin/.
[13] «Microsoft,» [Online]. Available: https://www.microsoft.com/it-it.
[14] «Visual Studio,» [Online]. Available: https://visualstudio.microsoft.com/it/.
[15] «PhoneGap,» [Online]. Available: https://phonegap.com/.
[16] «Adobe,» [Online]. Available: https://www.adobe.com/.
[17] «Apache Cordova,» [Online]. Available: https://cordova.apache.org/.
[18] «Appcelerator Titanium,» [Online]. Available: https://www.appcelerator.com/.
[19] «React Native,» [Online]. Available: https://facebook.github.io/react-native/.
136
[20] «DecoIde,» [Online]. Available: https://www.decoide.org/.
[21] «Android Studio,» [Online]. Available: https://developer.android.com/studio.
[22] «Apple XCode,» [Online]. Available: https://developer.apple.com/xcode/.
[23] E. Umuhoza, «Domain-specific modeling and code generation for cross-platform multi-device mobile
apps,» CoRR, 2015.
[24] E. Umuhoza e M. Brambilla, «Model driven development approaches for mobile applications: A
survey,» in Mobile Web and Intelligent Information Systems - 13th International Conference, MobiWIS
2016, Vienna, Austria, 2016.
[25] E. Umuhoza, M. Brambilla, H. Ed-douibi, J. Cabot e A. Bongio, «Automatic code generation for cross-
platform, multi-device mobile apps: Some reflections,» in Proceedings of the 3rd International
Workshop on Mobile Development Lifecycle, New York, NY, USA, 2015.
[26] «ReactJS,» [Online]. Available: https://reactjs.org/.
[27] «Yarn,» [Online]. Available: https://yarnpkg.com/lang/en/.
[28] Redux. [Online]. Available: https://redux.js.org/.
[29] «Bootstrap,» [Online]. Available: https://getbootstrap.com/.
[30] «React Router,» [Online]. Available: https://reacttraining.com/react-router.
[31] «FontAwesome,» [Online]. Available: https://fontawesome.com/.
[32] «FileSaver,» [Online]. Available: https://github.com/eligrey/FileSaver.js/.
[33] «XML Beautifier,» [Online]. Available: https://www.npmjs.com/package/xml-beautifier.
[34] «UniqId,» [Online]. Available: https://www.npmjs.com/package/uniqid.
[35] «Babel JS,» [Online]. Available: https://babeljs.io/.
[36] «Eclipse Modeling Framework,» [Online]. Available: https://www.eclipse.org/modeling/emf/.