BLOC SVILUPPO DI UN'APP IOS/ANDROID IN FLUTTER CON STATE MANAGEMENT

Pagina creata da Giulio Nanni
 
CONTINUA A LEGGERE
BLOC SVILUPPO DI UN'APP IOS/ANDROID IN FLUTTER CON STATE MANAGEMENT
U NIVERSITÀ DEGLI S TUDI DI NAPOLI F EDERICO II

          S CUOLA P OLITECNICA E DELLE S CIENZE DI BASE
D IPARTIMENTO DI I NGEGNERIA E LETTRICA E T ECNOLOGIE DELL’I NFORMAZIONE
               C ORSO DI L AUREA IN I NGEGNERIA I NFORMATICA
               T ESI DI L AUREA IN I NGEGNERIA DEL S OFTWARE

 S VILUPPO DI UN ’A PP I OS/A NDROID IN
  F LUTTER CON S TATE M ANAGEMENT
                 BL O C

Relatore                                                         Candidato
Ing. Roberto P IETRANTUONO                           Enzo Manuel M ANGANO
                                                                 N46/3381

                         Anno Accademico 2018–2019
BLOC SVILUPPO DI UN'APP IOS/ANDROID IN FLUTTER CON STATE MANAGEMENT
Indice

1   Introduzione a Flutter                                                                                     4
    1.1   Cos’è Flutter? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .   4
    1.2   Widgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    5
          1.2.1   Stateless Widget . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     5
          1.2.2   Stateful Widget . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    5
    1.3   Dart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .   6
    1.4   Perché Flutter è basato su Dart? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     6
    1.5   Material Design (Quantum Paper) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .        7
    1.6   Flutter vs React Native . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    7

2   State Management                                                                                           8
    2.1   Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .   8
    2.2   setState . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .   9
    2.3   StatefulBuilder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    9
    2.4   InheritedWidget & Scoped Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
    2.5   RxDart + BehavoirSubject + GetIt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
    2.6   BLoC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
    2.7   Redux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

3   MovieMentor                                                                                                18
    3.1   Descrizione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
          3.1.1   Use Case Diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
    3.2   Architettura del Progetto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
          3.2.1   Struttura Server Side . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
          3.2.2   Struttura Client Side . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
    3.3   User Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
          3.3.1   Logo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
          3.3.2   Schermate App MovieMentor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
    3.4   Server Side . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
          3.4.1   Omologazione API Canali TV sotto un formato unico . . . . . . . . . . . . . . . . 23
          3.4.2   Inserimento dati nel Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
          3.4.3   MongoDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

                                                       1
BLOC SVILUPPO DI UN'APP IOS/ANDROID IN FLUTTER CON STATE MANAGEMENT
3.4.4   Algoritmo sulla scelta del Film/SerieTv . . . . . . . . . . . . . . . . . . . . . . . . 26
      3.4.5   Invio delle notifiche e memorizzazione . . . . . . . . . . . . . . . . . . . . . . . . 30
      3.4.6   node-fcm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
      3.4.7   Creazione API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.5   MovieMentor - Front End . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
      3.5.1   Bloc basati su Servizi Esterni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
      3.5.2   Bloc non basati su Servizi Esterni . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

                                                  2
BLOC SVILUPPO DI UN'APP IOS/ANDROID IN FLUTTER CON STATE MANAGEMENT
Introduzione

Negli ultimi anni il concetto di applicazione mobile si è evoluto in modo a dir poco incredibile.
Se prima le app mobile rappresentavano esclusivamente un’estensione opzionale del nostro cellulare, oggi
ne sono a tutti gli effetti la caratteristica principale.
La continua evoluzione delle “app” ha dato il via ad una rivoluzione inarrestabile che ha segnato la nascita
di nuove figure lavorative: app developer e app designer.
Da quel momento le tecnologie nate per lo sviluppo di applicazioni mobile sono aumentate esponenzial-
mente. Oggi, data la vastità di queste nuove tecnologie sempre in continuo aggiornamento, il concetto di
“app developer” è diventato fin troppo generico. La figura richiesta dal mercato, non è più solo quella di
uno sviluppatore in grado di progettare una nuova “app”, ma di uno sviluppatore in grado di aggiornarla
continuamente e manutenerla nel tempo. Dato che per definizione, sviluppare un’app significa paradossal-
mente svilupparne due (iOS e Android), aggiornarla significa modificare parallelamente le funzionalità di
due codici totalmente diversi tra loro. Queste caratteristiche aumentano moltissimo i costi che le aziende
devono sostenere per la produzione di app, rendendo spesso estremamente difficile la possibilità di rivolgersi
a due mobile OS (Operating System) sul lungo termine. È da queste esigenze che è nato il concetto di app
cross-platform (indipendenti dalla piattaforma). Questa tesi mira a studiare nel dettaglio lo sviluppo di app
attraverso Flutter, una tecnologia che negli ultimi tempi è riuscita ad emergere quasi grazie allo stesso slogan
che determinò il successo di Java: “write once, run anywhere”.
La tesi si suddivide in tre capitoli. Il primo capitolo cerca di fornire una prospettiva dall’alto dei vantaggi
principali che offrono Flutter e Dart 1 , introducendo anche simbolicamente il Material Design e la rivali-
tà tra Flutter e React Native. Il secondo capitolo ha invece lo scopo di descrivere tutti i possibili pattern
architetturali utilizzabili nello sviluppo cross-platform, con particolare attenzione verso il BLoC (Business
Logic Component). Flutter rispecchia un’architettura EDA (Event Driven Architecture) che rompe brusca-
mente ogni legame con la logica architetturale dettata per anni dal Model View Controller nel mondo delle
app. Infine nel terzo capitolo ho deciso di commentare la progettazione e l’implementazione in Flutter e
NodeJS dell’app che ho sviluppato per questa tesi: MovieMentor. Lo scopo di questo studio è quello di
analizzare l’effettiva maturità che il mondo delle app platform-independent ha acquisito con Flutter e BLoC
(il principale pattern architetturale).

   1 Il   linguaggio di programmazione alla base di Flutter.

                                                               3
BLOC SVILUPPO DI UN'APP IOS/ANDROID IN FLUTTER CON STATE MANAGEMENT
Capitolo 1

Introduzione a Flutter

