Codifique uma classe Entrega, contendo as seguintes informações:
  • nome do cliente (string)
  • endereço de entrega (string)
  • peso (float)
  • status (status)
Os status disponíveis são:
  • pedido em processamento;
  • aguardando separação;
  • em transporte;
  • entregue ao destinatário e
  • cancelado.
A classe deve ter um método construtor e um método chamado atualizar_status, que define um novo status para aquela entrega. Uma atenção especial deve ser dada para a semântica envolvida na atualização do
status: não deve ser tolerada uma mudança de entregue ao destinatário para em transporte, por exemplo.
Sugere-se criar uma lista para os diferentes status e armazenar nos objetos apenas o índice do elemento da lista referente ao status atual. A lista pode ficar fora da classe.

Passo 1

O enunciado pede que eu codifique uma classe chamada Entrega com quatro informações (atributos): nome do cliente (string), endereço de entrega (string), peso (float) e status (status).
Os status disponíveis são exatamente estes cinco (palavras em português):
  • pedido em processamento;
  • aguardando separação;
  • em transporte;
  • entregue ao destinatário;
  • cancelado.
O enunciado exige também:
  • existir um construtor (método inicializador) para criar objetos Entrega;
  • existir um método chamado atualizar_status que defina um novo status para a entrega;
  • a semântica de atualização deve impedir transições inválidas (por exemplo, não permitir passar de “entregue ao destinatário” para “em transporte”);
  • sugere-se armazenar nos objetos apenas o índice do status numa lista global de status (a lista pode ficar fora da classe).
Resumo das informações dadas:
  • nome do cliente (string) — obrigatório no objeto;
  • endereço de entrega (string) — obrigatório no objeto;
  • peso (float) — obrigatório no objeto;
  • status (um dos cinco) — deve ser armazenado como índice;
  • método atualizar_status com validações de transição semântica.

Passo 2

Decisão de representação dos status e por que:
Vou criar uma lista global (fora da classe) chamada STATUSES contendo as cinco strings dos status, na ordem indicada. Cada objeto Entrega guardará apenas o índice inteiro que aponta para um elemento dessa lista.
Por que isso é bom? Porque:
  • economiza espaço (um inteiro é menor que repetir strings em cada objeto);
  • facilita comparar status (comparar índices é rápido);
  • mantém a lista de status centralizada (se quisermos traduzir ou alterar rótulos, alteramos somente a lista).
A lista ficará assim (apenas para explicar, o código final irá reproduzi-la):
  • índice 0: “pedido em processamento”
  • índice 1: “aguardando separação”
  • índice 2: “em transporte”
  • índice 3: “entregue ao destinatário”
  • índice 4: “cancelado”

Passo 3

Definição das transições válidas (a parte semântica importante):
Temos que decidir quais mudanças de status são permitidas. Uma escolha coerente para um fluxo de entrega é pensar que o processo vai numa direção e que cancelamento pode ocorrer em etapas iniciais, mas depois de entregue não há volta. Vou adotar estas regras (grafo de estados):
  • De “pedido em processamento” (0) pode ir para “aguardando separação” (1) ou para “cancelado” (4) — e manter-se em 0 (sem-op).
  • De “aguardando separação” (1) pode ir para “em transporte” (2) ou para “cancelado” (4) — e manter-se em 1.
  • De “em transporte” (2) pode ir para “entregue ao destinatário” (3) ou para “cancelado” (4) — e manter-se em 2.
  • De “entregue ao destinatário” (3) não pode ir para outros estados (apenas manter-se em 3).
  • De “cancelado” (4) não pode ir para outros estados (apenas manter-se em 4).
Incluo o “manter-se no mesmo estado” como permitido (atualizar para o mesmo status é um no-op válido).
Explicação didática: isso implementa um pequeno “automato” de estados; cada estado tem um conjunto de próximos estados permitidos. O método atualizar_status fará a checagem contra esse conjunto.

Passo 4

