Ir al contenido
wannacode

Principios de la POO

En resumen: La programación orientada a objetos se apoya en cuatro principios: encapsulamiento, herencia, polimorfismo y abstracción. Protegen los datos, escriben una sola vez lo común y sustituyen las cadenas de if sobre el tipo de objeto.

Problema

Nóminas sin principios

A final de mes la empresa paga a su gente. El programa se escribió con prisa: una sola clase con campos públicos, el rol es texto, y el pago se calcula en dos lugares. La función monthly_pay hace la nómina, y transfer envía el dinero al banco.

class Employee:
def __init__(self, name, role, salary, bonus):
self.name = name
self.role = role
self.salary = salary
self.bonus = bonus
def monthly_pay(e):
if e.role == "gerente":
return e.salary + e.bonus
elif e.role == "becario":
return e.salary // 2
return e.salary
def transfer(staff):
total = 0
for e in staff:
amount = e.salary
if e.role == "gerente":
amount += e.bonus
print(f"Transferencia: {e.name} {amount} €")
total += amount
print(f"Total: {total} €")

Hoy contabilidad introduce un aumento para Juan. Pero en el formulario pone -10 en lugar de 10:

staff = [
Employee("Juan", "empleado", 2000, 0),
Employee("María", "gerente", 3000, 500),
Employee("Ana", "becario", 1600, 0),
]
percent = -10
staff[0].salary += staff[0].salary * percent // 100
for e in staff:
print(f"Nómina: {e.name} {monthly_pay(e)} €")
transfer(staff)
Salida
Nómina: Juan 1800 €
Nómina: María 3500 €
Nómina: Ana 800 €
Transferencia: Juan 1800 €
Transferencia: María 3500 €
Transferencia: Ana 1600 €
Total: 6900 €

El programa no se detiene y no se queja. Pero la salida tiene tres grietas:

  1. Una escritura incorrecta. El salario de Juan bajó a 1800 €. El campo está abierto a cualquier línea del programa, y nadie pregunta si -10% es un aumento.
  2. Dos copias de una regla. La nómina de Ana dice 800 €, y el banco le envía 1600 €. La regla «un becario cobra la mitad» está en la nómina, pero alguien se la olvidó en la transferencia. La copia se alejó del original, y nadie se dio cuenta.
  3. Un tipo nuevo, muchos arreglos. Mañana llega un trabajador autónomo. Necesitas un elif nuevo en la nómina, en la transferencia, en los impuestos, en las vacaciones, en todos los sitios donde alguien pregunta por el rol. Y cada empleado ya lleva un campo bonus, aunque solo el gerente tiene bono.

La programación orientada a objetos es la respuesta justo a estas tres cosas.

La idea

Los cuatro principios

La programación orientada a objetos (POO) organiza un programa alrededor de objetos: datos junto con los métodos que los tocan. Se apoya en cuatro principios. Ya has trabajado con cada uno de ellos en los módulos «Clases y objetos» y «Herencia y polimorfismo»:

Principio Qué dice Qué grieta arregla
Encapsulamiento El objeto protege sus propios datos. Desde fuera llegas a ellos solo a través de métodos que comprueban. La escritura incorrecta: -10% se rechaza.
Herencia Lo que comparten varias clases se escribe una sola vez, en una clase padre. El bono vive solo en Manager, y lo común (el nombre, el salario, la comprobación del aumento) se escribe una sola vez en Employee.
Polimorfismo Una sola llamada, y cada objeto responde a su manera. El if sobre el rol: cada clase calcula su propio pago, sin segunda copia.
Abstracción El código depende de qué puede hacer un objeto, no de cómo lo hace. Un tipo de pago nuevo entra sin tocar la transferencia.

En resumen: el encapsulamiento protege, la herencia comparte, el polimorfismo elige, la abstracción promete.

A partir de aquí, cada principio arregla una grieta del programa de nóminas. Al final los verás juntos, en el mismo programa.

La idea

Encapsulamiento: el objeto protege sus datos

El salario pertenece al empleado. Que solo lo cambie el empleado. El campo pasa a ser interno, con un guion bajo, y el aumento es un método que comprueba:

class Employee:
def __init__(self, name, salary):
self._name = name
self._salary = salary
@property
def name(self):
return self._name
@property
def salary(self):
return self._salary
def raise_salary(self, percent):
if percent > 0:
self._salary += self._salary * percent // 100
ivan = Employee("Juan", 2000)
ivan.raise_salary(-10)
print(ivan.salary)
ivan.raise_salary(10)
print(ivan.salary)
Salida
2000
2200

