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.
status: não deve ser tolerada uma mudança de entregue ao destinatário para em transporte, por exemplo.
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):
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
Por que isso é bom? Porque:
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):
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.
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_statuscomo í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.
- Aceitar o parâmetro
- 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
STATUSESe 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
atualenovo, olhar no dicionárioALLOWED_TRANSITIONS[atual](que é um conjunto de índices permitidos) e ver senovo 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
STATUSESfora da classe; - o dicionário
ALLOWED_TRANSITIONScom conjuntos de índices permitidos; - a classe
Entregacom construtor, métodoatualizar_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)