Dar a mesma interface às regras de envio

JavaBeginner
Pratique Agora

Introdução

Um pequeno balcão de encomendas precisa comparar ofertas de diferentes regras de envio. Para um pacote de 500 gramas, os dados preparados indicam 600 centavos com Town e 200 com Steps. Você fará essas ofertas normais funcionarem e depois examinará o limite de peso que altera um preço.

Este laboratório pressupõe conhecimentos de classes, construtores, métodos, listas e polimorfismo básico em Java. Ele apresenta uma interface e um resultado de cotação explícito. O analisador e o script de execução são fornecidos; você implementa as decisões reais de elegibilidade e preço. O tempo estimado pelo autor inclui leitura e correções comuns.

Dar um contrato público a uma regra de preço fixo

Nesta etapa, você fará a regra Town retornar um preço real para pacotes elegíveis e rejeitar os que ultrapassam o peso máximo.

Abra a aba WebIDE e escolha Terminal → New Terminal no menu para abrir o terminal integrado. Execute os próximos comandos nesse terminal do WebIDE. Todos usam o mesmo shell; cd altera o diretório de trabalho dele. Se abrir outro terminal, repita o comando cd. O JDK e a estrutura de código estão preparados; não é necessário instalar uma ferramenta de build.

cd /home/labex/project/parcels

cat exibe um arquivo sem alterá-lo. Leia o pequeno arquivo de tarifas e a interface:

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

O arquivo usa TSV: campos separados por tabulações, sem cabeçalho. Uma linha flat contém type, ID da regra, máximo em gramas e centavos. Uma linha step acrescenta o tamanho da faixa antes dos centavos por faixa. Town aceita até 2,000 gramas e cobra 600 centavos; Steps aceita até 5,000 e cobra 200 centavos por faixa de 500 gramas iniciada.

Uma interface descreve os métodos que o chamador pode usar sem conhecer a classe de implementação. ShippingRule promete um ID e uma cotação para um pacote. Uma classe declara implements ShippingRule e fornece os dois métodos públicos. Ao chamar rule.quote(parcel), a implementação correspondente ao objeto é escolhida em tempo de execução. Não são necessárias conversões de tipo nem condições por nome da transportadora.

O contrato público inclui ShippingRule.id() e ShippingRule.quote(Parcel), além de Quote.eligible() e Quote.cents(). RuleFactory.flat(String, int, long) e RuleFactory.stepped(String, int, int, long) criam objetos de regras; QuoteEngine.bestRules(Parcel, List<ShippingRule>) retorna os IDs de todas as regras vencedoras. Essas assinaturas permitem ao mesmo código chamador usar implementações diferentes. Você pode organizar as classes de outra maneira e alterar RuleFactory para construí-las. Mantenha esses métodos públicos e o formato de comandos e saída fornecido; os nomes das classes internas da fábrica e o editor escolhido não são avaliados.

Os pesos aceitos são inteiros de 1 a 1,000,000 gramas. Os limites de peso e os tamanhos das faixas também são inteiros de 1 a 1,000,000; cada preço fixo ou por faixa é um inteiro de 0 a 1,000,000 centavos. Uma oferta elegível de zero centavos é válida. Ela difere de Quote.unavailable(), cujo eligible() é false e cujo valor armazenado não deve participar da comparação. Um long armazena os centavos calculados, evitando que um produto grande válido transborde um int de 32 bits. Todas as tarifas, pessoas e nomes de pacotes são sintéticos; não são preços de transportadoras consultados em tempo real.

Parcel já mantém ID, nome de exibição e gramas em campos privados, com métodos somente de leitura. Quote.priced(cents) cria um resultado elegível; Quote.unavailable() cria um inelegível. O construtor de FlatRule guarda os parâmetros. Complete seu método quote verificando a elegibilidade antes de retornar um preço. return encerra o método imediatamente; > mantém elegível um pacote exatamente no limite.

O comando a seguir substitui um arquivo-fonte Java. cat copia o texto entre as duas linhas JAVA para o caminho após >; as aspas do marcador inicial impedem substituições do shell no código Java. Cole o bloco completo, incluindo o último JAVA sozinho na linha. Você também pode editar o mesmo arquivo no WebIDE, salvá-lo e voltar ao terminal do WebIDE. Se uma colagem incompleta deixar um prompt de continuação, pressione Ctrl+C e cole o bloco inteiro novamente.

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 executa o script fornecido no diretório atual. Ele compila src/*.java com javac -encoding UTF-8 em um diretório temporário e inicia Java com codificação UTF-8. Cada execução recompila as fontes salvas, sem reutilizar arquivos de classe antigos. Um erro de compilação indica arquivo e linha: corrija o arquivo, salve e repita o comando. Um caminho incorreto causa um erro de arquivo; repita o cd indicado e confira o nome, sem reinicializar o ambiente.

./quote.sh quote 500

O comando quote recebe um peso em gramas e mostra cada ID de regra com centavos ou UNAVAILABLE. Town deve mostrar 600. Steps ainda mostra 0 porque seu método inicial está incompleto de propósito; isso não é uma oferta gratuita real.

Teste o máximo e um grama acima:

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

Town muda de 600 para UNAVAILABLE. Você preservou o máximo inclusivo sem alterar data/rates.tsv. Verifique esta etapa antes de continuar.

Cobrar por cada faixa de peso iniciada

Nesta etapa, você implementará a segunda regra pela mesma interface, usando aritmética inteira em vez de valores monetários de ponto flutuante.

Continue no terminal do WebIDE, em /home/labex/project/parcels. Cada faixa de 500 gramas iniciada custa 200 centavos: 1–500 gramas usam uma faixa, 501–1,000 usam duas e 1,001–1,500 usam três. Portanto, 500 gramas custam 200 centavos, enquanto 501 custam 400. A divisão inteira comum trunca: 501 / 500 resulta em 1, cobrando menos do que deveria.

Para pesos positivos, (grams + stepGrams - 1L) / stepGrams arredonda a quantidade de faixas para cima. L faz a operação usar long; multiplicar a quantidade por stepCents também usa long. O limite máximo é verificado primeiro, para não dar um preço enganoso a um pacote fora do serviço.

Substitua todo o arquivo da regra por esta implementação, usando a mesma 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

Saída esperada, com colunas separadas por tabulações:

Town 600
Steps 200

Agora atravesse uma fronteira de faixa e o limite de elegibilidade:

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

Com 501 gramas, Steps mostra 400. Com 5,001, as duas regras mostram UNAVAILABLE: o peso é válido, mas não há oferta; o pacote não é inválido. Experimente zero gramas de propósito:

./quote.sh quote 0

Este comando falha intencionalmente com INVALID_WEIGHT SAMPLE e status de saída 2. Ele não altera as fontes nem as tarifas. Para se recuperar, basta fornecer um peso válido:

./quote.sh quote 1500

As duas regras agora mostram 600. Você tem um empate real para tratar no próximo laboratório. As decisões de elegibilidade e preço estão dentro dos objetos de regras substituíveis, enquanto o chamador continua usando somente a interface.

Resumo

Você implementou duas regras por trás de uma interface Java, separou a elegibilidade dos valores inteiros em centavos e testou limites exatos e arredondamento de faixas iniciadas. O mesmo chamador agora obtém cotações úteis sem saber qual implementação as fornece.