-10% se rechaza, 10% pasa. La comprobación está en un solo lugar, la clase, y todo código que quiera cambiar el salario pasa por ella. Esto es encapsulamiento: los datos y sus reglas están juntos en el objeto, y desde fuera solo ves lo que la clase decide mostrar.

Python no tiene private. El guion bajo es un acuerdo entre programadores: «interno, no lo toques desde fuera». El lenguaje no detiene a nadie. Una línea que escribe directamente en el campo se ejecuta:

ivan._salary = -2000
print(ivan.salary)
Salida
-2000

Así que en Python el encapsulamiento lo protegen las personas que leen el código, y herramientas como PyCharm, que subrayan una línea así. Desde fuera el salario se lee a través de la propiedad salary, que no tiene setter, y solo se cambia con raise_salary().

Cuándo duele: getters y setters para todo. Si cada campo tiene un getter y un setter que escribe lo que le des, el campo sigue abierto, solo que con más líneas. El encapsulamiento no es «un campo oculto más get y set». Es: el objeto decide qué puede cambiar y cómo. Por eso Employee no tiene ningún método que escriba el salario, solo un aumento con comprobación. Empieza con todo oculto y abre solo lo que haga falta.

Más precisamente, para curiosos
  • Con dos guiones bajos (self.__salary) Python cambia el nombre del campo a _Employee__salary. Desde fuera ivan.__salary ya no existe, pero el nombre nuevo sigue llegando a él:
class Employee:
def __init__(self, name, salary):
self.__salary = salary
ivan = Employee("Juan", 2000)
try:
print(ivan.__salary)
except AttributeError as error:
print(error)
print(ivan._Employee__salary)
Salida
'Employee' object has no attribute '__salary'
2000

Esto protege de un choque accidental de nombres en las clases hijas, no de alguien que toca el campo a propósito. Normalmente basta con un guion bajo.

Pruébalo

¿Qué oculta el salario?

El salario es interno de la clase. ¿Cómo se llama el campo por acuerdo (con un solo guion bajo)?
class Employee:
    def __init__(self, name, salary):
        self._name = name
        self. = salary

La idea

Herencia: lo común, una sola vez

Un gerente es un empleado con un bono. En lugar de una copia de Employee con un campo más, Manager lo hereda todo y solo añade el bono:

class Manager(Employee):
def __init__(self, name, salary, bonus):
super().__init__(name, salary)
self._bonus = bonus
@property
def bonus(self):
return self._bonus
maria = Manager("María", 3000, 500)
maria.raise_salary(-10)
maria.raise_salary(10)
print(f"{maria.name}: {maria.salary} + {maria.bonus}")
Salida
María: 3300 + 500

El gerente no tiene su propia comprobación del aumento. Toma la de Employee: -10% se rechaza, y 10% sube 3000 a 3300. Si mañana cambia la regla de los aumentos, la cambias en un solo lugar y vale para todas las clases hijas. Esto es herencia: lo común está en la clase padre, y la clase hija escribe solo su propia parte.

super().__init__(name, salary) pasa el nombre y el salario al __init__ de la clase padre, y después Manager los lee a través de la propiedad salary. En Python una clase puede tener varias clases padre (la lección «Herencia»), pero lo mejor es que solo una de ellas tenga campos y código.

La prueba de si la herencia tiene sentido es la frase «X es un Y». «Un gerente es un empleado» es cierto.

Cuándo duele: cadenas largas y heredar por el código ya hecho. Employee → Manager → SeniorManager → RegionalSeniorManager: para entender qué hace la clase de abajo, lees cuatro clases, y un cambio arriba mueve todo lo que hay debajo. Otro error común es heredar de una clase solo para tener un método ya hecho: una factura que extiende Employee para tener un nombre. «Una factura es un empleado» no es cierto, y recibiría un salario y un aumento. Qué hacer en su lugar está en la tarjeta «Qué se suele confundir», más abajo.

La idea

Polimorfismo: cada clase responde por sí misma

Ahora la grieta de las copias. En lugar de un if sobre el rol, cada clase calcula su propio pago mensual. La clase padre tiene una versión general, y las clases hijas la sobrescriben:

class Employee:
# ... __init__, salary y raise_salary() de antes
def monthly_pay(self):
return self._salary
class Manager(Employee):
# ... __init__ y bonus
def monthly_pay(self):
return self.salary + self._bonus
class Intern(Employee):
def monthly_pay(self):
return self.salary // 2