1.1     Cos’è Flutter?
Flutter è uno UI Framework Open-Source e Cross Platform lanciato da Google nel Maggio 2017 e nato per
realizzare App mobile, web e desktop da una singola codebase.
Gli aspetti chiave su cui si basa Flutter sono:

   • Hot Reload: dal punto di vista dello sviluppatore, Flutter offre uno sviluppo delle app più dinamico
      e veloce. Gli sviluppatori possono effettuare al volo modifiche al codice sorgente e vedere quasi in
      real-time le modifiche sull’app. Questa funzione aiuta i team ad aggiungere funzionalità, correggere
      bug e sperimentare nuove idee in un istante. In particolare, l’Hot Reload risulta essere molto utile
      quando si tratta di app nate da una collaborazione sviluppatore-designer.

   • Una Codebase, Due Mobile Platform: con Flutter, gli sviluppatori possono scrivere un solo codice
      base per due applicazioni - sia per iOS che per Android. Flutter è platform-agnostic in quanto ha i pro-
      pri widget, il che significa che è possibile avere esattamente la stessa applicazione su due piattaforme
      (è anche possibile avere comportamenti diversi in relazione al differente OS su cui opera). Inoltre va
      menzionato che Google sta attualmente lavorando anche a "Flutter for Web" e ciò consentirà a breve
      la possibilità di utilizzare un’unica codebase per coprire Android, iOS e Web.
      Questa caratteristica porta anche alla realizzazione del 50% di casi di Test di unità in meno (dovuto al
      fatto di avere una singola codebase, rispetto ad averne due).

   • Performance Native: le app Flutter funzionano in modo fluido e veloce, pur non essendo ottimizzate
      per ogni piattaforma. Ciò succede perché Flutter utilizza la libreria grafica Skia Graphics Library.
      Grazie a Skia, l’interfaccia utente viene renderizzata ogni volta che una View varia (esattamente come
      nei Game Engine). La maggior parte del lavoro viene eseguita su GPU (graphics processing unit);
      ecco perché l’interfaccia utente Flutter è fluida e fornisce 60 fps (frame per second). Tuttavia, è
      necessario fare attenzione durante lo sviluppo, cercando di non renderizzare tutta la View, ma solo la
      porzione interessata. Renderizzare l’intera View invece dei soli elementi grafici che cambiano, può
      influenzare moltissimo le prestazioni e la velocità dell’applicazione.

                                                      4
BLOC SVILUPPO DI UN'APP IOS/ANDROID IN FLUTTER CON STATE MANAGEMENT
1.2     Widgets
L’intera logica di Flutter è costruita sul concetto di Widget. I widget Flutter sono realizzati utilizzando una
struttura moderna che si ispira a React1 . L’idea centrale è che si costruisce l’interfaccia utente attraverso
Widget innestati (che vanno a formare un albero detto Widget-Tree). I widget descrivono come dovrebbe
apparire la loro View, data la loro configurazione (descritta dal metodo Build) ed il loro stato attuale.

1.2.1       Stateless Widget
Ci sono casi in cui si ha bisogno di widget che dipendono esclusivamente dalla propria configurazione
interna: è in questi casi che si introduce uno StatelessWidget.
Questo Widget non dipende in alcun modo dallo Stato dell’app.

                                                                          build

                                                                Widget

                                                    Figura 1.1: Stateless Widget

1.2.2       Stateful Widget
Uno Stateful Widget è un Widget la cui configurazione interna dipende dallo Stato corrente dell’app. Questo
ci consente di creare widget che possono mutare dinamicamente la loro forma nel tempo.
Esempi tipici di possibili eventi che scaturiscono cambiamenti di stato sono:

   1. Input dell’Utente;

   2. Risposta Asincrona di Richiesta Web;

   3. Handling di Gestures;

                                                     build

                                                                 setState
                                              Widget                                State

                                                     Figura 1.2: Stateful Widget

  1 React   è una libreria JavaScript per la creazione di interfacce utente [22].

                                                                      5
BLOC SVILUPPO DI UN'APP IOS/ANDROID IN FLUTTER CON STATE MANAGEMENT
1.3     Dart
Dart è un linguaggio di programmazione nato ed utilizzato da Google per costruire applicazioni web, server
e mobile.
A differenza di altri linguaggi di programmazione, Dart ha un proprio package manager chiamato Pub.
Nonostante sia stato reso noto al grande pubblico dal 2011, Dart ha iniziato ad attirare l’attenzione nel
2017, quando Google ha annunciato ufficialmente Flutter beta per lo sviluppo di applicazioni mobile cross-
platform. Da allora, la popolarità di Dart è drasticamente aumentata andando di pari passo con quella di
Flutter. Le sue caratteristiche principali sono:

   • è un linguaggio fortemente tipizzato.

   • supporta la Programmazione funzionale, imperativa ed orientata agli oggetti.

   • supporta ampiamente la programmazione con async/await.

1.4     Perché Flutter è basato su Dart?
Negli ultimi anni Google ha dato vita a molti nuovi linguaggi di programmazione tra i quali spiccano sicu-
ramente Kotlin e Go.
Vale la pena allora chiedersi perché è stato deciso di realizzare Flutter in Dart?
Ecco una serie di ragioni alla base di questa scelta.

   1. Dart è AOT (Ahead Of Time) compilato in codice nativo veloce e prevedibile, che permette di scrivere
      quasi tutto il Framework in Dart (la restante parte è scritta in C++). Questo non solo rende Flutter
      veloce, ma consente anche una forte personalizzazione (inclusi tutti i widget).

   2. Dart può anche essere compilato JIT (Just In Time) per cicli di sviluppo estremamente veloci (incluso
      il popolare hot-reload di Flutter).

   3. Dart rende più facile creare animazioni e transizioni fluide che girano a 60-120 FPS.

   4. Dart permette a Flutter di evitare un secondo linguaggio di layout dichiarativo separato come JSX o
      XML (del quale fanno uso Java, Kotlin, Swift, Objective-C), o di creare interfacce visive separate.

   5. Dart possiede una sintassi C-like, il che lo rende di semplice apprendimento.

                                                        6
BLOC SVILUPPO DI UN'APP IOS/ANDROID IN FLUTTER CON STATE MANAGEMENT
1.5       Material Design (Quantum Paper)
Il Material Design (o Quantum Paper) è il Design Language che Google ha sviluppato nel 2014, con lo
scopo di fornire al mondo dello sviluppo App un Design universale per gli utenti.
L’idea di Material Design si spinge oltre il concetto di design nativo e cerca di superare le barriere definite
dalle Human Guideline Interfaces di ogni OS mobile.
Il principio di tale design è quello di sfruttare lo spessore del dispositivo per disporre i Materials (oggetti)
assegnando a ciascuno una propria profondità attraverso le ombre. Se da un lato il Material Design si pone
come obiettivo quello di offrire un Design Language universale, dall’altro Flutter si pone quello di offrire
un linguaggio di programmazione universale. Gli scopi di entrambi sono chiaramente molto simili e non
è un caso. Infatti Google ha programmaticamente sviluppato Flutter sui pilastri fondamentali del Material
Design.

1.6       Flutter vs React Native
React Native è un framework JavaScript del 2015 per la scrittura di applicazioni mobile iOS e Android.
L’idea alla base di React Native è quella di sfruttare React - la nota libreria per realizzazione di UI (User
Interface) in ambito Web - nel mondo mobile. L’avvento di React Native ha letteralmente rivoluzionato il
mondo dello sviluppo mobile, grazie all’incredibile intuizione della UI dichiarativa portata avanti da Face-
book. Per quanto React Native e Flutter siano simili, esistono radicali differenze circa le filosofie di entrambi
i progetti. Mentre React Native spinge molto sulla creazione di App dal "native look and feel" (design coe-
rente con la piattaforma utilizzata), Flutter insiste sulla realizzazione di App basate su Material Design
(indipendenti dalla piattaforma). Al giorno d’oggi lo scontro tra Flutter e React Native (rispettivamente
Google e Facebook) è sempre più acceso, con entrambi i progetti che cercano di portarsi dalla loro parte
quanti più mobile developer possibile. Tuttavia la legge del più forte la detta il mercato che - nonostante
Flutter abbia recuperato moltissimo terreno (vedi Figura 1.3) - continua a preferire React Native risultando
essere la scelta più stabile e diffusa. Tra le App realizzate in React Native vale la pena nominare: Facebook,
Instagram, Skype, Testla, Uber.

                        Figura 1.3: Stackoverflow Trends Flutter vs React Native [7]

                                                       7
