23 - POO - Conceptos de programación orientada a objetos

Kotlin nos permite utilizar la metodología de programación orientada a objetos.

Con la metodología de programación orientada a objetos (POO) se irán introduciendo conceptos de objeto, clase, propiedad, campo, método, constructor, herencia etc. y de todos estos temas se irán planteando problemas resueltos.

Prácticamente todos los lenguajes desarrollados en los últimos 25 años implementan la posibilidad de trabajar con POO (Programación Orientada a Objetos)

Conceptos básicos de Objetos

Un objeto es una entidad independiente con sus propios datos y programación. Las ventanas, menúes, carpetas de archivos pueden ser identificados como objetos; el motor de un auto también es considerado un objeto, en este caso, sus datos (campos y propiedades) describen sus características físicas y su programación (métodos) describen el funcionamiento interno y su interrelación con otras partes del automóvil (también objetos).

El concepto renovador de la tecnología Orientación a Objetos es la suma de funciones a elementos de datos, a esta unión se le llama encapsulamiento. Por ejemplo, un objeto Auto contiene ruedas, motor, velocidad, color, etc, llamados atributos. Encapsulados con estos datos se encuentran los métodos para arrancar, detenerse, dobla, frenar etc. La responsabilidad de un objeto auto consiste en realizar las acciones apropiadas y mantener actualizados sus datos internos. Cuando otra parte del programa (otros objetos) necesitan que el auto realice alguna de estas tareas (por ejemplo, arrancar) le envía un mensaje. A estos objetos que envían mensajes no les interesa la manera en que el objeto auto lleva a cabo sus tareas ni las estructuras de datos que maneja, por ello, están ocultos. Entonces, un objeto contiene información pública, lo que necesitan los otros objetos para interactuar con él e información privada, interna, lo que necesita el objeto para operar y que es irrelevante para los otros objetos de la aplicación.

Concepto de Clase y definición de Objetos

La programación orientada a objetos se basa en la programación de clases; a diferencia de la programación estructurada, que está centrada en las funciones.

Una clase es un molde del que luego se pueden crear múltiples objetos, con similares características.

Una clase es una plantilla (molde), que define propiedades (variables) y métodos (funciones)

La clase define las propiedades y métodos comunes a los objetos de ese tipo, pero luego, cada objeto tendrá sus propios valores y compartirán las mismas funciones.

Debemos crear una clase antes de poder crear objetos (instancias) de esa clase. Al crear un objeto de una clase, se dice que se crea una instancia de la clase o un objeto propiamente dicho.

La estructura básica en Kotlin de una clase es:

class [nombre de la clase] {
  [propiedades de la clase]
  [métodos o funciones de la clase]
}

Problema 1

Implementaremos una clase llamada Persona que tendrá como propiedades (variables) su nombre y edad, y tres métodos (funciones), uno de dichos métodos inicializará las propiedades del nombre y la edad, otro método mostrará en la pantalla el contenido de las propiedades y por último uno que imprima si es mayor de edad.

Definir dos objetos de la clase Persona.

Proyecto109 - Principal.kt

class Persona {
    var nombre: String = ""
    var edad: Int = 0

    fun inicializar(nombre: String, edad: Int) {
        this.nombre = nombre
        this.edad = edad
    }

    fun imprimir() {
        println("Nombre: $nombre y tiene una edad de $edad")
    }

    fun esMayorEdad() {
        if (edad >= 18)
            println("Es mayor de edad $nombre")
        else
            println("No es mayor de edad $nombre")
    }
}

fun main(parametro: Array<String>) {
    val persona1: Persona
    persona1 = Persona()
    persona1.inicializar("Juan", 12)
    persona1.imprimir()
    persona1.esMayorEdad()
    val persona2: Persona
    persona2 = Persona()
    persona2.inicializar("Ana", 50)
    persona2.imprimir()
    persona2.esMayorEdad()
}

El nombre de la clase debe hacer referencia al concepto (en este caso la hemos llamado Persona). La palabra clave para declarar la clase es class, seguidamente el nombre de la clase y luego una llave de apertura que debe cerrarse al final de la declaración de la clase:

class Persona {

Definimos dos propiedades llamadas nombre y edad, las inicializamos con un String vacío a una y con 0 a la otra:

    var nombre: String = ""
    var edad: Int = 0

Una de las premisas del lenguaje Kotlin es que sea seguro y no permite definir una propiedad y no asignarle un valor y que quede con el valor null

Luego definimos sus tres métodos (es lo que conocemos como funciones hasta ahora, pero al estar dentro de una clase se las llama métodos)

El método inicializar recibe como parámetros un String y un Int con el objetivo de cargar las propiedades nombre y edad:

    fun inicializar(nombre: String, edad: Int) {
        this.nombre = nombre
        this.edad = edad
    }

Como los parámetros se llaman en este caso igual que las propiedades las diferenciamos antecediendo la palabra clave this al nombre de la propiedad:

        this.nombre = nombre
        this.edad = edad

El método imprimir tiene por objetivo mostrar el contenido de las dos propiedades:

    fun imprimir() {
        println("Nombre: $nombre y tiene una edad de $edad")
    }

Como en este método no hay parámetros que se llamen igual a las propiedades podemos acceder a las propiedades directamente por su nombre y no estar obligados a anteceder el operador this, no habría problema de anteceder el this y escribir esto:

    fun imprimir() {
        println("Nombre: ${this.nombre} y tiene una edad de ${this.edad}")
    }

El tercer y último método tiene por objetivo mostrar un mensaje si la persona es mayor de edad o no:

