Donner la même interface aux règles d’expédition

JavaBeginner
Pratiquer maintenant

Introduction

Un petit comptoir d’expédition doit comparer les offres de plusieurs règles de transport. Pour un colis de 500 grammes, les tarifs préparés indiquent 600 centimes pour Town et 200 pour Steps. Vous rendrez ces offres normales opérationnelles, puis étudierez la limite de poids qui modifie un prix.

Ce laboratoire suppose des connaissances des classes, constructeurs, méthodes, listes et du polymorphisme de base en Java. Il introduit une interface et un résultat de devis explicite. L’analyseur et le lanceur sont fournis ; vous implémentez les véritables décisions d’admissibilité et de prix. La durée estimée par l’auteur inclut la lecture et les corrections habituelles.

Donner un contrat public à une règle de prix fixe

Dans cette étape, vous ferez renvoyer un véritable prix admissible par la règle Town et refuserez les colis trop lourds.

Ouvrez l’onglet WebIDE, puis choisissez Terminal → New Terminal dans le menu pour ouvrir le terminal intégré. Exécutez les commandes suivantes dans ce terminal WebIDE. Elles utilisent le même shell ; cd change son répertoire de travail. Si vous rouvrez un terminal, répétez la commande cd. Le JDK et le code de base sont préparés ; aucun outil de construction n’est à installer.

cd /home/labex/project/parcels

cat affiche un fichier sans le modifier. Lisez le petit fichier de tarifs et l’interface :

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

Le fichier est au format TSV : champs séparés par des tabulations, sans en-tête. Une ligne flat contient type, ID de règle, poids maximal en grammes et prix en centimes. Une ligne step ajoute la taille du palier avant son prix. Ici, Town accepte au maximum 2,000 grammes et facture 600 centimes ; Steps accepte 5,000 grammes et facture 200 centimes par palier de 500 grammes entamé.

Une interface décrit les méthodes qu’un appelant peut utiliser sans connaître la classe d’implémentation. ShippingRule promet un ID et un devis pour un colis. Une classe déclare implements ShippingRule et fournit les deux méthodes publiques. L’appel rule.quote(parcel) choisit à l’exécution l’implémentation correspondant à cet objet. Aucun transtypage ni branchement par nom de transporteur n’est nécessaire.

Le contrat public comprend ShippingRule.id() et ShippingRule.quote(Parcel), ainsi que Quote.eligible() et Quote.cents(). RuleFactory.flat(String, int, long) et RuleFactory.stepped(String, int, int, long) créent les objets de règles ; QuoteEngine.bestRules(Parcel, List<ShippingRule>) renvoie les ID de toutes les règles gagnantes. Ces signatures permettent au même appelant d’utiliser différentes implémentations. Vous pouvez organiser les classes autrement, y compris modifier RuleFactory pour les construire. Conservez ces méthodes publiques et les formats de commande et de sortie fournis ; les noms des classes internes à la fabrique et votre choix d’éditeur ne sont pas évalués.

Les poids acceptés sont des entiers de 1 à 1,000,000 grammes. Les limites des règles et les tailles de palier sont également des entiers de 1 à 1,000,000 ; chaque prix fixe ou par palier est un entier de 0 à 1,000,000 centimes. Une offre admissible à zéro centime est valide. Elle se distingue de Quote.unavailable(), dont eligible() vaut false et dont le montant stocké ne doit pas participer à la comparaison. Un long conserve les montants calculés, afin qu’un grand produit valide ne déborde pas un int de 32 bits. Tous les tarifs, personnes et noms de colis sont synthétiques ; il ne s’agit pas de prix réels de transporteurs consultés en direct.

Parcel conserve déjà son ID, son nom affiché et ses grammes dans des champs privés, avec des méthodes de lecture seule. Quote.priced(cents) crée un résultat admissible ; Quote.unavailable() en crée un non admissible. Le constructeur de FlatRule mémorise ses paramètres. Complétez sa méthode quote en vérifiant l’admissibilité avant de renvoyer le prix. return termine immédiatement la méthode ; > laisse admissible un colis exactement à la limite.