BLOC SVILUPPO DI UN'APP IOS/ANDROID IN FLUTTER CON STATE MANAGEMENT
Capitolo 2

State Management

2.1     Introduzione
Flutter è uno UI Framework interamente basato sul concetto di stato, il che significa che l’intero “Presenta-
tion Layer” dell’applicativo dipende dallo stato corrente. Per enucleare l’importanza del concetto suddetto
basta ricordare la differenza tra i due Widget fondamentali: Stateless Widget e Stateful Widget.
Tuttavia, Flutter non è l’unico framework a dipendere da questa logica. Infatti, quasi tutti i nuovi Mobile
Hybrid Framework – tra cui React Native e Ionic – rispettano questa funzione. Con la nascita di queste
nuove tecnologie è nata dunque l’esigenza di creare nuovi Pattern di sviluppo dediti alla gestione della UI
in funzione dei cambiamenti di Stato. Questi Pattern sono noti col nome di State Management.
Si cercherà dunque di analizzare i principali State Management utilizzabili in Flutter e quali ruoli svolgano
nell’architettura complessiva di un’applicazione è quello di studiarne l’impiego nello sviluppo di un’app che
funge da Counter.

                                      Figura 2.1: Screen App Counter

                                                     8
BLOC SVILUPPO DI UN'APP IOS/ANDROID IN FLUTTER CON STATE MANAGEMENT
2.2           setState
    Più che uno State Management, il setState è una funzione che, posta in uno State Widget, ricostruisce inte-
    ramente la UI. La ricostruzione completa della UI, cioè il rendering dell’intera schermata, non è un metodo
    conveniente dal punto di vista dell’economicità dei processi coinvolti.
    Cliccando il “+” del Floating Action Button in Figura 2.1, infatti, sarebbe sufficiente renderizzare soltan-
    to il testo invece di tutta la pagina.
    Questa caratteristica lo rende quindi particolarmente svantaggioso per quanto riguarda le performance.
    L’uso di setState risulta inoltre particolarmente dannoso per la scalabilità dell’applicazione. Più che un ap-
    proccio di sviluppo vero e proprio, ricorda il noto “Code & Fix” usato agli albori della programmazione.
    Risulta comunque un metodo valido nel caso di applicazioni realizzate a scopi dimostrativi o didattici.
1         setState (() {
2            _counter++;
3       }) ;

    2.3           StatefulBuilder
    Lo StatefulBuilder è un Widget finalizzato alla risoluzione di problemi relativi alle performance che scatu-
    riscono dal setState (ma non quelli relativi alla strutturazione di un codice scalabile).
    La differenza consiste nel fatto che lo StatefulBuilder consente di poter scegliere, tramite una funzione detta
    “builder”, la parte di UI che necessita di essere ricostruita (in setState viene necessariamente ricostruita
    tutta la UI dell’applicativo).
    Un’ulteriore particolarità dello StatefulBuilder è che può essere utilizzato all’interno di uno StatelessWidget.
1       return StatefulBuilder (
2            builder : (BuildContext context , StateSetter   setState ) {
3                 return ...
4            },
5       );

        NOTA
        Questi due approcci (setState e StatefulBuilder) vengono usati raramente, per le motivazioni già
        enunciate.
        Tuttavia, nell’adoperare tali metodi, non ci si imbatte nei tipici problemi degli State Management.
        Questi sono:

               • comunicazione tra Widget figlio e Widget padre;

               • comunicazione tra Widget figli.

        PS: La comunicazione Padre - Figlio non risulta quasi mai complessa.

                                                               9
2.4       InheritedWidget & Scoped Model
Un InheritedWidget è un Widget in grado di racchiudere un dato accessibile elementarmente da tutti i suoi
Widget Figli. Inoltre, un InheritedWidget possiede una funzione nota come “updateShouldNotify”, la quale
viene chiamata dai Widget Figli al fine di notificare il Widget padre della circostanza di un avvenuto cam-
biamento.
In genere si tende a creare un nuovo Widget che eredita le caratteristiche dell’InheritedWidget, in modo da
poter definire sia il tipo di dato, sia il comportamento della funzione updateShouldNotify.

    NOTA

    Scoped Model è un package di terze parti gestito da Brian Egan (Google Developer). È costruito
    sulla base di un InheritedWidget che offre un modo leggermente migliore per accedere, aggiornare
    e mutare lo stato.
    Permette di passare facilmente un modello di dati da un widget al discendente e ricostruisce anche
    tutti i "children" che usano il modello quando quest’ultimo viene aggiornato.
    Per verificare eventuali modifiche, lo Scoped Model utilizza il metodo notifyListeners();

                   Figura 2.2: Schema di interazione tra Widget Figlio ed InheritedWidget [10]

N.B: Il dato racchiuso nell’inheritedWidget viene iniettato nei Widget Figli mediante Dependency Injec-
tion1 .

    1 La Dependency injection (DI) è un design pattern della Programmazione orientata agli oggetti il cui scopo è quello di semplificare

lo sviluppo e migliorare la testabilità di software di grandi dimensioni.

                                                                  10
2.5       RxDart + BehavoirSubject + GetIt
    Pur essendo di grande semplicità, questo tipo di approccio riesce ad essere notevolmente efficace. L’idea
    che ne sta alla base è quella di creare una Classe Wrapper di un BehaviorSubject (un particolare Widget di
    Flutter).
    L’uso di un BehaviorSubject presenta i seguenti vantaggi:

         • esso ha un comportamento analogo allo Stream in Broadcast (può essere ascoltato da qualsiasi
            Widget dell’applicazione);

         • rende possibile l’accesso al valore corrente dello Stream da qualsiasi parte del codice;

         • facilita l’iniezione di un dato in uno Stream.

          NOTA
          L’uso degli Stream negli State Management è molto frequente; è quindi fondamentale comprendere
          cosa essi siano.
          Per far ciò è utile paragonare lo Stream ad un fiume all’interno del quale scorrono alcuni rami (i
          dati effettivi). Ogni qual volta cambia un determinato dato, viene immesso un nuovo ramo nel fiume.
          Per Stream in Broadcast si intende uno Stream osservabile da N Osservatori (il compito del-
          l’osservatore in Flutter è svolto dallo StreamBuilder) i quali notificano la UI del cambiamento del
          dato nello stream.

    Tenendo a mente la Figura 2.1 si avrà una struttura simile alla seguente.
1   class Counter {
2       BehaviorSubject _counter = BehaviorSubject .seeded(0) ;
3

4       Observable get stream => _counter . stream ;
5       int get current => _counter . value ;
6

7       increment () { _counter .add( current +1); }
8       decrement() { _counter .add( current −1); }
9   }

    Essendo lo stream in Broadcast, basterà porre un StreamBuilder (osservatore dello stream) in un Widget e
    costruire la UI in relazione allo stato corrente.
    Risulta allora chiaro che nell’accedere allo Stream si passerà per un’istanza della Classe Counter. Tale
    istanza dovrà quindi essere unica (nel nostro caso il Counter è unico). Per poter soddisfare questo requisito
    si possono seguire due diversi percorsi:

         1. creare Counter come Singleton;

         2. importare la libreria GetIt.

    Mentre il primo approccio implica i vantaggi e gli svantaggi del Singleton (semplice ma non facilmente
    testabile), il secondo invece offre la possibilità di iniettare il Counter con Dependency Injection (dipendendo
    però da una libreria di terze parti).

                                                            11