Projeto dos métodos e validações necessárias:
  • Construtor (init): deve receber nome, endereço, peso e opcionalmente um status inicial.
    • Validar que nome e endereço são strings não vazias.
    • Validar que peso é float (ou número) e não negativo.
    • Aceitar o status inicial tanto como índice inteiro quanto como string; converter para índice válido. Se não fornecido, iniciar em índice 0 (pedido em processamento).
  • Método atualizar_status(novo_status):
    • Aceitar o parâmetro novo_status como índice inteiro ou como string do status.
    • Converter para índice; validar que o índice é válido (entre 0 e 4).
    • Verificar se a transição do índice atual para o novo índice está na tabela de transições permitidas.
    • Se permitida, atualizar o índice e retornar algo que indique sucesso (pode ser True).
    • Se não permitida, lançar uma exceção (ValueError) com mensagem clara explicando por que a transição é inválida.
  • Métodos auxiliares (privados) úteis: por exemplo, um método para converter string->índice e validar inputs.
  • Implementar repr ou str para facilitar depuração/visualização (mostrando o rótulo do status a partir do índice).

Passo 5

Explicando passo a passo como converter status e checar transições (didática):
  • Como converter uma string de status para índice: procurar a string (comparação exata) na lista STATUSES e retornar o índice. Se não achar, informar que o status é inválido. Isso permite receber entrada amigável (string) no método atualizar_status.
  • Como converter um inteiro recebido: checar se é inteiro e se 0 <= índice < len(STATUSES).
  • Como checar transição: dado atual e novo, olhar no dicionário ALLOWED_TRANSITIONS[atual] (que é um conjunto de índices permitidos) e ver se novo in ALLOWED_TRANSITIONS[atual].
  • Motivo de lançar exceção em vez de retornar False: em prova/implementação, lançar uma exceção com mensagem clara ajuda o usuário/programador a entender por que a atualização falhou (ex.: “não é possível ir de entregue ao destinatário para em transporte”). Em contextos onde se prefere não interromper o fluxo, poderíamos retornar um booleano, mas o enunciado não restringe esse comportamento — optei pela exceção porque deixa a semântica explícita.

Passo 6

Boas práticas extras que vou seguir no código (e que você pode usar na prova):
  • Documentar os métodos com docstrings curtos.
  • Validar tipos e valores de entrada com mensagens claras (isso evita bugs em tempo de execução).
  • Deixar a lista de status fora da classe (como pediu o enunciado) e o dicionário de transições também fora da classe para clareza.
  • Implementar mensagens de erro em português, explicando a tentativa e os estados envolvidos (isso ajuda nas correções e debugging).

Passo 7

Agora, o código final que implementa tudo isso. Ele inclui:
  • a lista STATUSES fora da classe;
  • o dicionário ALLOWED_TRANSITIONS com conjuntos de índices permitidos;
  • a classe Entrega com construtor, método atualizar_status, método auxiliar para converter status e um repr legível.

Resposta

# Lista global de status (fora da classe), conforme sugerido no enunciado
STATUSES = [
    "pedido em processamento",
    "aguardando separação",
    "em transporte",
    "entregue ao destinatário",
    "cancelado",
]

# Dicionário de transições permitidas: chave = índice atual, valor = conjunto de índices permitidos
ALLOWED_TRANSITIONS = {
    0: {0, 1, 4},  # pedido em processamento -> permanecer, aguardando separação, cancelado
    1: {1, 2, 4},  # aguardando separação -> permanecer, em transporte, cancelado
    2: {2, 3, 4},  # em transporte -> permanecer, entregue ao destinatário, cancelado
    3: {3},        # entregue ao destinatário -> permanecer (sem volta)
    4: {4},        # cancelado -> permanecer (sem volta)
}


