¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Principios de diseño orientado a objetos

Después de esta lección: Puedo detectar código que da dos trabajos a una clase o que cuesta ampliar.

Lección de 70 min5 comandosLaboratorio de comandos de Linux de cada día: los treinta comandos que se escriben de memoria

Una clase que hace tres trabajos

El principio de responsabilidad única dice que una clase debe tener un solo motivo para cambiar. Si una clase hace tres trabajos, un cambio en uno puede romper los otros dos.

Una tienda tiene una clase Factura que calcula el total, da formato al texto y lo guarda en un archivo. Cuando cambia el IVA, cambia el aspecto o cambia el sitio donde se guarda, hay que abrir la misma clase.

Al final de esta lección

Nota

Puedo detectar código que da dos trabajos a una clase o que cuesta ampliar.

Palabras clave

PalabraQué significa
Responsabilidadun trabajo que una clase tiene a su cargo
Responsabilidad únicauna clase, un solo motivo para cambiar
Cohesióncuánto encaja lo que hace una clase
Abierto y cerradose amplía añadiendo código, no editando el que funciona
Extenderdar una función nueva sin tocar lo anterior

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Una clase, un solo motivo para cambiar

Una responsabilidad es un trabajo, y cada trabajo cambia por una razón y a petición de una persona distinta.

Cuando una clase mezcla trabajos, tocar uno obliga a repasar los demás. Una clase con cohesión alta hace cosas que encajan entre sí.

Ejemplo: tres trabajos y tres motivos de cambio

TrabajoQuién pide el cambioUn cambio posible
Calcular el totalContabilidadCambia el IVA
Dar formato al textoDiseñoHay una cabecera nueva
Guardar la facturaInformáticaSe guarda en otro sitio

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Cuenta los trabajos de una clase

Guarda la clase Factura con sus tres trabajos en factura.py. Con grep listas sus métodos y los cuentas. Cada método es una pista, no una prueba.

La orden grep -n "def " imprime cada línea que contiene def con su número de línea.

Entra en la carpeta de trabajo

Comando
mkdir -p ~/oop && cd ~/oop

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Escribe una Factura que hace tres trabajos

Comando
printf 'class Factura:\n    def __init__(self, cliente):\n        self.cliente = cliente\n        self.lineas = []\n\n    def anadir(self, concepto, precio):\n        self.lineas.append([concepto, precio])\n\n    def total(self):\n        suma = 0\n        for linea in self.lineas:\n            suma = suma + linea[1]\n        return suma\n\n    def como_texto(self):\n        texto = "Cliente: " + self.cliente\n        for linea in self.lineas:\n            texto = texto + "\\n" + linea[0] + ": " + str(linea[1])\n        return texto\n\n    def guardar(self, archivo):\n        with open(archivo, "w") as f:\n            f.write(self.como_texto())\n' > factura.py

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Lista los métodos con su línea

Comando
grep -n "def " factura.py

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Cuenta cuántos métodos hay

Comando
grep -c "def " factura.py

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Usa la clase: total y texto

Comando
python3 -c "from factura import Factura; f = Factura('Ana'); f.anadir('cafe', 2); f.anadir('bocadillo', 4); print(f.total()); print(f.como_texto())"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Un error frecuente

Advertencia

Mucha gente cree que una responsabilidad es un método. No es así. Una responsabilidad es un motivo para cambiar. Una clase puede tener cinco métodos y una sola responsabilidad. Si dos métodos cambian por razones distintas, sepáralos.

Se separa cada trabajo en su propia clase

La Factura guarda los datos y calcula el total. Otra clase da formato al texto. Otra la guarda. Cada una se puede probar sola.

Mira el código: FormateadorTexto recibe la factura y no la modifica. Si cambia la cabecera, solo se toca esa clase.

La factura y su formateador por separado

Código: python
# Cada clase tiene un solo motivo para cambiar.
class Factura:
    def __init__(self, cliente):
        self.cliente = cliente
        self.lineas = []

    def anadir(self, concepto, precio):
        self.lineas.append([concepto, precio])

    def total(self):
        suma = 0
        for linea in self.lineas:
            suma = suma + linea[1]
        return suma

class FormateadorTexto:
    def formatear(self, factura):
        texto = "Cliente: " + factura.cliente
        for linea in factura.lineas:
            texto = texto + "\n" + linea[0] + ": " + str(linea[1])
        return texto

factura = Factura("Ana")
factura.anadir("cafe", 2)
factura.anadir("bocadillo", 4)
print(factura.total())
print(FormateadorTexto().formatear(factura))

Abierto para extender, cerrado para modificar

Un cambio ideal se hace añadiendo código nuevo, sin editar el que ya funciona. Es el principio abierto y cerrado.

Una cadena de if y elif por tipo es una mala señal. Cada tipo nuevo obliga a editar la función. Con una clase por tipo, un formato nuevo es una clase nueva.

Cadena de if frente a clase por formato

Código: python
# Mal: cada formato nuevo obliga a editar esta función.
def formatear(factura, tipo):
    if tipo == "texto":
        return texto_plano(factura)
    elif tipo == "csv":
        return texto_csv(factura)
    # Un formato nuevo es otro elif aquí.

# Bien: cada formato es una clase con el mismo método.
class FormateadorCsv:
    def formatear(self, factura):
        return texto_csv(factura)
# Un formato nuevo es otra clase, y nada de lo anterior cambia.

Sobre este laboratorio y su código: Laboratorio de comandos de Linux de cada día: los treinta comandos que se escriben de memoria

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.