NOTA
    Se dipendere da una libreria di terze parti non è mai troppo vantaggioso, in Flutter lo è ancora meno.
    Dati i continui aggiornamenti è molto facile ritrovarsi con una libreria non più adeguata.

2.6        BLoC
Il BLoC (Business Logic Component) è lo State Management consigliato da “Google Developers” per
la realizzazione di applicazioni in Flutter. È estremamente scalabile ma non semplice quanto l’approccio
RxDart + BehaviorSubject.

                                          Figura 2.3: Schema generale BLoC [19]

La diffusione del BLoC in Flutter è avvenuta in particolar modo grazie alla libreria flutter_bloc ad opera di
Felix Angelov (Senior Software Engineer BMW). Essa si struttura in una moltitudine di componenti fonda-
mentali, con lo scopo di astrarre alcuni Widget Flutter e di ridurre drasticamente il codice BoilerPlate da
scrivere nello sviluppo. I componenti principali sono:

   • Bloc2

   • BlocBuilder

   • BlocProvider

   • RepositoryProvider

Per comprenderli al meglio, si cercherà di realizzare l’app counter in Figura 2.1 tramite questa architettura.

Bloc

Il Bloc fornito da tale libreria non è altro che uno Stream che prende in ingresso Eventi e fornisce in uscita
un possibile Stato. Tale componente deve inoltre necessariamente implementare la funzione mapEventTo-
State, la quale definirà l’intera logica comportamentale.
Risulta dunque ovvio che per la configurazione di un Bloc è prima necessario definire:
  2 Per   BLoC si intende l’astrazione di uno Stream (In Flutter il concetto di BLoC nasce in un secondo momento).

                                                                 12
1. Eventi;

         2. Stati;

         3. mapEventToState.

    Dal punto di vista pratico gli eventi saranno incremento e decremento, mentre lo stato sarà semplicemente
    costituito dal valore che si sta contando.
    In termini puramente implementativi si andranno quindi a definire tre file distinti:

         • counter_events.dart;

         • counter_states.dart;

         • counter_bloc.dart.

    Counter Events Il codice alla base della definizione degli eventi risulta particolarmente semplice:
1   abstract class CounterEvent {}
2

3   class IncrementEvent extends CounterEvent {}
4   class DecrementEvent extends CounterEvent {}

    La definizione degli eventi sfrutta il principio dell’ereditarietà per declinare un evento generale ed eventi se-
    condari. Gli eventi secondari (Increment e Decrement) saranno quelli che verranno richiamati dall’utente al
    click di un pulsante, mentre l’evento generale servirà successivamente nella definizione del Bloc. L’utilizzo
    di una classe astratta per definire il "CounterEvent" non è necessario, ma è ampiamente raccomandato dalle
    Guide lines (linee guida) della documentazione per due motivi:

         • semplificare il riconoscimento dell’evento generale;

         • prevenire l’errata istanziazione dell’evento generale.

    Counter States        La definizione degli stati, in genere, risulta spesso più complessa di quella degli eventi, in
    quanto uno stato deve portare con sé informazione (anche nel caso degli eventi può succedere, ma in genere
    l’evento in sé rappresenta l’informazione da passare).
    In questo caso infatti è facile notare come il "CounterState" inglobi il valore intero che la view dovrà
    utilizzare.
1   class CounterState {
2       final int counter ;
3       CounterState ({ this . counter }) : assert ( counter != null ) ;
4   }

    Il CounterState viene definito come classe wrapper, ma sarebbe bastato usare int counter (il motivo per cui
    è stata usata una classe wrapper è relativo al fatto che in tutti gli esempi successivi gli stati non saranno mai
    un tipo atomico e non saranno mai unici).

                                                                  13
NOTA

          È importante menzionare che la definizione degli stati richiede quasi sempre l’uso di uno Stato
          generale da cui ereditano degli Stati secondari (come succedeva con gli eventi). Tuttavia questo
          risulta un caso particolare, in quanto vi è la necessità di un unico Stato.

    Counter Bloc Il file alla base del del Bloc è lo stesso che implementa la funzione mapEventToState, il
    vero e proprio cuore dello State Management.
    È in tale funzione che emerge il concetto di Programmazione Reattiva3 in Flutter grazie alle keywords yield
    e async*. Con yield si intende una sottospecie di return, con la sola differenza che "return" determina la
    fine della funzione, mentre yield immette semplicemente un nuovo dato nel flusso.
    L’utilizzo della parola chiave async* è fondamentale, in quanto consente l’utilizzo di yield.
1     Stream mapEventToState(CounterEvent event) async∗ {
2         if ( event is IncrementEvent) {
3             yield CounterState ( counter : currentState . counter + 1) ;
4         } else if ( event is DecrementEvent) {
5             yield CounterState ( counter : currentState . counter − 1);
6         } else {
7             yield currentState ;
8         }
9     }

    L’insieme di questi ingredienti dà vita ad una funzione caratterizzata quasi esclusivamente da "if-else", ma
    di una potenza disarmante. Inoltre essendo il Bloc considerabile come un’entità Reattiva, esso risulta essere
    perfettamente schematizzabile tramite State Diagram.
    Lo State Diagram di un oggetto rappresenta la sequenza di stati, le risposte, le azioni che l’oggetto attra-
    versa durante la sua vita in risposta agli stimoli ricevuti.

                                            Figura 2.4: State Diagram dell’app Counter

      3 Per   Programmazione Reattiva si intende programmare facendo uso di flussi di dati asincroni.

                                                                     14
BlocBuilder

Il BlocBuilder4 risulta essere un Observer5 a tutti gli effetti. Il suo compito è quello di registrarsi al Flusso
di dati (in questo caso al CounterBloc) e notificare la UI in caso di cambiamenti.

                                                                          IncrementEvent
                                                                         DecrementEvent

                                                    tch
                                                   pa
                                                 dis
                                                                              mapEventToState

                                                                               yie
                                                                                                        BLOC

                                                                                ld
                                                                                     +1

                                                                                            BlocBuilder

                                          Figura 2.5: Interazione tra utente e BLoC

  4 Il   BlocBuilder non è nativo in Flutter ma rappresenta un’astrazione del Widget "StreamBuilder".
  5 Si   intende il noto Design Pattern.

                                                                  15
BlocProvider

Il BlocProvider è un Widget che "provvede" a creare una comunicazione tra View e Bloc. Infatti il Bloc-
Builder è un elemento della View e riesce a mettersi in ascolto di un determinato Bloc proprio grazie al
BlocProvider. Un’ulteriore caratteristica che lo contraddistingue è la capacità di essere accessibile non solo
dal Widget figlio, ma da tutti i Widget sottostanti.
Porre un BlocProvider in corrispondenza del primo Widget dell’applicazione rende il Bloc accessibile ovun-
que (tale pratica viene in genere evitata, al fine di garantire l’utilizzo di un determinato Bloc, solo dove
effettivamente serve).
    NOTA

    È interessante notare come queste caratteristiche lo rendano a tutti gli effetti un’inheritedWidget,
    anch’esso uno degli State Management fondamentali di Flutter.