La commande suivante remplace un fichier source Java. cat copie le texte entre les deux lignes JAVA vers le chemin placé après > ; les guillemets du marqueur initial empêchent les substitutions du shell dans le code Java. Collez tout le bloc, y compris le dernier JAVA seul sur sa ligne. Vous pouvez aussi modifier le même fichier dans WebIDE, l’enregistrer et revenir au terminal WebIDE. Si un collage incomplet laisse une invite de continuation, appuyez sur Ctrl+C puis recollez le bloc complet.

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 exécute le lanceur fourni dans le répertoire courant. Il compile src/*.java avec javac -encoding UTF-8 dans un répertoire temporaire, puis lance Java avec l’encodage UTF-8. Chaque exécution recompile les sources enregistrées, sans réutiliser d’anciens fichiers de classes. Une erreur de compilation indique un fichier et une ligne : corrigez, enregistrez et relancez la même commande. Un chemin incorrect provoque une erreur de fichier ; répétez le cd indiqué et vérifiez le nom, sans réinitialiser l’environnement.

./quote.sh quote 500

La commande quote reçoit un poids en grammes et affiche l’ID de chaque règle avec son montant en centimes ou UNAVAILABLE. Town doit maintenant afficher 600. Steps affiche encore 0, car sa méthode initiale est volontairement incomplète ; ce n’est pas une véritable offre gratuite.

Essayez la limite et un gramme de plus :

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

Town passe de 600 à UNAVAILABLE. Vous avez conservé la limite inclusive sans modifier data/rates.tsv. Vérifiez cette étape avant de continuer.

Facturer chaque palier de poids entamé

Dans cette étape, vous implémenterez la seconde règle avec la même interface, en utilisant des calculs entiers plutôt que des montants à virgule flottante.

Continuez dans le terminal WebIDE, dans /home/labex/project/parcels. Un palier de 500 grammes entamé coûte 200 centimes : 1–500 grammes utilisent un palier, 501–1,000 en utilisent deux et 1,001–1,500 en utilisent trois. Ainsi, 500 grammes coûtent 200 centimes et 501 en coûtent 400. La division entière ordinaire tronque : 501 / 500 vaut 1, ce qui ferait sous-facturer.

Pour des grammes positifs, (grams + stepGrams - 1L) / stepGrams arrondit le nombre de paliers vers le haut. L fait effectuer le calcul en long ; multiplier ce nombre par stepCents utilise aussi un long. La vérification du poids maximal vient en premier, afin de ne pas proposer un prix trompeur pour un colis hors service.

Remplacez tout le fichier de la règle par cette implémentation, avec la même technique here-document que précédemment :

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

Sortie attendue, avec des colonnes séparées par des tabulations :

Town 600
Steps 200

Franchissez maintenant une limite de palier et la limite d’admissibilité :

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

À 501 grammes, Steps affiche 400. À 5,001 grammes, les deux règles affichent UNAVAILABLE : le poids est valide mais aucune offre n’existe ; le colis n’est pas invalide. Essayez volontairement zéro gramme :

./quote.sh quote 0

Cette commande échoue intentionnellement avec INVALID_WEIGHT SAMPLE et le code de sortie 2. Elle ne modifie ni vos sources ni les tarifs. Pour reprendre, fournissez simplement un poids valide :

./quote.sh quote 1500

Les deux règles affichent maintenant 600. Vous disposez d’un véritable ex æquo à gérer dans le prochain laboratoire. Les décisions d’admissibilité et de prix résident dans les objets de règles remplaçables, tandis que l’appelant utilise toujours uniquement l’interface.

Résumé

Vous avez implémenté deux règles derrière une interface Java, séparé l’admissibilité des montants entiers en centimes et testé les limites exactes et l’arrondi des paliers entamés. Le même appelant peut désormais obtenir des devis utiles sans connaître leur implémentation.