Data

  • Unes dades (o "data") és un un conjunt de valors que estan realacionats i que es gestionen com un conjunt.

    Introducció

    Crea un projecte data amb Amper.

    Dades

    Unes dades són un conjunt de valors que ens interessa tractar i que agrupem en una estructura de dades.

    Un exemple és id, name i married que agrupem en l’estructura Person

    Person

    id: Long

    name: String

    married: Boolean

    Aquesta estructura es pot implementar en Kotlin mitjançant un data class.

    Modifica el fitxer src/main.kt

    data class Person(
        val id: Long,
        val name: String,
        val married: Boolean,
    )
    
    fun main() {
    }

    Una estructura de dades sempre ha de ser immutable: totes les propietats de la classe són val.

    A continuació crea algunes “dades”:

    data class Person(val id: Long, val name: String, val married: Boolean)
    
    fun main() {
    
        val david = Person(1, "David", false)
        val esther = Person(2, "Esther", true)
    
        println(esther)
    }

    Executa l’aplicació:

    .\amper run

    Pots veure que un data class té algunes implementacions molt útils.

    Person(id=2, name=Esther, married=true)

    Quan fas un “print” d’un objecte, aquest et mostra totes les propietats amb el seu valor:

    Si comparo dues persones amb les mateixes propietats, el resultat és true.

    Crea el fitxer test/DatTest.kt

    import kotlin.test.Test
    import kotlin.test.assertEquals
    
    class DataTest {
        @Test
        fun testPerson() {
            assertEquals(Person(1, "David", false), Person(1, "David", false))
        }
    }

    Si executo la tasca test tot es correcte 👌

    .\amper test
    
    BUILD SUCCESSFUL in 1s

    Com que les dades són inmutables, per modificar unes dades has de crear un objecte nou.

    Amb un data class pots utilizar les dades antigues per crear dades noves indicant només la nova informació:

    data class Person(val id: Long, val name: String, val married: Boolean, )
    
    fun main() {
    
        val esther = Person(2, "Esther", true)
        require(esther.married)
    
        val estherDivorced = esther.copy(married = false)
        require(esther.name == "Esther")
        require(!estherDivorced.married)
    }

    Amb la funció require dic a l’aplicació que el que dic és true o l’aplicació ha de petar amb un “Failed requirement” 💣🔥 !!.

    Modificar el fitxer main.kt

    data class Person(val id: Long, val name: String, val married: Boolean, )
    
    fun main() {
    
        val esther = Person(2, "Esther", true)
        require(!esther.married)
    }

    I equivoca’t:

    .\amper run

    Doncs si … 🙄

    Exception in thread "main" java.lang.IllegalArgumentException: Failed requirement.
            at MainKt.main(main.kt:30)
            at MainKt.main(main.kt)
    
    ERROR: Task ':data:runJvm' failed: Process exited with exit code 1

    L’Esther encara no s’ha divorciat 😒 🤨 🥳

    Mermaid

    Mermaid ens permet crear el nostre diagrama de classes.

    Instal.la el plugin Mermaid:

    • Ctrl + Alt + S per obrir el menú “Settings”
    • Selecciona Plugins
    • Busca “Mermaid” i instal.la

    Crea el fitxer REAMDE.md.

    A Gitlab tens un exemple d’un diagrama de classes fet amb Mermaid.

    Crea el diagrama de la classe Person:

    Composició

    Un objecte pot estar composat d’altres objectes definits per altres classes.

    Per exemple, una persona pot tenir una adreça:

    Address

    street: String

    city: String

    zipcode: String

    country: String = "Spain"

    Person

    id: Long

    name: String

    married: Boolean

    En Kotlin tots són objectes!

    Per tant, com que Long, String, etc. són objectes, no hi diferència entre els teus objectes i els objectes predefinits per Kotlin.

    Modifica el fitxer main.kt:

    data class Address(
        val street: String,
        val city: String,
        val zipcode: String,
        val country: String = "Spain",
    )
    
    data class Person(
        val id: Long,
        val name: String,
        val address: Address,
    )
    
    fun main() {
    }

    Crea el test corresponent:

    Activitats

    1. Modifica el fitxer README.md amb aquest nou disseny:

    from

    to

    1..*

    User

    id: Long

    name: String

    Message

    id: Long

    time: LocalTime

    text: String

    2. Modifica el fitxer main.kt amb les classes corresponents:

    3. Modifica el fitxer DataTest.kt amb el test corresponent

    Propietat derivada

    Hi ha molts objectes que tenen propietats que es deriven d’altres propietats.

    Per exemple, un rectangle té dos propietats bàsiques width i length.

    Però també té altres propietats que es deriven d’aquestes propietats bàsiques, com poden ser area i perimeter.

    data class Rectangle(val width: Int, val length: Int)
    
    fun main() {
    
        val r = Rectangle(4, 5)
    
        val area = r.width * r.length
        require(area == 20)
    
        val perimeter = r.width * 2 + r.length * 2
        require(perimeter == 18)
    }

    Aquestes propietats derivades es poden definir a la declaració de la classe.

    Per exemple, podem definir la propietat derivada area:

    data class Rectangle(val width: Int, val length: Int) {
        val area get() = this.width * this.length
    }
    
    fun main() {
    
        val r = Rectangle(4, 5)
        require(r.area == 20)
    
        val perimeter = r.width * 2 + r.length * 2
        require(perimeter == 18)
    }

    Si fas un print de l’objecte r pots veure que no apareix la propietat derivada area:

    Rectangle(width=4, length=5)

    Activitats

    1.- Afegeix la propietat derivada perimeter a la classe Rectangle.

    2.- Defineix una classe Circle amb les propietats derivades més importants.

    3.- A continuació tens el diagrama de la classe Patient d’un Hospital:

    Patient

    id: Long

    givenName: String

    familyName: String

    /name: String

    birthDate: Date

    addmitted: Date

    /age: Int

    La classe Patient té els atributs derivats name i age:

    • name és el nom complet i es deriva de givenName i familyName.
    • age és calcula en funció de birthDate i la data actual.

    Modifica els fitxers README.md, main.kt i DataTest.kt:

    README.md

    main.kt

    DataTest.kt

    5- A continuació tens el diagrama de classes del prèstec d’un llibre d’una Biblioteca:

    Book

    id: Long

    title: String

    author: String

    BookCopy

    id: Long

    BookBorrow

    id: Long

    date: Date

    loadPeriod: Long

    /dueDate: Date

    /isOverdue: Boolean

    La classe BookBorrow té els atributs derivats dueData i isOverdue:

    • dueData és la data de venciment de la devolució del llibre i es calcula en funció de la data del préstec i el període de préstec.

    • overdue indica si el prèstec a vençut i és calcula en funció de dueDate i la data actual.

    Modifica els fitxers README.md, App.kt i AppTest.kt:

    README.md

    main.kt

    DataTest.kt

    Herència

    TODO

    Activitat (Client)

    1. Crea aquest diagrama:

    Client

    id: Long

    name: String

    2.- Crea el model de dades en el fitxer main.kt:

    3.- Crea els tests corresponents en el fitxer DataTest.kt:

    4.- Modifica el fitxer main.kt per tal que demani el nom a l’usuari i crei un nou usuari:

    Activitat (Sky)

    Has de dissenyar el model de dades d’una aplicació que controla els vols d’avions dins del Cel Únic Europeu (Single European Sky, SES).

    L’aplicació ha de tenir la informació dels diferents vols, on en cada vol consta l’aeroport d’origen i destí, data prevista d’enlairament i aterratge, avió que farà el vol, companyia aèrea, etc.

    1.- Crea el diagrama de dades:

    2.- L’usuari ha de poder consultar els vols que tenen origen en un aeroport.

    3. Crea un excutable amb `amper package”:

    El resultat està a la carpeta build/tasks.

    java -jar .\build\tasks\_data_executableJarJvm\data-jvm-executable.jar