Dar la misma interfaz a las reglas de envío

JavaBeginner
Practicar Ahora

Introducción

Un pequeño mostrador de paquetería necesita comparar ofertas de distintas reglas de envío. Para un paquete de 500 gramos, los datos preparados indican 600 céntimos con Town y 200 con Steps. Harás que esas ofertas normales funcionen y después examinarás el límite de peso que cambia un precio.

Este laboratorio presupone conocimientos de clases, constructores, métodos, listas y polimorfismo básico de Java. Introduce una interfaz y un resultado de cotización explícito. El analizador y el lanzador están preparados; tú implementas las decisiones reales de aptitud y precio. El tiempo estimado por el autor incluye lectura y correcciones habituales.

Dar un contrato público a una regla de tarifa fija

En este paso, harás que la regla Town devuelva un precio real cuando el paquete sea apto y rechace los paquetes con exceso de peso.

Abre la pestaña WebIDE y selecciona Terminal → New Terminal en el menú para abrir la terminal integrada. Ejecuta allí los siguientes comandos. Todos usan la misma sesión de shell; cd cambia su directorio de trabajo. Si vuelves a abrir una terminal, repite el comando cd. El JDK y el código de base están preparados; no necesitas instalar una herramienta de compilación.

cd /home/labex/project/parcels

cat muestra un archivo sin modificarlo. Lee el pequeño archivo de tarifas y la interfaz:

cat data/rates.tsv
cat src/ShippingRule.java

El archivo usa TSV: campos separados por tabulaciones, sin cabecera. Una fila flat contiene type, ID de regla, máximo de gramos y céntimos. Una fila step añade el tamaño del tramo antes de los céntimos por tramo. Town admite hasta 2,000 gramos y cobra 600 céntimos; Steps admite hasta 5,000 y cobra 200 por cada tramo de 500 gramos iniciado.

Una interface describe métodos que el cliente puede usar sin conocer la clase que los implementa. ShippingRule promete un ID y una cotización para un paquete. La clase declara implements ShippingRule y proporciona ambos métodos públicos. Al llamar a rule.quote(parcel), se elige en tiempo de ejecución la implementación correspondiente a ese objeto. No hacen falta conversiones de tipo ni ramas basadas en el nombre del transportista.

El contrato público incluye ShippingRule.id() y ShippingRule.quote(Parcel), junto con Quote.eligible() y Quote.cents(). RuleFactory.flat(String, int, long) y RuleFactory.stepped(String, int, int, long) crean objetos de reglas; QuoteEngine.bestRules(Parcel, List<ShippingRule>) devuelve los ID de todas las reglas ganadoras. Estas firmas permiten que el mismo código cliente use implementaciones distintas. Puedes organizar las clases de otra forma y modificar RuleFactory para crearlas. Conserva estos métodos públicos y el formato de comandos y salida proporcionado; no se evalúan los nombres de las clases internas de la fábrica ni el editor que uses.

Los pesos admitidos son enteros de 1 a 1,000,000 gramos. Los límites de peso y los tamaños de tramo también son enteros de 1 a 1,000,000; cada tarifa fija o por tramo es un entero de 0 a 1,000,000 céntimos. Una oferta apta de cero céntimos es válida. Se distingue de Quote.unavailable(), cuyo eligible() es false y cuyo importe almacenado no debe participar en la comparación. Los importes calculados se guardan en un long, de modo que un producto grande válido no desborde un int de 32 bits. Todas las tarifas, personas y denominaciones de paquetes son datos sintéticos; no son precios en directo de transportistas.

Parcel ya mantiene privados el ID, el nombre mostrado y los gramos, con métodos de solo lectura. Quote.priced(cents) crea un resultado apto; Quote.unavailable() crea uno no apto. El constructor de FlatRule guarda sus parámetros. Completa su método quote comprobando la aptitud antes de devolver un precio. return termina el método inmediatamente; > permite que un paquete exactamente en el límite siga siendo apto.

El siguiente comando sustituye un archivo fuente Java. cat copia el texto situado entre las dos líneas JAVA a la ruta indicada después de >; las comillas del marcador inicial impiden que el shell sustituya expresiones del código Java. Pega el bloque completo, incluida la última línea JAVA, que debe estar sola. También puedes editar el mismo archivo en WebIDE, guardarlo y volver a la terminal de WebIDE. Si una copia incompleta deja un indicador de continuación, pulsa Ctrl+C y pega de nuevo el bloque completo.