RepositoryProvider

Il RepositoryProvider presenta le stesse caratteristiche del BlocProvider, con la sola differenza che mette in
comunicazione View e Repository.
Questo componente non rientra nell’esempio del Counter, ma viene spesso adoperato per casi ben più com-
plessi. Il concetto di Repository in sé fa riferimento al Repository Pattern e descrive generalmente un’astra-
zione di un insieme di servizi o classi (sarà visto nel dettaglio successivamente).

    NOTA
    Per chiarezza, ho deciso di pubblicare l’esempio in questione su GitHub:
    https://github.com/egeondundee/counter_app

                                                       16
2.7     Redux
Redux risulta essere indubbiamente lo State Management de facto di React Native. La sua caratteristica
principale è data dall’estrema modularità, pagata al prezzo di tanto codice BoilerPlate.

                                  Figura 2.6: Schema generale Redux [12]

Dal Diagramma di Redux in Figura 2.6 emergono i seguenti elementi fondamentali:

   • Action: del tutto equivalente ad un "Evento" in Bloc (coincide con il "click" sul "+" del Counter);

   • Reducer: ha il compito di "mappare" le Action in futuri Stati che andranno a modificare lo Store;

   • Store: ha il ruolo di notificare la UI in caso di cambiamenti di Stato.

È così immediato notare l’infinità di similitudini esistenti tra Redux e Bloc che si fa fatica a percepirne le
differenze, le quali esistono ma risultano quasi nascoste. La differenza sostanziale risiede nelle finalità di
ciascuno State Management. Mentre la finalità principale di Bloc è quella di garantire un’eccezionale ge-
stione dello Stato Locale, quella di Redux è invece la garanzia di una semplice gestione dello Stato Globale.
In ambito realizzativo, in BLoC si vanno sempre a realizzare più Bloc distinti tra loro e indipendenti in grado
di gestire singole schermate o singole funzionalità di schermate (dalla schermata A non è concesso l’accesso
al Bloc della schermata B), in Redux invece lo scopo è quello di avere uno Store centralizzato facilmente
accessibile da ogni componente della UI.

Gestione Stato Locale vs Gestione Stato Globale
Il dibattito su quale sia la scelta migliore è estremamente aperto ed è spesso ciò che influenza maggiormente
la scelta dello State Management.

      Figura 2.7: Confronto Stato Locale e Globale per gli State Management principali in Flutter [13]

                                                      17
Capitolo 3

MovieMentor

3.1     Descrizione
MovieMentor è un’app iOS/Android, realizzata come caso di studio per questa tesi, che si propone di
consigliare all’utente quali film vedere in TV dopo aver compreso le sue preferenze, attraverso un sistema
Tinder-Like. Essa consente inoltre di avere un’overview generale su tutti i Programmi TV (Film/SerieTV) in
diretta sui canali televisivi e sui loro dati di interesse (rating, cast, produzione, descrizione).

3.1.1    Use Case Diagram
L’insieme dei requisiti espressi nella descrizione possono essere schematizzati come Use Case Diagram.

                                Figura 3.1: Use Case Diagram di MovieMentor

                                                        18
I casi d’uso che emergono dal diagramma in Figura 3.1 sono:

   • Ottieni Programmi TV: si tratta di "raccogliere" quanti più dati è possibile su tutti i Programmi TV
      trasmessi sui canali televisivi principali, attraverso un Sistema Esterno.

   • Crea Notifica: è un’operazione che viene realizzata periodicamente dal Sistema Esterno per consi-
      gliare all’utente i Programmi TV più adatti a lui.

Dalla descrizione emerge il fatto che l’app debba essere cross-platform1 , per tale motivo l’intero lato Client
sarà realizzato in Flutter. Inoltre va sottolineata la necessità di adoperare NodeJs per i compiti legati al lato
Server dal Sistema Esterno.

3.2      Architettura del Progetto
L’architettura del progetto è descritta dal Component Diagram in Figura 3.2.

                                      Figura 3.2: Component Diagram di MovieMentor

  1 Orientata   a più sistemi operativi.

                                                          19
Il diagramma in Figura 3.1 si suddivide intuitivamente in due sottosistemi principali2 :

    • External System;

    • Client Side.

Un’ulteriore sottosistema è quello relativo all’invio delle notifiche, dal quale il Sistema Esterno deve ne-
cessariamente dipendere (per poter inviare notifiche ad un dispositivo iOS/Android si deve sempre essere
autorizzati da un Messaging System).

3.2.1      Struttura Server Side
Il back-end, realizzato in NodeJS, è caratterizzato quasi esclusivamente da un Business Layer, il quale
ingloba più componenti modulari e coesi. I compiti principali svolti dai singoli componenti sono i seguenti:

   1. omologazione API Canali TV sotto un formato unico - Parser;

   2. inserimento dei dati in un database non relazionale - DbOperator;

   3. algoritmo sulla scelta del Programma TV sulla base delle categorie preferite dall’utente - Parser;

   4. invio delle notifiche e memorizzazione - NotificationSender;

   5. creazione API - Router.

3.2.2      Struttura Client Side
Lo scopo del Front End è quello di fornire all’utente un’applicazione in grado di:

   1. visualizzare cronologicamente info sui Programmi TV;

   2. ricevere notifiche;

   3. impostare filtri per le notifiche;

   4. selezionare categorie/generi preferiti di Programmi TV.

State Management

Lo State Management scelto per la realizzazione del Front End è il BLoC (Business Logic Component). La
scelta è stata dettata dai seguenti motivi:

    • consente la creazione di componenti indipendenti e riusabili;

    • facile manutenzione: modificare una funzionalità si riduce a modificare una relazione evento-stato;

    • è l’unico State Management che dà la possibilità di gestire lo Stato Locale.

  2 Un   sottosistema è un componente che agisce come unità di scomposizione per un sistema più grande.

                                                                20
3.3     User Interface
Al giorno d’oggi, in particolar modo nel mondo delle app, l’aspetto grafico ha assunto quasi più importanza
dell’aspetto funzionale. Il funzionamento dell’app è quasi sempre dato per scontato da parte dell’utente, il
quale ha quindi la possibilità di giudicare solo la UI e la UX (User Experience).
Per tale motivo la User Interface è stata realizzata con l’obiettivo di integrare tutte le funzionalità richieste
in maniera "gradevole". Per riuscire in questo intento si è cercato di realizzare componenti non nativi,
prendendo ampiamente spunto dall’HamburgerMenu di Bear (app di note, vincitrice dell’Apple Design
Award nel 2017) e dalla nota animazione dell’App Store.

          Figura 3.3: Confronto Hamburger Menu tra Bear (a sinistra) e MovieMentor (a destra)

3.3.1    Logo
Il logo rappresenta Alan Turing (tratto dal film "The Imitation Game"). L’idea che sta dietro questa scelta
consiste nell’identificare nel personaggio di Alan Turing i calcoli svolti dal Sistema Esterno, quasi come se
egli fosse un mentore per la scelta del film giusto (da qui il nome MovieMentor).

                                        Figura 3.4: Logo MovieMentor

                                                       21
