{}
FUNDAMENTOS DE DESARROLLO DE SOFTWARE
Programación
Orientada a Objetos
Miguel Romilio Aceituno Rojo
Clases y Objetos
Encapsulamiento
Herencia
Polimorfismo
Abstracción
CONTENIDO DEL MÓDULO
Agenda
01
¿Qué es la POO?
03
Los 4 pilares
05
Principios SOLID
Paradigma, objetos y clases como base del diseño de software
Encapsulamiento, herencia, polimorfismo y abstracción
Buenas prácticas para un diseño mantenible
02
Clases, objetos y constructores
04
Interfaces y composición
06
Caso integrador
Cómo se define y se instancia un tipo en Kotlin
Alternativas y complementos a la herencia
Un ejemplo completo que reúne todos los conceptos
02
CONCEPTO CENTRAL
¿Qué es la Programación Orientada a Objetos?
Es un paradigma de programación que organiza el software como
un conjunto de objetos que combinan estado (atributos) y
comportamiento (métodos), modelando entidades del mundo real
dentro del código.
LOS 4 PILARES
1
Encapsulamiento
Ocultar el estado interno y exponer solo lo necesario
• Un objeto agrupa datos y las funciones que operan sobre ellos
• Una clase es el molde o plantilla que describe a los objetos
2
Herencia
Reutilizar y extender comportamiento entre clases
• Favorece la reutilización, el mantenimiento y la organización del código
• Es el paradigma base de Kotlin, Java, C#, Python y Swift
3
4
Polimorfismo
Un mismo mensaje, distintas implementaciones
Abstracción
Modelar lo esencial, ocultando la complejidad
03
FUNDAMENTO 1
Clases y Objetos
Clase
Plantilla que define qué atributos y métodos tendrán sus instancias.
No ocupa memoria por sí misma hasta que se crea un objeto a partir
de ella.
Objeto
Instancia concreta de una clase, con valores propios para cada
atributo. Se crea invocando al constructor de la clase.
auto1 y auto2 son dos objetos distintos creados a partir
de la misma clase
Auto, cada uno con su propia placa y color.
class Auto(
val placa: String,
var color: String
) {
fun pintar(nuevoColor: String) {
color = nuevoColor
}
}
fun main() {
val auto1 = Auto("V1A-234", "Rojo")
val auto2 = Auto("W2B-901", "Azul")
auto1.pintar("Negro")
println("${auto1.placa}: ${auto1.color}")
// V1A-234: Negro
}
04
FUNDAMENTO 2
Atributos, Métodos y Constructores
• Atributos (propiedades): describen el estado del objeto — val
(inmutable) o var (mutable)
• Métodos: definen el comportamiento del objeto
• Constructor primario: se declara junto al nombre de la clase
• Bloque init: código adicional que se ejecuta al crear el objeto
class Producto(
val codigo: String,
var precio: Double
) {
var stock: Int = 0
init {
require(precio >= 0) {
"Precio inválido"
}
}
Constructor secundario
Kotlin permite constructores adicionales con la palabra clave
constructor, útiles cuando se necesitan formas alternativas de crear un
constructor(codigo: String) :
this(codigo, 0.0)
objeto — por ejemplo, un auto sin color asignado que toma un valor
por defecto.
fun aplicarDescuento(pct: Double) {
precio -= precio * pct / 100
}
}
05
PILAR 1 DE 4
Encapsulamiento
Consiste en ocultar el estado interno de un objeto y exponer
únicamente lo necesario a través de una interfaz controlada, evitando
modificaciones inválidas desde fuera de la clase.
private
protected
public
class CuentaBancaria(
val titular: String,
saldoInicial: Double
) {
private var saldo = saldoInicial
fun consultarSaldo() = saldo
Solo visible dentro de la propia clase
fun depositar(monto: Double) {
require(monto > 0)
saldo += monto
}
Visible en la clase y sus subclases
Visible desde cualquier parte (por defecto en Kotlin)
fun retirar(monto: Double) {
require(monto in 0.0..saldo)
saldo -= monto
}
}
06
PILAR 2 DE 4
Herencia
Permite que una clase (subclase) reutilice y extienda los atributos y
métodos de otra (superclase), modelando relaciones del tipo "es un".
En Kotlin, la superclase debe marcarse como open para poder
heredarse.
Vehiculo
Auto
Motocicleta
open class Vehiculo(
val marca: String,
protected var velocidad: Int = 0
) {
open fun acelerar() {
velocidad += 10
}
fun estado() =
"$marca a $velocidad km/h"
}
class Auto(marca: String) :
Vehiculo(marca) {
override fun acelerar() {
velocidad += 20 // más potencia
}
}
val auto = Auto("Toyota")
auto.acelerar()
println(auto.estado()) // 20 km/h
07
PILAR 3 DE 4
Polimorfismo
Es la capacidad de que un mismo mensaje (método) produzca
comportamientos distintos según el tipo real del objeto que lo recibe.
Permite tratar objetos de distintas subclases de forma uniforme.
abstract class Figura {
abstract fun area(): Double
}
class Circulo(val r: Double) : Figura() {
override fun area() = Math.PI * r * r
}
Salida en tiempo de ejecución
▸
Circulo(5.0)
Área: 78.54
▸
Cuadrado(4.0)
Área: 16.00
El bucle recorre la lista de figuras y llama a figura.area() sin conocer el tipo
concreto de cada una: cada clase responde con su propia implementación.
class Cuadrado(val lado: Double) : Figura() {
override fun area() = lado * lado
}
val figuras: List<Figura> = listOf(
Circulo(5.0), Cuadrado(4.0)
)
for (figura in figuras) {
println("Área: ${figura.area()}")
}
08
PILAR 4 DE 4
Abstracción
Consiste en modelar únicamente los aspectos esenciales de un objeto
para el problema que se resuelve, ocultando los detalles de
implementación. Se expresa mediante clases abstractas e interfaces.
interface Notificable {
fun notificar(msg: String)
}
abstract class Empleado(val nombre: String) {
abstract fun calcularSueldo(): Double
fun presentarse() = "Soy $nombre"
}
Clase abstracta
Puede tener métodos con y sin implementación, y guardar estado. No se
puede instanciar directamente.
class Vendedor(
nombre: String, val comision: Double
) : Empleado(nombre), Notificable {
override fun calcularSueldo() =
1200.0 + comision
Interfaz
override fun notificar(msg: String) {
println("Para $nombre: $msg")
}
Define un contrato de comportamiento (qué debe hacer una clase), sin
guardar estado propio.
}
09
COMPARACIÓN
Interfaces vs. Clases Abstractas
Criterio
Interfaz
Clase abstracta
Estado (propiedades con valor)
No permite
Sí permite
Una clase puede implementar varias
Solo se extiende de una
No tiene
Sí tiene
Sí, desde Kotlin (default)
Sí, libremente
Definir un contrato / capacidad
Compartir código base común
Herencia múltiple
Constructores
Métodos con implementación
Uso típico
10
BUENAS DECISIONES DE DISEÑO
Composición vs. Herencia
"Favorece la composición sobre la herencia"
Herencia — relación "es un"
Composición — relación "tiene un"
•
Un Auto es un Vehiculo
•
Un Auto tiene un Motor
•
Reutiliza comportamiento heredando de una superclase
•
Combina objetos independientes para lograr un comportamiento
•
Acopla fuertemente subclase y superclase
•
Más flexible: los componentes se pueden intercambiar
class Auto : Vehiculo() {
// hereda todo de Vehiculo
}
class Auto(
val motor: Motor
) {
fun arrancar() = motor.encender()
}
11
BUENAS PRÁCTICAS DE DISEÑO OO
Principios SOLID
S
Responsabilidad única
O
Abierto / cerrado
Una clase debe tener una sola razón para
cambiar
Abierta a extensión, cerrada a modificación
Segregación de interfaces
Inversión de dependencias
I
Interfaces pequeñas y específicas, no
genéricas
D
L
Sustitución de Liskov
Una subclase debe poder reemplazar a su
superclase
Depender de abstracciones, no de
implementaciones
12
CASO INTEGRADOR · PARTE 1
Sistema de Empleados
• Empleado (abstracta): define el contrato calcularSueldo() —
abstracción
• nombre y sueldoBase son privados/protegidos — encapsulamiento
• Desarrollador y Gerente extienden Empleado — herencia
abstract class Empleado(
protected val nombre: String,
protected val sueldoBase: Double
) {
abstract fun calcularSueldo(): Double
• Cada subclase calcula su sueldo de forma distinta — polimorfismo
fun resumen() =
"$nombre: S/ ${calcularSueldo()}"
}
class Desarrollador(
nombre: String, sueldoBase: Double,
val bonoPorProyecto: Double
) : Empleado(nombre, sueldoBase) {
Al recorrer la lista y llamar a e.calcularSueldo(), cada empleado
responde con su propio cálculo sin necesidad de preguntar su tipo.
override fun calcularSueldo() =
sueldoBase + bonoPorProyecto
}
13
CASO INTEGRADOR · PARTE 2
Ejecución y Resultado
Salida por consola
Ana: S/ 3800.0
class Gerente(
nombre: String, sueldoBase: Double,
val bonoGestion: Double
) : Empleado(nombre, sueldoBase) {
Luis: S/ 6500.0
Dos tipos de empleado, una sola llamada de método: e.calcularSueldo().
Conceptos aplicados
•
Abstracción → Empleado como contrato
•
Herencia → Desarrollador y Gerente
•
Polimorfismo → calcularSueldo() distinto por tipo
•
Encapsulamiento → estado protegido
override fun calcularSueldo() =
sueldoBase + bonoGestion * 1.5
}
fun main() {
val equipo: List<Empleado> = listOf(
Desarrollador("Ana", 3000.0, 800.0),
Gerente("Luis", 4000.0, 1666.67)
)
equipo.forEach {
println(it.resumen())
}
}
14
RECOMENDACIONES
Buenas Prácticas en Diseño OO
Nombrar con intención
✓
Clases como sustantivos, métodos como verbos y en el dominio del
problema
Minimizar la visibilidad
✓
Preferir val sobre var
✓
Reduce el estado mutable y los efectos secundarios inesperados
Programar contra interfaces
✓
Clases pequeñas y enfocadas
✓
Cada clase debe resolver un único propósito claro
Empezar en private y abrir solo lo que otros módulos realmente
necesitan
Depender de abstracciones facilita pruebas y cambios futuros
Evitar jerarquías profundas
✓
Más de 2-3 niveles de herencia suele indicar un mal diseño
15
REPASO RÁPIDO
Resumen de los 4 Pilares
01
02
03
04
Encapsulamiento
Herencia
Polimorfismo
Abstracción
private / public + getters
open class + :
SuperClase()
override + una lista, N
comportamientos
abstract class / interface
16
{}
¿Preguntas?
Gracias por su atención — Fundamentos de Programación Orientada a Objetos
0
You can add this document to your study collection(s)
Sign in Available only to authorized usersYou can add this document to your saved list
Sign in Available only to authorized users(For complaints, use another form )