cat > src/FlatRule.java <<'JAVA'
public final class FlatRule implements ShippingRule {
    private final String id;
    private final int maxGrams;
    private final long cents;
    public FlatRule(String id, int maxGrams, long cents) {
        this.id = id; this.maxGrams = maxGrams; this.cents = cents;
    }
    public String id() { return id; }
    public Quote quote(Parcel parcel) {
        if (parcel.grams() > maxGrams) return Quote.unavailable();
        return Quote.priced(cents);
    }
}
JAVA

./quote.sh ejecuta el lanzador proporcionado en el directorio actual. Compila src/*.java con javac -encoding UTF-8 en un directorio temporal e inicia Java con codificación UTF-8. Cada ejecución recompila las fuentes guardadas, sin reutilizar clases antiguas. Un error de compilación indica el archivo y la línea: corrígelo, guarda y repite el mismo comando. Una ruta incorrecta produce un error de archivo; repite el cd indicado y revisa el nombre, sin restablecer el entorno.

./quote.sh quote 500

El comando quote recibe un peso en gramos y muestra cada ID de regla con su importe en céntimos o UNAVAILABLE. Town debe mostrar ahora 600. Steps sigue mostrando 0 porque su método inicial está incompleto a propósito; no lo interpretes como una oferta gratuita real.

Prueba el peso máximo y un gramo por encima:

./quote.sh quote 2000
./quote.sh quote 2001

Town cambia de 600 a UNAVAILABLE. Has conservado el límite inclusivo sin cambiar data/rates.tsv. Comprueba este paso antes de continuar.

Cobrar por cada tramo de peso iniciado

En este paso, implementarás la segunda regla mediante la misma interfaz, usando aritmética entera en lugar de dinero en coma flotante.

Continúa en la terminal de WebIDE, en /home/labex/project/parcels. Cada tramo de 500 gramos iniciado cuesta 200 céntimos: 1–500 gramos usan un tramo, 501–1,000 usan dos y 1,001–1,500 usan tres. Por tanto, 500 gramos cuestan 200 céntimos y 501 cuestan 400. La división entera normal trunca: 501 / 500 da 1, lo que cobraría de menos.

Para pesos positivos, (grams + stepGrams - 1L) / stepGrams redondea hacia arriba el número de tramos. L hace que la operación use long; multiplicar el número de tramos por stepCents también usa long. Primero se comprueba el peso máximo, para no dar un precio engañoso a un paquete fuera del servicio.

Sustituye todo el archivo de la regla por esta implementación, usando la misma técnica here-document de antes:

cat > src/StepRule.java <<'JAVA'
public final class StepRule implements ShippingRule {
    private final String id;
    private final int maxGrams, stepGrams;
    private final long stepCents;
    public StepRule(String id, int maxGrams, int stepGrams, long stepCents) {
        this.id = id; this.maxGrams = maxGrams;
        this.stepGrams = stepGrams; this.stepCents = stepCents;
    }
    public String id() { return id; }
    public Quote quote(Parcel parcel) {
        if (parcel.grams() > maxGrams) return Quote.unavailable();
        long steps = (parcel.grams() + stepGrams - 1L) / stepGrams;
        return Quote.priced(steps * stepCents);
    }
}
JAVA
./quote.sh quote 500

Salida esperada, con columnas separadas por tabulaciones:

Town 600
Steps 200

Cruza ahora un límite de tramo y el límite de aptitud:

./quote.sh quote 501
./quote.sh quote 5001

Con 501 gramos, Steps muestra 400. Con 5,001, ambas reglas muestran UNAVAILABLE: es un peso válido sin ninguna oferta, no un paquete inválido. Prueba deliberadamente cero gramos:

./quote.sh quote 0

Este comando falla de forma intencionada con INVALID_WEIGHT SAMPLE y estado de salida 2. No modifica las fuentes ni los registros de tarifas. Para recuperarte basta con indicar un peso válido:

./quote.sh quote 1500

Ambas reglas muestran ahora 600. Tienes un empate real para tratar en el siguiente laboratorio. Las decisiones de aptitud y precio están dentro de los objetos de reglas sustituibles, mientras que el cliente sigue usando solo la interfaz.

Resumen

Implementaste dos reglas detrás de una interfaz Java, separaste la aptitud de los importes enteros en céntimos y probaste límites exactos y el redondeo de tramos iniciados. El mismo cliente ya puede obtener cotizaciones útiles sin saber qué implementación las proporciona.