    fun esMayorEdad() {
        if (edad >= 18)
            println("Es mayor de edad $nombre")
        else
            println("No es mayor de edad $nombre")
    }

Decíamos que una clase es un molde que nos permite crear objetos. Ahora veamos cual es la sintaxis para la creación de objetos de la clase Persona:

fun main(parametro: Array<String>) {
    val persona1: Persona
    persona1 = Persona()
    persona1.inicializar("Juan", 12)
    persona1.imprimir()
    persona1.esMayorEdad()

Primero debemos definir una variable de tipo Persona:

    val persona1: Persona

Para crear el objeto debemos asignar a la variable persona1 el nombre de la clase y unos paréntesis abiertos y cerrados:

    persona1 = Persona()

Una vez que hemos creado el objeto podemos llamar a sus métodos antecediendo primero el nombre del objeto (persona1):

    persona1.inicializar("Juan", 12)
    persona1.imprimir()
    persona1.esMayorEdad()

Es importante el orden que llamamos a los métodos, por ejemplo si primero llamamos a imprimir antes de inicializar, veremos que muestra una edad de cero y un String vacío como nombre.

Una clase es un molde que nos permite crear tantos objetos como necesitemos, en nuestro problema debemos crear dos objetos de la clase Persona, el segundo lo creamos en forma similar al primero:

    val persona2: Persona
    persona2 = Persona()
    persona2.inicializar("Ana", 50)
    persona2.imprimir()
    persona2.esMayorEdad()

Acotación.

Hemos visto que Kotlin busca ser conciso, podemos en una sola línea definir el objeto y crearlo con la siguiente sintaxis:

    val persona1 = Persona()

En lugar de:

    val persona1: Persona
    persona1 = Persona()

Esto nos debe recordar a conceptos anteriores cuando definimos un objeto de la clase Int:

    val peso: Int
    peso = 40

Y en forma concisa escribimos:

    val peso = 40

Problema 2

Implementar una clase que cargue los lados de un triángulo e implemente los siguientes métodos: inicializar las propiedades, imprimir el valor del lado mayor y otro método que muestre si es equilátero o no.

Proyecto110 - Principal.kt

class Triangulo {
    var lado1: Int = 0
    var lado2: Int = 0
    var lado3: Int =0

    fun inicializar() {
        print("Ingrese lado 1:")
        lado1 = readln().toInt()
        print("Ingrese lado 2:")
        lado2 = readln().toInt()
        print("Ingrese lado 3:")
        lado3 = readln().toInt()
    }

    fun ladoMayor() {
        print("Lado mayor:")
        when {
            lado1 > lado2 && lado1 > lado3 -> println(lado1)
            lado2 > lado3 -> println(lado2)
            else -> println(lado3)
        }
    }

    fun esEquilatero() {
        if (lado1 == lado2 && lado1 == lado3)
            print("Es un triángulo equilátero")
        else
            print("No es un triángulo equilátero")
    }
}


fun main(parametro: Array<String>) {
    val triangulo1 = Triangulo()
    triangulo1.inicializar()
    triangulo1.ladoMayor()
    triangulo1.esEquilatero()
}

La clase Triangulo define tres propiedades donde almacenamos los lados del triángulo:

class Triangulo {
    var lado1: Int = 0
    var lado2: Int = 0
    var lado3: Int =0

El método inicializar, que debemos recordar que sea el primero en ser llamado procede a cargar por teclado los tres lados del triángulo:

    fun inicializar() {
        print("Ingrese lado 1:")
        lado1 = readln().toInt()
        print("Ingrese lado 2:")
        lado2 = readln().toInt()
        print("Ingrese lado 3:")
        lado3 = readln().toInt()
    }

El segundo método verifica cual de los tres lados tiene almacenado un valor mayor utilizando la sentencia when (en lugar de una serie de if anidados):

    fun ladoMayor() {
        print("Lado mayor:")
        when {
            lado1 > lado2 && lado1 > lado3 -> println(lado1)
            lado2 > lado3 -> println(lado2)
            else -> println(lado3)
        }
    }

El tercer método verifica si se trata de un triángulo equilátero:

    fun esEquilatero() {
        if (lado1 == lado2 && lado1 == lado3)
            print("Es un triángulo equilátero")
        else
            print("No es un triángulo equilátero")
    }

En la función main definimos un objeto de la clase Triangulo y llamamos a sus métodos:

fun main(parametro: Array<String>) {
    val triangulo1 = Triangulo()
    triangulo1.inicializar()
    triangulo1.ladoMayor()
    triangulo1.esEquilatero()
}

Problema propuesto

  • Implementar una clase llamada Alumno que tenga como propiedades su nombre y su nota. Definir los métodos para inicializar sus propiedades por teclado, imprimirlos y mostrar un mensaje si está regular (nota mayor o igual a 4)
    Definir dos objetos de la clase Alumno.
Solución
Proyecto111

class Alumno {
    var nombre: String = ""
    var nota: Int = 0

    fun inicializar() {
        print("Ingrese el nombre del alumno:")
        nombre = readln().toString()
        print("Ingrese su nota:")
        nota = readln().toInt()
    }

    fun imprimir() {
        println("Alumno: $nombre tiene una nota de $nota")
    }

    fun mostrarEstado () {
        if (nota >= 4)
            println("$nombre se encuentra en estado REGULAR")
        else
            println("$nombre no está REGULAR")
    }
}

fun main(parametros: Array<String>) {
    val alumno1 = Alumno()
    alumno1.inicializar()
    alumno1.imprimir()
    alumno1.mostrarEstado()
    val alumno2 = Alumno()
    alumno2.inicializar()
    alumno2.imprimir()
    alumno2.mostrarEstado()
}