Tanto la nómina como la transferencia llaman al mismo método:

def transfer(staff):
total = 0
for e in staff:
print(f"Transferencia: {e.name} {e.monthly_pay()} €")
total += e.monthly_pay()
print(f"Total: {total} €")
staff = [
Employee("Juan", 2000),
Manager("María", 3000, 500),
Intern("Ana", 1600),
]
for e in staff:
print(f"Nómina: {e.name} {e.monthly_pay()} €")
transfer(staff)
Salida
Nómina: Juan 2000 €
Nómina: María 3500 €
Nómina: Ana 800 €
Transferencia: Juan 2000 €
Transferencia: María 3500 €
Transferencia: Ana 800 €
Total: 6300 €

Ana recibe 800 € tanto en la nómina como en la transferencia. La regla del becario vive solo en Intern, y no hay una segunda copia que se aleje. Un tipo nuevo de empleado es una clase nueva con su propio método, y el bucle y la transferencia quedan intactos. Esto es polimorfismo: una sola llamada, e.monthly_pay(), y cada objeto responde a su manera.

Python busca el método por su nombre: primero en la clase del objeto, después en su clase padre. No se comprueba nada por adelantado. Cualquier objeto que tenga un método con ese nombre sirve para el bucle, sea de la clase que sea. Esto se llama duck typing: «si camina como un pato y grazna como un pato, es un pato».

Cuándo duele. Dos casos que nunca llegarán a ser tres no necesitan una jerarquía de clases: un solo if es más corto y más claro. Además: una clase hija que sobrescribe el método pero rompe la promesa de la clase padre (un pago que sale negativo, o un método que detiene el programa) rompe todos los bucles que confían en la clase padre.

Pruébalo

¿Cuánto enviará el banco?

Las clases son de las tarjetas de arriba, junto con el contrato de pago de la tarjeta siguiente: amount_due() está en Employee y devuelve self.monthly_pay().

e = Intern("Ana", 1600)
e.raise_salary(10)
print(e.amount_due())
Respuesta

La idea

Abstracción: qué, no cómo

Contabilidad quiere las facturas de los proveedores en la misma transferencia. Una factura no es un empleado. Pero a la transferencia no le importa qué tipo de cosa es cada uno. Necesita dos cosas: a quién, y cuánto. Ese es el contrato:

class Payable(ABC):
@abstractmethod
def amount_due(self):
pass
@abstractmethod
def payee(self):
pass
class Employee(Payable):
# ... todo lo de antes
def amount_due(self):
return self.monthly_pay()
def payee(self):
return self._name
def transfer(payments):
total = 0
for p in payments:
print(f"Transferencia: {p.payee()} {p.amount_due()} €")
total += p.amount_due()
print(f"Total: {total} €")
class Invoice(Payable):
def __init__(self, supplier, amount):
self._supplier = supplier
self._amount = amount
def amount_due(self):
return self._amount
def payee(self):
return self._supplier
payments = [
Employee("Juan", 2000),
Manager("María", 3000, 500),
Intern("Ana", 1600),
Invoice("Compañía eléctrica", 340),
]
transfer(payments)
Salida
Transferencia: Juan 2000 €
Transferencia: María 3500 €
Transferencia: Ana 800 €
Transferencia: Compañía eléctrica 340 €
Total: 6640 €

transfer no sabe nada de salarios, bonos y facturas. Solo conoce el contrato. Esto es abstracción: muestras qué puede hacer un objeto y escondes cómo lo hace. La transferencia depende de la abstracción, así que todo lo que la cumple entra sin ningún cambio en la transferencia.

Python no tiene interface. El contrato es una clase abstracta (ABC) en la que cada método tiene @abstractmethod. La comprobación se hace cuando se crea un objeto: una clase que pone Payable entre sus paréntesis pero no ha escrito amount_due() no puede tener objetos. El propio transfer no comprueba nada: cualquier objeto con estos dos métodos pasa por él.

Aquí el contrato es una clase abstracta sin código. Una clase abstracta también puede llevar código común: Shape promete un área, y cada figura dice cómo se calcula («Clases abstractas»). Cuándo usar cada una está en la lección «Interfaces».

Cuándo duele: abstracción demasiado pronto. Un contrato con una sola implementación, «por si acaso», es un nombre más, un archivo más y un salto más mientras lees. La abstracción compensa cuando hay al menos dos implementaciones, o cuando necesitas cambiar la que tienes, por ejemplo en un test. Payable apareció cuando llegó la factura, no antes.

