Apex Academy
Módulo 5·10·26 min

Interfaces y polimorfismo

Una interfaz es un contrato: «quien me implemente tendrá estos métodos». El polimorfismo es tratar igual a objetos distintos que cumplen el mismo contrato.

En Admin esSchedule Apex en Setup: solo aparecen las clases que implementan Schedulable

✓ En esta sub-lección

  1. 01Declarar una interfaz e implementarla en varias clases.
  2. 02Recorrer una lista de objetos distintos que cumplen el mismo contrato y tratarlos igual.
  3. 03Elegir entre interfaz y clase abstracta según haya o no código común.

La nutria: ¿Te acuerdas? · Repaso de la lección 9

format(Decimal) y format(Decimal, String) en la misma clase son…

La herencia dice «es un». Pero muchas veces lo que importa no es qué es un objeto, sino qué sabe hacer: puntuar un lead, ejecutarse de madrugada, ordenarse en una lista. Una describe exactamente eso: una capacidad, sin decir cómo se consigue.

La nutria: El paralelo de Admin

Esto me llamó la atención en Setup: en Apex Classes → Schedule Apex, el desplegable no muestra todas las clases de la org, solo las que implementan la interfaz Schedulable. Salesforce no sabe ni le importa qué hace tu clase; solo necesita saber que tiene un método execute() al que llamar a la hora programada. Eso es una interfaz: un contrato que la plataforma puede usar sin conocerte.

Declarar e implementar

Apex
public interface LeadScoringRule {
    Integer score(Lead candidate);      // solo la firma: sin cuerpo
}

public class IndustryRule implements LeadScoringRule {
    public Integer score(Lead candidate) {
        return candidate.Industry == 'Technology' ? 20 : 0;
    }
}

public class SizeRule implements LeadScoringRule {
    public Integer score(Lead candidate) {
        Integer employees = candidate.NumberOfEmployees ?? 0;
        return employees >= 500 ? 30 : 0;
    }
}
  • La interfaz solo lista firmas: nombre, parámetros y tipo de retorno. No tiene atributos ni cuerpo.
  • implements LeadScoringRule obliga a la clase a tener todos esos métodos, públicos. Si falta uno, no compila.
  • Una clase puede implementar varias interfaces a la vez (implements A, B) y, además, heredar de una clase.

Polimorfismo: tratar igual lo que es distinto

Aquí está la recompensa. Una List<LeadScoringRule> puede guardar reglas de clases distintas, y el bucle que las recorre no necesita saber cuál es cuál: llama a score() y cada una responde a su manera. Para añadir una regla nueva mañana, escribes una clase y la metes en la lista; el bucle no se toca.

Apex
List<LeadScoringRule> rules = new List<LeadScoringRule>{
    new IndustryRule(),
    new SizeRule()
};

Lead candidate = new Lead(LastName = 'Kim', Company = 'Globex',
                          Industry = 'Technology', NumberOfEmployees = 800);
Integer total = 0;
for (LeadScoringRule rule : rules) {
    total += rule.score(candidate);    // cada regla a su manera
}
System.debug(total);   // 50

List<LeadScoringRule> rules

EL BUCLE · no cambia nunca

Integer total = 0; for (LeadScoringRule r : rules) { total += r.score(candidate); }
  • IndustryRuleTechnology → 20+20
  • SizeRule≥ 500 → 30+30
  • CountryRuleSpain → 10—

total = 50. Activa o quita reglas: el total cambia y el bucle sigue siendo el mismo, porque solo conoce el contrato —score()— y no qué clase hay detrás. Añadir la regla del trimestre que viene es escribir una clase nueva, sin tocar nada más.

El bucle solo conoce el contrato. Detrás, cada clase lo cumple a su manera.

Interfaces que ya existen en la plataforma

InterfazTe obliga a tener…Para qué
Schedulableexecute(SchedulableContext ctx)Programar la clase desde Setup (Módulo 9)
Queueableexecute(QueueableContext ctx)Trabajo asíncrono encadenable (Módulo 9)
Database.Batchablestart, execute y finishProcesar millones de registros (Módulo 9)
ComparablecompareTo(Object other)Que list.sort() sepa ordenar tus objetos
InterfazClase abstracta
¿Tiene código común?No, solo firmasSí, además de métodos abstractos
¿Cuántas por clase?Todas las que quierasUna sola (extends)
Úsala cuando…compartes una capacidadcompartes una base y código

Por qué no un if por cada tipo

Podrías escribir el scoring con un bucle y una cadena de if: si es la regla del sector, haz esto; si es la del origen, esto otro. Funciona el primer trimestre. El segundo, Marketing añade dos reglas y alguien tiene que abrir el bucle, añadir ramas y volver a probarlo todo, arriesgando las reglas que ya funcionaban. Con la interfaz, añadir una regla es escribir una clase nueva y meterla en la lista: el bucle que calcula el total no se toca nunca. Es el mismo razonamiento que en el Módulo 7 llevó las reglas de casos a clases de servicio: lo que cambia a menudo se aísla de lo que no debería cambiar.

La nutria: El elemento Subflow solo conoce el contrato

Yo lo entendí con los subflows: cuando un flow llama a un subflow, solo le importa qué variables de entrada acepta y cuáles devuelve. Puedes cambiar por completo lo que hace ese subflow por dentro (o sustituirlo por otro con las mismas variables) y el flow que lo llama ni se entera. Una interfaz es ese contrato de entradas y salidas, escrito en código: el bucle del scoring llama a score() sin saber qué clase hay detrás.

La nutria: ¿Y por qué no un Flow? Porque un flow no elige el subflow al ejecutarse

Con subflows llegué hasta aquí: cada regla de scoring era un subflow. Pero el elemento Subflow apunta a un flow concreto que eliges al diseñar; no puedes recorrer una lista de reglas y llamar a cada una. Cada regla nueva era un elemento más en el flow principal, y quitar una, otro cambio y otro despliegue. Con la interfaz, añadir una regla es añadir una línea a la lista y el cálculo del total no se toca.