3.3.2    Schermate App MovieMentor
Di seguito, sono mostrate le schermate principali dell’app.

                                  Figura 3.5: Overview sui ProgrammiTV

                 Figura 3.6: Impostazioni (a sinistra), visualizzazione notifiche (a destra)

                                                     22
3.4       Server Side
Questa sezione ha lo scopo di analizzare i compiti svolti dal Sistema Esterno.

3.4.1        Omologazione API Canali TV sotto un formato unico
I dati relativi ai programmi televisivi sono ricavati da canali amministrati da società diverse e quindi format-
tati in modi totalmente distinti.
Questa caratteristica ha fatto sì che fosse necessaria un’operazione di refactoring3 . L’operazione di refac-
toring è stata realizzata adoperando il package “node-fetch”, il quale consente di decodificare il JSON
ritornato dalle API in un tipo di dato strutturato facilmente gestibile (Dizionario/Map).
Tale operazione è suddivisa in N-Parser, dove N rappresenta il numero di formati distinti. Fortunatamente,
dato che Rai e Mediaset coprono più di 20 canali televisivi italiani, il progetto si suddivide in soli due parser
specifici:

   1. rai_parser.js

   2. mediaset_parser.js

Purtroppo, una volta terminata questa operazione è diventato evidente che molti dati forniti dalle API risul-
tavano essere insufficienti.
Spesso i campi relativi alla descrizione e all’immagine del programma TV erano nulli; inoltre, non vi era
nessuna informazione circa il rating del programma trasmesso.
Per tale motivo sono state quindi utilizzate anche le API di TheMovieDB (aggiunta di rating, popolarità,
cast, produzione, immagine e descrizione completa).

    L’idea guida è diventata quella di utilizzare le API di Mediaset e Rai per ottenere le informazioni tem-
porali sui canali trasmessi in TV, per poi completarle attraverso quelle di TheMovieDB.

Tale idea è stata concretizzata, nell’ambito del progetto, dai componenti themoviedb.js e parser.js.
Nello specifico, il compito del file themoviedb.js è quello di fornire le funzioni finalizzate all’ottenimento
del programma TV a partire dal titolo, mentre quello del file parser.js è di effettuare un merge tra i dati
ottenuti dalle API dei canali televisivi e quelli ottenuti da TMDB. In tal modo per la maggior parte dei pro-
grammi TV è stato possibile ottenere molta più informazione al prezzo di un estremo calo delle performance
(dovendo effettuare un “merge” vero e proprio tra più dati distinti).
Semplificando molto, prima di introdurre TMDB il tempo di risposta di un’API era pari alla somma del tem-
po di risposta Rai e Mediaset, con l’aggiunta del tempo necessario per il refactoring. Tale tempo di risposta
non andava quasi mai oltre i 700ms.
Con l’aggiunta dei dati di TMDB, si è resa necessaria una chiamata a TheMovieDB per ogni Programma
TV, portando il tempo di risposta complessivo ad un valore assolutamente inaccettabile.

    3 Con Refactoring si vuole intendere l’operazione di omologazione dei dati sotto un formato unico. In questo caso, si intende dunque

la ricostruzione di una struttura uguale sia per i dati della Rai, che quelli di Mediaset.

                                                                  23
Esempio di JSON Risultante dall’operazione di Omologazione .

 1   {
 2        "timePublished": "22:33",
 3        "title": "Big Bang Theory",
 4        "type": "TV SERIES",
 5        "description": "Guest star: Charlie Sheen. Raj e’ esaltato dal fatto che
               la rivista \"People\" lo ha inserito in una classifica dei trenta
               giovani scienziati piu’ promettenti.\nVISIONE ADATTA A TUTTI.\nQUESTO
               PROGRAMMA E’ SOTTOTITOLATO ALLA PAGINA 777DEL MEDIAVIDEO.",
 6        "duration": "00:20:00",
 7        "channel": "Italia 2",
 8        "day": "09-01-2019",
 9        "mainCategories": [
10             "Commedia"
11        ],
12        "subCategories": null,
13        "tmdb_info_is_ok": 1,
14        "tmdb_info": {
15             "overview": "Pasadena, California. Quattro giovani scienziati di
                    diversi campi: il fisico sperimentale Leonard Hofstadter, il
                    fisico teorico Sheldon Cooper, l’astrofisico Rajesh Koothrappali e
                    l’ingegnere aerospaziale Howard Wolowitz lavorano insieme al
                    California Institute of Technology. Questi fanno amicizia con
                    Penny, una bella ragazza di provincia col sogno di diventare
                    attrice, venuta a vivere nell’appartamento di fronte a quello
                    condiviso da Leonard e Sheldon.",
16             "popularity": 58.358,
17             "release_date": null,
18             "img":
                    "http://image.tmdb.org/t/p/w500/ooBGRQBdbGzBxAVfExiO8r7kloA.jpg",
19             "rating": 6.8,
20             "genres": [
21                  "Commedia"
22             ],
23             "cast": [...],
24             "crew": [...]
25        },
26   },

                                                 24
3.4.2      Inserimento dati nel Database
Per evitare le complicazioni derivanti dall’evoluzione delle API si è deciso di creare un database all’interno
del quale immettere periodicamente i nuovi dati.
In tal modo, il nuovo tempo di risposta dipende esclusivamente dal tempo di risposta della query nel
DB. Come DBMS4 è stato scelto MongoDB.

3.4.3      MongoDB
MongoDB (da "humongous", enorme) è un DBMS non relazionale, orientato ai documenti. Classificato
come un database di tipo NoSQL , MongoDB si allontana dalla struttura tradizionale dei database relazio-
nali basata su tabelle in favore di documenti in stile JSON con schema dinamico, rendendo l’integrazione di
dati di alcuni tipi di applicazioni più facile e veloce.
Rilasciato sotto una combinazione della GNU Affero General Public License e dell’Apache License, Mon-
goDB è un software libero e open source.

Perché MongoDB?

La scelta di MongoDB è stata dettata dalla constatazione della sua semplicità e della sua ottima integrazione
con NodeJS.
Inoltre, va specificato che il modo in cui il database è stato utilizzato in questo progetto non è quello di un
database tradizionale, bensì quello di Buffer di JSON (finalizzato esclusivamente alla minimizzazione dei
tempi di risposta).

Perché non un Database Relazionale?

L’unico motivo per cui è stato adoperato un Database nel progetto è la necessità di “custodire i dati”, con lo
scopo di minimizzare il tempo di risposta delle chiamate. Usare un database relazionale non sarebbe stato
sbagliato, ma avrebbe comportato lo sforzo della creazione di tabelle e relazioni in ultima analisi inutili,
considerato che i dati che servono sono esclusivamente quelli inseriti.

   4 In informatica, un Database Management System, abbreviato in DBMS o Sistema di gestione di basi di dati, è un sistema software

progettato per consentire la creazione, la manipolazione e l’interrogazione efficiente di database, per questo detto anche "gestore o
motore del database".

                                                                25