Míralo

Los cuatro juntos

La escena recorre el programa desde el principio: cada principio arregla su propia grieta, y los números de abajo salen del mismo programa.

staff = [
Employee("Juan", 2000),
Manager("María", 3000, 500),
Intern("Ana", 1600),
]
staff[0].raise_salary(-10)
for e in staff:
print(f"Nómina: {e.name} {e.monthly_pay()} €")
payments = [staff[0], staff[1], staff[2], Invoice("Compañía eléctrica", 340)]
transfer(payments)
Paso a paso, en palabras
  1. Antes: el rol es texto, la regla del pago está copiada en dos cadenas de if, y cualquiera escribe el salario. Juan queda en 1800 €, y la nómina y la transferencia de Ana no coinciden.
  2. Encapsulamiento: el salario está oculto, y raise_salary() rechaza -10%. Juan se queda en 2000 €.
  3. Herencia: Manager toma de Employee el nombre, el salario y la comprobación del aumento, y solo añade el bono. Los objetos ya tienen clases, no un rol en texto.
  4. Polimorfismo: cada clase calcula su propio monthly_pay(). La transferencia llama al mismo método que la nómina, y Ana recibe 800 € en las dos.
  5. Abstracción: la transferencia solo conoce el contrato Payable. La factura de la luz entra sin ser un empleado.
  6. Las cuatro juntas: el mismo -10% se rechaza, cada nómina coincide con su transferencia, y el total es 6640 €.

Aquí está el programa antes y después, con un 10% para Juan. El procedimiento «antes» se ha arreglado a mano: la regla del becario también se ha añadido a la transferencia.

Antes
def monthly_pay(e):
if e.role == "gerente":
return e.salary + e.bonus
elif e.role == "becario":
return e.salary // 2
return e.salary
def transfer(staff):
total = 0
for e in staff:
amount = e.salary
if e.role == "gerente":
amount += e.bonus
elif e.role == "becario":
amount = e.salary // 2
print(f"Transferencia: {e.name} {amount} €")
total += amount
print(f"Total: {total} €")
staff = [
Employee("Juan", "empleado", 2000, 0),
Employee("María", "gerente", 3000, 500),
Employee("Ana", "becario", 1600, 0),
]
percent = 10
staff[0].salary += staff[0].salary * percent // 100
for e in staff:
print(f"Nómina: {e.name} {monthly_pay(e)} €")
transfer(staff)
Salida
Nómina: Juan 2200 €
Nómina: María 3500 €
Nómina: Ana 800 €
Transferencia: Juan 2200 €
Transferencia: María 3500 €
Transferencia: Ana 800 €
Total: 6500 €
Después
def transfer(payments):
total = 0
for p in payments:
print(f"Transferencia: {p.payee()} {p.amount_due()} €")
total += p.amount_due()
print(f"Total: {total} €")
staff = [
Employee("Juan", 2000),
Manager("María", 3000, 500),
Intern("Ana", 1600),
]
staff[0].raise_salary(10)
for e in staff:
print(f"Nómina: {e.name} {e.monthly_pay()} €")
transfer(staff)
Salida
Nómina: Juan 2200 €
Nómina: María 3500 €
Nómina: Ana 800 €
Transferencia: Juan 2200 €
Transferencia: María 3500 €
Transferencia: Ana 800 €
Total: 6500 €

La salida es la misma. La diferencia está en lo que pase mañana:

  • Alguien introduce -10 → raise_salary() lo rechaza (encapsulamiento).
  • Una regla nueva para los aumentos → cambia en un solo lugar, Employee (herencia).
  • Un tipo nuevo de empleado → una clase nueva con su propio monthly_pay(); la nómina y la transferencia quedan intactas (polimorfismo).
  • Una cosa nueva que pagar → una clase nueva que cumple el contrato; la transferencia queda intacta (abstracción).

El mismo programa con el -10% incorrecto del problema y con la factura:

staff = [
Employee("Juan", 2000),
Manager("María", 3000, 500),
Intern("Ana", 1600),
]
staff[0].raise_salary(-10)
for e in staff:
print(f"Nómina: {e.name} {e.monthly_pay()} €")
payments = [staff[0], staff[1], staff[2], Invoice("Compañía eléctrica", 340)]
transfer(payments)
Salida
Nómina: Juan 2000 €
Nómina: María 3500 €
Nómina: Ana 800 €
Transferencia: Juan 2000 €
Transferencia: María 3500 €
Transferencia: Ana 800 €
Transferencia: Compañía eléctrica 340 €
Total: 6640 €
Todas las clases en un solo lugar
from abc import ABC, abstractmethod
class Payable(ABC):
@abstractmethod
def amount_due(self):
pass
@abstractmethod
def payee(self):
pass
class Employee(Payable):
def __init__(self, name, salary):
self._name = name
self._salary = salary
@property
def name(self):
return self._name
@property
def salary(self):
return self._salary
def raise_salary(self, percent):
if percent > 0:
self._salary += self._salary * percent // 100
def monthly_pay(self):
return self._salary
def amount_due(self):
return self.monthly_pay()
def payee(self):
return self._name
class Manager(Employee):
def __init__(self, name, salary, bonus):
super().__init__(name, salary)
self._bonus = bonus
@property
def bonus(self):
return self._bonus
def monthly_pay(self):
return self.salary + self._bonus
class Intern(Employee):
def monthly_pay(self):
return self.salary // 2
class Invoice(Payable):
def __init__(self, supplier, amount):
self._supplier = supplier
self._amount = amount
def amount_due(self):
return self._amount
def payee(self):
return self._supplier

Pruébalo

Ordena el gerente

Las líneas de Manager están desordenadas. Ponlas en el orden habitual: el constructor, que primero llama a la clase padre, y después el método del pago.

  1. class Manager(Employee):
  2. return self.salary + self._bonus
  3. super().__init__(name, salary)
  4. def __init__(self, name, salary, bonus):
  5. def monthly_pay(self):
  6. self._bonus = bonus

Pruébalo

Encuentra el error

Un colega «simplificó» el contrato en Employee. El programa es el de «Los cuatro juntos», y imprime:

Salida
Nómina: Juan 2200 €
Nómina: María 3500 €
Nómina: Ana 800 €
Transferencia: Juan 2200 €
Transferencia: María 3000 €
Transferencia: Ana 1600 €
Total: 6800 €

La nómina de María es de 3500 €, y el banco le envía 3000 €. ¿En qué línea está el error?

Elige la línea con el error.

La idea

Qué se suele confundir

¿Abstracción o encapsulamiento? Los dos esconden algo, pero cosas distintas. El encapsulamiento esconde los datos de un objeto para que nadie de fuera los pueda romper: el salario está oculto, el aumento comprueba. La abstracción esconde las diferencias entre muchas clases detrás de una promesa común: la transferencia solo ve amount_due() y payee(). El encapsulamiento mira el objeto desde dentro, la abstracción mira desde fuera el código que lo usa.

¿Herencia o composición? La herencia dice «es un»: un gerente es un empleado. La composición dice «tiene un»: un empleado tiene una cuenta bancaria. Si haces al empleado clase hija de la cuenta de la lección «Encapsulamiento» para tener deposit() gratis,

class Employee(Account):
# ... name y salary

también recibirá los retiros, un saldo y todo lo que la cuenta aprenda mañana. Y «un empleado es una cuenta» no es cierto. En su lugar, la cuenta es un campo del empleado:

class Employee:
def __init__(self, name, salary, account):
self._name = name
self._salary = salary
self._account = account
def payday(self):
self._account.deposit(self._salary)
account = Account(100)
ivan = Employee("Juan", 2000, account)
ivan.payday()
print(account.balance)
Salida
2100

Por eso el consejo habitual es «prefiere la composición a la herencia». Hereda solo cuando «X es un Y» es cierto y lo seguirá siendo. Si no, deja que el objeto tenga al otro como campo.

¿Sobrecarga o sobrescritura? En Java y C# a las dos a veces se las llama polimorfismo. La sobrecarga son varios métodos con un mismo nombre y parámetros distintos. Python no la tiene: una segunda función con el mismo nombre reemplaza a la primera, y en su lugar hay valores por defecto (la lección sobre ellos). El polimorfismo en la POO es la sobrescritura: la elección la hace el objeto, mientras el programa se ejecuta.

Más precisamente, para curiosos
  • Algunos libros enumeran tres pilares: encapsulamiento, herencia y polimorfismo. La abstracción es entonces la idea común que está debajo. En una entrevista, lo que se suele esperar son cuatro.
  • Los principios no son reglas que se cumplen «al 100 %». Cada uno tiene un coste: los campos ocultos significan más métodos, las jerarquías más clases, los contratos un nombre más. Los usas donde la grieta es real.
  • Después de estos cuatro vienen los cinco principios SOLID para un código que aguanta los cambios, y los patrones de diseño: soluciones ya hechas construidas con los mismos cuatro. Están en el curso «Ingeniería de software»: SOLID y Patrones de diseño.