class Entrega:
    """
    Classe que representa uma entrega com nome do cliente, endereço, peso e status.
    O status é armazenado internamente como índice na lista STATUSES.
    """

    def __init__(self, nome_cliente: str, endereco: str, peso: float, status=0):
        """
        Construtor da classe Entrega.

        Parâmetros:
        - nome_cliente: string com o nome do cliente (não vazio)
        - endereco: string com o endereço de entrega (não vazio)
        - peso: número (float) com o peso da encomenda (>= 0)
        - status: opcional, índice (int) ou string com o status inicial. Default = 0 ("pedido em processamento")
        """
        # Validações simples
        if not isinstance(nome_cliente, str) or not nome_cliente.strip():
            raise ValueError("nome_cliente deve ser uma string não vazia.")
        if not isinstance(endereco, str) or not endereco.strip():
            raise ValueError("endereco deve ser uma string não vazia.")
        if not (isinstance(peso, (int, float))):
            raise ValueError("peso deve ser um número (int ou float).")
        if peso < 0:
            raise ValueError("peso não pode ser negativo.")

        self.nome_cliente = nome_cliente.strip()
        self.endereco = endereco.strip()
        # armazenamos peso como float
        self.peso = float(peso)
        # converte o status (pode ser int índice ou string)
        self.status = self._status_to_index(status)

    def _status_to_index(self, status):
        """
        Converte um status dado (int índice ou string) para o índice correspondente em STATUSES.
        Lança ValueError se o status for inválido.
        """
        # Se já for inteiro, valida intervalo
        if isinstance(status, int):
            if 0 <= status < len(STATUSES):
                return status
            else:
                raise ValueError(f"Índice de status inválido: {status}")
        # Se for string, procura na lista (comparação exata)
        if isinstance(status, str):
            try:
                idx = STATUSES.index(status)
                return idx
            except ValueError:
                raise ValueError(f"Status inválido (string não reconhecida): '{status}'")
        # Outros tipos não são aceitos
        raise ValueError("status deve ser um índice inteiro ou uma string correspondente a um dos STATUSES.")

    def atualizar_status(self, novo_status):
        """
        Tenta atualizar o status da entrega para novo_status (índice ou string).
        Verifica se a transição é permitida de acordo com ALLOWED_TRANSITIONS.
        Em caso de sucesso, atualiza e retorna True.
        Em caso de transição inválida, lança ValueError com mensagem explicativa.
        """
        novo_idx = self._status_to_index(novo_status)
        atual_idx = self.status

        # Se a transição é permitida
        permitidos = ALLOWED_TRANSITIONS.get(atual_idx, set())
        if novo_idx in permitidos:
            # Atualiza (mesmo que seja o mesmo índice, é um no-op válido)
            self.status = novo_idx
            return True
        # Caso não permitido, lança erro explicativo
        raise ValueError(
            f"Transição inválida: não é permitido ir de '{STATUSES[atual_idx]}' para '{STATUSES[novo_idx]}'."
        )

    def get_status(self):
        """Retorna o rótulo do status atual (string)."""
        return STATUSES[self.status]

    def __repr__(self):
        return (
            f"Entrega(nome_cliente={self.nome_cliente!r}, "
            f"endereco={self.endereco!r}, peso={self.peso!r}, "
            f"status={self.status!r} -> {self.get_status()!r})"
        )


# Exemplo rápido de uso (não parte do core da classe, só ilustração):
if __name__ == "__main__":
    # Criando uma entrega
    e = Entrega("Maria Silva", "Rua A, 123", 2.5)  # status default 0
    print(e)  # deve mostrar status "pedido em processamento"

    # Atualizando para "aguardando separação"
    e.atualizar_status("aguardando separação")
    print(e)  # agora deve estar no índice 1

    # Tentando uma transição inválida (por exemplo, voltar de entregue para em transporte)
    try:
        e.atualizar_status("em transporte")  # válido a partir de 1 -> 2, então ok
        e.atualizar_status("entregue ao destinatário")  # 2 -> 3 ok
        e.atualizar_status("em transporte")  # agora inválido: 3 -> 2 não permitido
    except ValueError as ve:
        print("Erro ao atualizar status:", ve)

Exercícios relacionados