3.4.4    Algoritmo sulla scelta del Film/SerieTv
L’algoritmo relativo alla scelta del Film/SerieTV sulla base delle categorie preferite dell’utente è stata forse
una delle parti più stimolanti ed impegnative del processo di creazione di questo progetto.

Per comprendere a fondo la logica utilizzata è necessario considerare un ipotetico caso d’uso. Supponiamo
che un generico utente sia appassionato di film Crime e Biografici, supponiamo inoltre che durante la setti-
mana venga trasmesso in TV il film “The Wolf of Wall Street”.

Consideriamo inoltre i dati delle categorie relative al film preso in esempio.

                    Figura 3.7: Confronto dati generi/categorie "The Wolf of Wall Street"

Sono stati scelti i dati di Wikipedia in quanto molto simili a quelli riproposti dai canali TV.
Supponendo di effettuare un “merge” tra i dati di Wikipedia e quelli di TMDB, le categorie di “The Wolf of
Wall Street” risultanti sarebbero le seguenti: Crime, Drammatico, Commedia, Biografico.

A primo impatto, al fine di ottenere il film giusto per l’utente, si potrebbe pensare di “scorrere” i dati
presenti nel DB e restituire il film che possiede il rating superiore e che presenta al contempo anche le cate-
gorie preferite dall’utente.
Con molta probabilità quest’algoritmo restituirebbe "The Wolf of Wall Street” come film consigliato per l’u-
tente, nonostante questo sia solo marginalmente un film del genere “Biografico e Crime”.

                                                      26
Come risolvere quindi questo problema?
     L’errore principale sta nel “merge” effettuato tra i dati. Affermando che “The Wolf of Wall Street” è un
     film che rientra nei generi “Crime, Drammatico, Commedia e Biografico”, stiamo affermando che tutte le
     categorie descrivono in egual misura il film (in termini percentuali sono tutte al 25% appartenenti al film).
     Dato che questo tipo di approccio risulta evidentemente fallimentare, è necessario suddividere l’algoritmo
     in due distinte fasi:

          1. “merging pesato” delle categorie;

          2. selezione del film/SerieTV.

     Merging pesato delle categorie

     L’idea alla base di questa fase cerca di attribuire determinati pesi ad ogni film, sulla base dei dati ottenuti.
     Per chiarezza ripropongo i dati precedentemente esposti:

          • Wikipedia: commedia, drammatico, biografico;

          • TMDB: crime, drammatico, commedia.

     Dal punto di vista probabilistico si può ipotizzare che le categorie “Commedia” e “Drammatico” hanno si-
     curamente un peso superiore alle altre, in quanto sia Wikipedia che TMDB le ritornano, mentre le rimanenti
     possono essere supposte come categorie secondarie. In particolare ciò che succede è che ogni volta che due
     categorie risultano identiche nella fase di “merge”, i pesi vengono sommati.

     All’inizio quindi si avrà:

 1   { TMDB_Genres: {Crime: 1.0, Drammatico: 1.0, Commedia: 1.0}}
 2   { Wikipedia_Genres: {Commedia: 1.0, Drammatico: 1.0, Biografico: 1.0}}
 3

 4   { totalGenresWithScore: {
 5       Crime: 1.0,
 6       Drammatico: 2.0,
 7       Commedia: 2.0,
 8       Biografico: 1.0
 9    }
10   }

     Normalizzando i dati ottenuti da totalGenresWithScore a 1.0 si ottiene:

 1   { totalGenresWithScore: {
 2       Crime: 0.5,
 3       Drammatico: 1.0,
 4       Commedia: 1.0,
 5       Biografico: 0.5
 6    }
 7   }

                                                           27
Sulla base dei dati ottenuti si cerca di effettuare la selezione.
Riporto alcuni esempi (i dati non sono normalizzati a 1.0):

     NOTA
     Chiaramente è necessaria un’operazione di refactoring anche sui generi dei film. Infatti, i dati otte-
     nuti da un merge tra Rai, Mediaset e TMDB portano ad ottenere più di 100 categorie diverse, per
     cui si è cercato di omologarle fra loro, al fine di ridurle a circa 20.
     L’omologazione ha portato ad ulteriori sfumature.
     Cosa si intende per omologazione in questo caso? Ecco alcuni esempi:

 1   {in_costume: {Storico: 1.0}},
 2   {spionaggio: {Crime: 0.5, Azione: 0.5}},
 3   {sci-fi: {Fantascienza: 1.0}},
 4   {sovrannaturale: {Horror: 0.5, Fantasy: 0.5}}

     L’operazione di omologazione è stata realizzata tramite espressioni regolari.

Le categorie ottenute alla fine del processo sono: Commedia, Parodia, Drammatico, Azione, Avventura,
Thriller, Crime, Giallo, Horror, Romantico, Storico, Fantasy, Fantascienza, Animazione, Musica, Famiglia,
Western, Natura, Documentario, Sportivo.

                                                         28
Selezione del Film/SerieTV