Ejercicio

¿Qué principio está roto?

Una pregunta clásica de entrevista. Para cada fragmento, responde en voz alta: qué principio está roto, qué se va a romper y cómo lo arreglas.

1.

class Cart:
def __init__(self):
self.total = 0
# ... en 12 lugares del programa:
cart.total -= discount

2.

def area(s):
if isinstance(s, Circle):
return 3.14 * s.r * s.r
if isinstance(s, Rectangle):
return s.w * s.h
return 0

3.

class Invoice(Employee):
# recibe name y payee() ya hechos
pass

4.

class SalaryProvider(ABC):
@abstractmethod
def provide(self, e):
pass
# la única implementación:
class DefaultSalaryProvider(SalaryProvider):
def provide(self, e):
return e.monthly_pay()
Las respuestas
  1. Encapsulamiento. El total está abierto y se reduce desde 12 lugares. Un descuento mayor que el total lo deja negativo, y nadie pregunta. El total pasa a ser _total, y el descuento es un método del carrito que comprueba.
  2. Polimorfismo (y abstracción). Un if sobre el tipo de figura: una figura nueva significa una línea nueva aquí y en todos los sitios donde alguien pregunta por el tipo. El área pasa a ser un método abstracto de Shape, y cada figura escribe el suyo.
  3. Herencia en el lugar equivocado. «Una factura es un empleado» no es cierto: recibe un salario, un aumento y un pago mensual. Lo que comparten es que se les paga: un contrato Payable.
  4. Abstracción demasiado pronto. Un contrato con una sola implementación que solo pasa la llamada a monthly_pay() es un nombre más y un salto más al leer, y no protege de nada. Quítalo y llama al método directamente. El contrato tendrá sentido cuando aparezca una segunda implementación.

Ejercicio

Un autónomo y una terraza

Resuélvelas en tu editor.

1. Un autónomo. Contractor (Pablo, 40 horas a 25 €) no es un empleado: no hay nómina, pero tiene que estar en la transferencia. Añádelo de manera que transfer no cambie ni una línea. Pon también en la transferencia una factura de alquiler, 1800 € para el casero.

Salida
Transferencia: Juan 2000 €
Transferencia: María 3500 €
Transferencia: Ana 800 €
Transferencia: Pablo 1000 €
Transferencia: Casero 1800 €
Total: 9100 €

2. La terraza sin if. Este procedimiento calcula el área según el tipo de figura:

def area(kind, a, b):
if kind == "rectángulo":
return a * b
if kind == "triángulo":
return a * b // 2
if kind == "cuadrado":
return a * a
return 0

Escríbelo con un Shape abstracto y las clases Rectangle, Triangle y Square. La terraza es un rectángulo de 4 × 3, un triángulo con base 2 y altura 3, y un cuadrado de lado 2. Un cuadrado es un rectángulo con los lados iguales: escríbelo como clase hija de Rectangle, sin fórmula de área propia.

Salida
Rectángulo: 12 m²
Triángulo: 3 m²
Cuadrado: 4 m²
Total: 19 m²

Más tareas

  • Una nómina también para el autónomo. Si al final el autónomo quiere una nómina, sin pasar a ser un empleado: ¿qué tiene que estar en el contrato, y quién lo cumple?
  • Pagar en una cuenta. Dale al empleado una cuenta (composición, como en «Qué se suele confundir») y un método que, el día de pago, ingrese monthly_pay(), no el salario. El gerente también tiene que recibir el bono, sin tocar monthly_pay() en Manager.
  • Un aumento para todos. Sube el salario de cada empleado un 5% en un solo bucle. ¿Qué clases tocas?

Test

Ponte a prueba

1. El salario está oculto, y solo cambia a través de raise_salary(), que rechaza un porcentaje menor que cero. ¿Qué principio es este?
2. La nómina y la transferencia calculan el pago con dos copias del if sobre el rol, y una de ellas se olvidó del becario. ¿Qué principio elimina la segunda copia?
3. ¿En qué se diferencian la abstracción y el encapsulamiento?
4. Una factura necesita un nombre y un importe. Un colega propone que Invoice extienda Employee para tenerlos gratis. ¿Por qué no?
5. ¿Qué necesita un objeto para pasar por transfer()?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·