L’operazione di selezione è piuttosto semplice, trattandosi di una ricerca lineare.
Innanzitutto va specificato che dal lato applicativo i dati ottenuti sulle categorie preferite saranno inviati in
maniera radicalmente diversa rispetto a quanto avveniva prima (nell’esempio introduttivo).
Se prima l’utente generico inviava le categorie come “Crime e Biografico” (in un’array), adesso le invia
tramite un dizionario ed in forma pesata (esempio: Crime: 1.0, Biografico: 0.5).
Questo fa si che l’operazione di selezione del film si riduca ad essere una ricerca del minor errore qua-
dratico medio.

                                                                 xi )2
                                                      ∑ni=1 (xi −ˆ
                                    MSE =                    n
In termini semplici, si scorreranno tutti programmi TV del giorno dd/mm/yyyy in tal modo:

                   (genres[Crime]−programsi [Crime])2 +(genres[Biogra f ico]−programsi [Biogra f ico])2
        MSE =                                       genres.length

Il programma TV con il minor MSE sarà quello che avrà le categorie più simili a quelle desiderate dall’utente
(in termini pesati).
Per garantire anche la qualità del programma TV, è necessario che il rating di questo sia superiore a 7.5 (il
massimo è 10). L’utente dall’app avrà la possibilità di aggiungere ulteriori filtri (fascia oraria, solo Film,
solo Serie TV).
    NOTA
    N.B: Quest’algoritmo è basato su una serie di assunzioni che non necessariamente risultano ve-
    rificate, anche se sperimentalmente hanno fornito buoni risultati. E’ inutile dire che riuscire ad
    ottenere sempre il programma TV più adatto all’utente richiederebbe una logica ben più complessa.

                                                       29
3.4.5      Invio delle notifiche e memorizzazione
     Questa fase non risulta esclusivamente relativa a nodejs ma è frutto di una “cooperazione” tra back-end e
     front-end. Innanzitutto il sistema adoperato per l’invio delle notifiche è stato Firebase Cloud Messaging.

     Firebase Cloud Messaging

     Firebase Cloud Messaging (FCM), precedentemente noto come Google Cloud Messaging (GCM), è una
     soluzione cloud multipiattaforma per messaggi e notifiche per Android, iOS e applicazioni web, che at-
     tualmente può essere utilizzata gratuitamente. Il servizio è fornito da Firebase, una società controllata da
     Google.
     FCM consente agli sviluppatori di software di inviare notifiche push per le loro applicazioni agli utenti finali
     attraverso delle API.

     La scelta è ricaduta su FCM essendo il servizio gratuito, ben documentato e ricco di librerie integrabili
     su node. La libreria con la quale è stato integrato Firebase Cloud Messaging a node è “node-fcm”.

     3.4.6 node-fcm
     La libreria node-fcm consente di inviare notifiche push ad un mittente identificato tramite un token (univo-
     co). In particolare, il token identifica il dispositivo e può essere ottenuto esclusivamente dall’applicativo.
     Affinché possa essere inviata una notifica ad un determinato token, devono prima essere verificate due fasi
     fondamentali.

        1. invio token via app;

        2. memorizzazione token in DB.

     Questo fa si che in MongoDB venga creato un nuovo documento (chiamato users) all’interno del quale sono
     presenti tutti i token registrati.
     Essendo i token veri e propri identificativi ed essendo sicuramente unici, è possibile memorizzare anche le
     categorie preferite dall’utente per ogni token (questo consente la memorizzazione di dati per utente, senza
     necessaria registrazione). Una volta effettuate ambedue le fasi è possibile inviare la notifica push attraverso
     node-fcm in un messaggio formattato come segue:
 1       var message = {
 2            to : token ,
 3             collapse_key : ’type_a’ ,
 4

 5              notification : {
 6                   title : programMessage. title ,
 7                  body: programMessage.channel + "ore : " + programMessage.timePublished,
 8                  click_action : ’FLUTTER_NOTIFICATION_CLICK’
 9             },
10       };

                                                            30
3.4.7      Creazione API
L’ultima fase svolta da node è chiaramente la creazione delle API. La libreria utilizzata per la realizzazione
delle API è express.js (la quale consente la creazione di Router5 ) in quanto di una semplicità disarmante e
ben documentata.
Le API fondamentali attualmente realizzate sono:

   • API con info su Programmi TV trasmessi per ogni canale;

   • API con info sui Programmi TV trasmessi per orario;

   • API con info sui Programmi TV trasmessi per giorno dd-mm-yyyy;

   • API con info sugli utenti (categorie preferite e token);

   • API con info circa le categorie utilizzate.

Questa fase è risultata indubbiamente la più semplice di tutto il back-end in NodeJS, ma al contempo risulta
essere la più importante. È proprio grazie alle seguenti API che è possibile stabilire una relazione tra Front-
End in Flutter e back-end in Node, consentendo quindi l’utilizzo dell’approccio CLI (Call Level Interface).

                                     Figura 3.8: Schema relativo all’approccio CLI

  5 Un   Express.Router è un Handler di Web Routes.

                                                          31
3.5       MovieMentor - Front End
Nella realizzazione dell’app MovieMentor è stato fatto un larghissimo utilizzo di Bloc. Questa strutturazio-
ne ha consentito un’eccezionale gestione dello stato locale ed una forte indipendenza di ogni componente
fondamentale. Componenti come il gestore delle notifiche, il loader dei programmi, l’AnimationController
dell’HamburgerMenu risultano essere facilmente riutilizzabili. Tuttavia, seppur i Bloc adoperati risultano
essere sostanzialmente diversi tra loro (per compiti e ruoli), nello sviluppo si è notato che sommariamente
sono tutti assimilabili a due categorie fondamentali:

    • Bloc basati su Servizi Esterni;

    • Bloc non basati su Servizi Esterni.

3.5.1     Bloc basati su Servizi Esterni
Questa tipologia di Bloc è sicuramente molto più complessa strutturalmente della seconda, in quanto implica
l’utilizzo di molti più livelli.
                                                                                                MovieMentor
                                                                                                Remote DB

                                                                                          API
                                                                             Service

          UI                       BLoC              Repository

                                                                                                MovieMentor
                                                                               DAO               Local DB

                                   Figura 3.9: Schema Bloc basato su Servizi Esterni

I principali Bloc che adoperano questa struttura sono:

   1. database_bloc: Bloc impiegato nella gestione del DB Locale (è stato usato SQLite);

   2. programs_bloc: Bloc impiegato per il Loading dei Programmi nella schermata "Programmi TV";

   3. notification_bloc: Bloc impiegato per la gestione delle notifiche.

Dallo schema proposto sopra, saltano subito all’occhio i concetti di Repository, Service, DAO. Cerchiamo
di comprenderli a fondo.

Service

Con il termine Service o Service Provider, si intende tipicamente un insieme di funzioni che effettuano
richieste http nei confronti di servizi esterni. In generale tali funzioni tendono ad essere asincrone.

                                                          32
DAO

In informatica, nell’ambito della programmazione Web, il DAO (Data Access Object) è un pattern archi-
tetturale per la gestione della persistenza: si tratta fondamentalmente di una classe con relativi metodi che
rappresenta un’entità tabellare di un RDBMS, usata principalmente in applicazioni web sia di tipo Java EE
sia di tipo EJB, per stratificare e isolare l’accesso ad una tabella tramite query (poste all’interno dei metodi
della classe) ovvero al data layer da parte della business logic creando un maggiore livello di astrazione ed
una più facile manutenibilità. I metodi del DAO con le rispettive query interne verranno così richiamati dalle
classi della business logic.
Nel caso di MovieMentor le classi DAO sono state create per poter effettuare le operazioni CRUD6 sul
Database Locale (in SQLite). In particolare sono state create le classi DAO relative ai Programmi TV ed alle
loro categorie.

Repository

Il concetto di Repository è di capitale importanza in moltissimi pattern (Model View Presenter, Model View-
View Model, Redux, Bloc ecc...). La sua utilità nel Bloc State Management è quella di realizzare un’ulteriore
livello di astrazione tra Data-Layer e Bloc, al fine di “mascherare” al Bloc la sorgente dati. Il Bloc non è
interessato a sapere da dove provengano i dati e di certo, non ha il compito di richiamare le funzioni delle
classi DAO o dei ServiceProvider.

    NOTA
    Un esempio lampante di come funzioni la tipologia “Bloc basati su Servizi Esterni” è dato indub-
    biamente dal Bloc “programs_bloc” il quale, come citato prima, è impiegato per il Loading dei
    Programmi sulla schermata Programmi TV.

programs_bloc

Gli step per la definizione di tale Bloc sono:

   1. definizione di un ServiceProvider (in questo caso non servono DAO);

   2. definizione di una Repository;

   3. definizione Eventi e Stati + mapEventToState.

ServiceProvider In questo particolare caso il ServiceProvider è dato da una classe denominata “Pro-
gramApiProvider” con la sola funzione fetchPrograms (funzione dedita all’ottenimento dei programmiTV
tramite richiesta http post al database remoto MongoDB).
  6 Create,   Retrieve, Update, Delete.

                                                      33
Repository La definizione della Repository è veramente elementare in quanto si tratta semplicemente di
    inglobare tutte le funzionalità dei servizi su cui si fa affidamento. Per comprenderne la semplicità basta
    osservare il codice.
1   class ProgramRepository {
2       ProgramApiProvider _programApiProvider = ProgramApiProvider();
3       Future fetchPrograms(int from, int limit ) => _programApiProvider.fetchPrograms(from,
             limit ) ;
4   }

    La parola chiave Future
Puoi anche leggere