Os padrões comportamentais cuidam de COMO os objetos se comunicam e dividem responsabilidades entre si — depois que já existem. É a família ao lado dos criacionais (aula 20), que cuidam de COMO os objetos são criados. A imagem que ajuda: os criacionais montam o elenco; os comportamentais ensaiam a peça.
A pergunta que separa os dois: ele AVISA uma lista quando muda de estado? Observer. Ele DELEGA um cálculo a um objeto trocável? Strategy.
O Observer resolve o problema dos avisos chumbados: em vez de o código que muda o estado chamar cada interessado pelo nome (enviar_email(ana); enviar_push(bruno)...), o Subject mantém uma lista de Observers e, ao mudar, percorre a lista chamando o mesmo método em todos. É uma relação de um para muitos, e o Subject não precisa conhecer cada observer pelo tipo.
from abc import ABC, abstractmethod
class Inscrito(ABC):
@abstractmethod
def atualizar(self, video):
pass
class Canal: # o Subject
def __init__(self, nome):
self.nome = nome
self.inscritos = []
def inscrever(self, inscrito):
self.inscritos.append(inscrito)
def cancelar(self, inscrito):
self.inscritos.remove(inscrito)
def publicar(self, video):
for inscrito in self.inscritos:
inscrito.atualizar(video) # avisa todos Armadilha: não ponha lógica que dispara NOVAS notificações dentro do atualizar() (cascata imprevisível). E não use Observer se há um único interessado fixo — aí um aviso direto basta.
O Strategy resolve o problema do if/elif de algoritmos: em vez de um método inchar com if tipo == "black": ... elif tipo == "cupom": ..., cada algoritmo vira uma classe com um método comum, e o Contexto guarda a estratégia ativa e DELEGA a ela. Trocar o COMO é só trocar o objeto da estratégia, em tempo de execução, sem mexer em quem chama.
from abc import ABC, abstractmethod
class Desconto(ABC):
@abstractmethod
def calcular(self, valor):
pass
class DescontoBlackFriday(Desconto):
def calcular(self, valor):
return valor * 0.70
class Carrinho: # o Contexto
def __init__(self, total, desconto):
self.total = total
self.desconto = desconto # a estratégia ativa
def total_final(self):
return self.desconto.calcular(self.total) # DELEGA
carrinho = Carrinho(100.0, DescontoBlackFriday())
carrinho.desconto = DescontoBlackFriday() # troca em runtime Contraste com a aula 20: a Factory escolhe qual objeto CRIAR; o Strategy escolhe qual algoritmo USAR num objeto que já existe. E não confunda com Observer: Strategy delega a UMA estratégia; Observer notifica VÁRIOS observers.
Ontem vimos os criacionais (como CRIAR objetos). Hoje, os COMPORTAMENTAIS: como os objetos se comunicam e dividem responsabilidades. Observer (quando algo muda, AVISA vários interessados) e Strategy (troca o COMO — o algoritmo — sem mexer em quem usa). Os dois vivem de ABC, polimorfismo e composição.
roteiro da aula
A segunda família de padrões: a que cuida de COMO os objetos se comunicam e dividem o trabalho — não de como são criados.
Um Subject mantém uma lista de interessados e, quando muda de estado, AVISA todos eles. Um para muitos, sem conhecer cada um.
Cada algoritmo vira uma classe trocável. Um Contexto guarda a estratégia ativa e DELEGA a ela — dá pra trocar o COMO em tempo de execução.
Um pedido de e-commerce: avisa interessados quando o status muda (Observer) e calcula o frete por estratégia trocável (Strategy).
ponte com a aula 20
@abstractmethod (10), polimorfismo (09), composição (05). Não é Python novo — é arranjo novo do que você já sabe. comportamentais · panorama
isinstance, e composição pra guardar os colaboradores. Vamos pela dor de cada um. observer · definição
inscrever() e pronto. O Subject não muda. É o oposto de ter os avisos chumbados dentro do código que dispara a mudança. observer · o problema
publicar(), chamar enviar_email(ana), enviar_push(bruno)... um a um, na mão. publicar(); cada forma de aviso vira mais um if/elif. O método que devia só "publicar" sabe demais sobre QUEM avisar e COMO. publicar(). Queríamos que ele só avisasse — sem saber quem está ouvindo. observer · ancorando a ideia
observer · o problema
O mesmo canal, dois jeitos de notificar: cada aviso chumbado dentro de publicar() (o problema) e uma lista de inscritos que recebe o aviso por polimorfismo (o Observer).
def publicar(self, video):
# cada inscrito chumbado aqui dentro
enviar_email("Ana", video)
enviar_push("Bruno", video)
# inscrito novo? mais uma linha aqui.
# forma de aviso nova? mais um if.
# o publicar() conhece cada um pelo nome. def publicar(self, video):
# só percorre a lista e avisa
for inscrito in self.inscritos:
inscrito.atualizar(video)
# inscrito novo? só inscrever().
# o publicar() NÃO conhece ninguém
# pelo nome — só chama atualizar(). observer · o código
Uma ABC Inscrito (aula 10) com o método atualizar() abstrato — o contrato de "sei reagir a um aviso". O Canal é o Subject: guarda a lista de inscritos e, ao publicar(), percorre e avisa todos por polimorfismo (aula 09).
self.inscritos é a lista de observers (aula 18). O Canal COMPÕE os inscritos (composição, aula 05): ele tem uma lista deles, mas não os cria.for chama atualizar() em cada inscrito sem isinstance: cada um reage do seu jeito (e-mail ou push). É o polimorfismo da aula 09 fazendo o trabalho.from abc import ABC, abstractmethod
class Inscrito(ABC):
@abstractmethod
def atualizar(self, video):
pass
class Email(Inscrito):
def __init__(self, nome):
self.nome = nome
def atualizar(self, video):
print(f"[E-mail] {self.nome}: novo vídeo '{video}'")
class Push(Inscrito):
def __init__(self, nome):
self.nome = nome
def atualizar(self, video):
print(f"[Push] {self.nome}: saiu '{video}'!")
class Canal: # o Subject
def __init__(self, nome):
self.nome = nome
self.inscritos = [] # composição: o Canal COMPÕE inscritos
def inscrever(self, inscrito):
self.inscritos.append(inscrito)
def publicar(self, video):
print(f"== {self.nome} publicou: {video} ==")
for inscrito in self.inscritos:
inscrito.atualizar(video) # avisa todos, sem saber o tipo
canal = Canal("DevTube")
canal.inscrever(Email("Ana"))
canal.inscrever(Push("Bruno"))
canal.publicar("POO em Python") observer · o código
Rodando o canal.py, um único publicar() dispara o aviso para os dois inscritos:
publicar() só, dois avisos: o Subject percorreu a lista e chamou atualizar() em cada inscrito. Um para muitos.[E-mail] e [Push]) porque cada subclasse implementou atualizar() do seu jeito — polimorfismo puro.== DevTube publicou: POO em Python ==
[E-mail] Ana: novo vídeo 'POO em Python'
[Push] Bruno: saiu 'POO em Python'! observer · na UML
O Canal e os inscritos que acabamos de codar, desenhados como caixas de classe (a notação da aula 19): o Subject que compõe uma lista de Observers.
Canal ◆── Inscrito (composição: o Subject guarda uma lista de observers) e Inscrito «abstract» △ Email/Push (herança: cada observer implementa o contrato atualizar). É a mesma UML da aula 19 — o losango ◆ é o Subject conhecendo seus observers.observer · onde você já viu (e vai ver)
button.config(command=ao_clicar) — você está REGISTRANDO um observer no botão. ao_clicar() que você registrou. O botão é o Subject; a sua função é o observer. No navegador é o mesmo: addEventListener("click", ...). observer · amarrando com a POO
@abstractmethod (aula 10): Inscrito é o contrato — todo observer PRECISA ter atualizar(). O Subject confia nesse contrato pra avisar qualquer um. for inscrito in ...: inscrito.atualizar(video) chama o método certo de cada subclasse, sem isinstance, sem if/elif. Canal COMPÕE uma lista de inscritos — tem-um, não é-um. A relação Subject↔Observers é composição pura. self.inscritos é uma lista que você percorre. Tudo o que muda em relação à aula 20 é a INTENÇÃO: lá a gente criava objetos; aqui eles se comunicam. observer · quando NÃO usar
O erro mais comum: inscrever e nunca remover. A lista só cresce, observers "mortos" continuam recebendo aviso. A defesa é ter um cancelar() que tira da lista.
# só inscreve, nunca remove:
loja.inscrever("Ana")
loja.inscrever("Bruno")
# Ana saiu do site, mas continua
# na lista — vai receber avisos
# pra sempre. A lista só CRESCE. def cancelar(self, obs):
self.inscritos.remove(obs)
loja.inscrever("Ana")
loja.inscrever("Bruno")
loja.cancelar("Ana")
loja.avisar_promocao("Notebook")
# Bruno: promoção de Notebook!
# (só Bruno — Ana saiu da lista) strategy · definição
strategy · o problema
if tipo == "black": total *= 0.7 elif tipo == "cupom": total -= 15... tudo dentro de calcular_total(). elif. O calcular_total() incha: vira um emaranhado de regras misturadas, e mexer numa promoção arrisca quebrar as outras. strategy · ancorando a ideia
strategy · o problema
O mesmo carrinho, dois jeitos de aplicar desconto: um if/elif que incha (o problema) e cada regra num objeto trocável a que o carrinho delega (o Strategy).
def total_final(self):
if self.tipo == "black":
return self.total * 0.70
elif self.tipo == "cupom":
return self.total - 15.00
elif self.tipo == "nenhum":
return self.total
# promoção nova? mais um elif aqui. def total_final(self):
# delega o cálculo à estratégia ativa
return self.desconto.calcular(self.total)
# trocar o COMO em runtime:
carrinho.desconto = DescontoBlackFriday()
# promoção nova? uma CLASSE nova.
# o total_final() não muda. strategy · o código
Uma ABC Desconto (aula 10) com calcular(valor) abstrato — o contrato comum. Cada promoção é uma subclasse. O Carrinho é o Contexto: guarda a estratégia ativa (self.desconto) e DELEGA a ela. Trocar a estratégia em runtime é só reatribuir o atributo.
self.desconto guarda a estratégia ATIVA — uma só por vez. O Carrinho COMPÕE a estratégia (composição, aula 05): tem-uma, e delega o cálculo a ela.carrinho.desconto = DescontoBlackFriday() troca o algoritmo em tempo de execução — sem mexer no total_final(). Mesma chamada, resultado diferente: polimorfismo (aula 09).from abc import ABC, abstractmethod
class Desconto(ABC):
@abstractmethod
def calcular(self, valor):
pass
class SemDesconto(Desconto):
def calcular(self, valor):
return valor
class DescontoBlackFriday(Desconto):
def calcular(self, valor):
return valor * 0.70
class DescontoCupom(Desconto):
def calcular(self, valor):
return valor - 15.00
class Carrinho: # o Contexto
def __init__(self, total, desconto):
self.total = total
self.desconto = desconto # composição: COMPÕE a estratégia ativa
def total_final(self):
return self.desconto.calcular(self.total) # DELEGA à estratégia
carrinho = Carrinho(100.00, SemDesconto())
print(f"Sem desconto: R$ {carrinho.total_final():.2f}")
carrinho.desconto = DescontoBlackFriday() # troca o COMO em runtime
print(f"Black Friday: R$ {carrinho.total_final():.2f}")
carrinho.desconto = DescontoCupom() # troca de novo
print(f"Cupom: R$ {carrinho.total_final():.2f}") strategy · o código
Rodando o carrinho.py, o MESMO carrinho devolve três totais diferentes — só trocando a estratégia entre as chamadas:
self.desconto entre cada total_final(). O Contexto não mudou — mudou a estratégia a quem ele delega.if tipo == ... apareceu. Cada cálculo ficou encapsulado na sua classe de desconto, e o carrinho só chamou calcular().Sem desconto: R$ 100.00
Black Friday: R$ 70.00
Cupom: R$ 85.00 strategy · na UML
O Carrinho e os descontos que acabamos de codar: o Contexto que compõe UMA estratégia trocável de uma hierarquia de algoritmos.
Carrinho ◆── Desconto (composição: o Contexto guarda UMA estratégia) e Desconto «abstract» △ as três promoções (herança: cada algoritmo implementa calcular). Compare com o Observer (s11): lá o losango ◆ aponta pra uma LISTA de observers; aqui aponta pra UMA estratégia ativa. Essa é a diferença visual entre os dois.strategy · onde você já viu (sem saber o nome)
O sorted(lista, key=...) da aula 18 é Strategy puro. A função que você passa em key= É a estratégia (o algoritmo de ordenação); o sorted é o Contexto que delega a ela. Trocar a key é trocar a estratégia.
key=lambda p: p["preco"] por key=lambda p: p["nome"] troca o ALGORITMO de ordenação sem mudar quem chama (sorted). É exatamente o Strategy.produtos = [
{"nome": "Teclado", "preco": 120},
{"nome": "Mouse", "preco": 80},
{"nome": "Monitor", "preco": 950},
]
# a key É a estratégia; o sorted é o Contexto que delega a ela
por_preco = sorted(produtos, key=lambda p: p["preco"])
por_nome = sorted(produtos, key=lambda p: p["nome"])
print([p["nome"] for p in por_preco]) # ['Mouse', 'Teclado', 'Monitor']
print([p["nome"] for p in por_nome]) # ['Monitor', 'Mouse', 'Teclado'] strategy · variação pythônica
Em Python, quando a estratégia não tem estado, ela pode ser apenas uma FUNÇÃO passada como valor (aula 18) — sem precisar de uma classe inteira. O Contexto recebe a função e a chama.
black_friday, passada como argumento. O total_final é o Contexto: recebe a função e a chama. Sem ABC, sem subclasse — leve e direto.def black_friday(valor):
return valor * 0.70
def total_final(valor, estrategia):
return estrategia(valor) # delega à função-estratégia
print(f"R$ {total_final(100.0, black_friday):.2f}") # R$ 70.00 strategy · amarrando com a POO
@abstractmethod (aula 10): Desconto é o contrato — toda estratégia PRECISA ter calcular(). O Contexto confia nesse contrato pra delegar a qualquer uma. self.desconto.calcular(self.total) chama o método certo da estratégia ativa, sem isinstance. Mesma chamada, resultados diferentes. Carrinho COMPÕE a estratégia — tem-uma, trocável. E funções como valor (aula 18): a estratégia pode ser só uma função. strategy · quando NÃO usar
if que não existe. Strategy é pra QUANDO o algoritmo precisa trocar. fixando os dois
Os dois são comportamentais e vivem de ABC + polimorfismo + composição. A diferença é a INTENÇÃO. Grave: Observer = quando algo muda, AVISA vários; Strategy = troca o COMO sem mudar quem chama.
Quando algo muda, AVISA vários
interessados de uma vez.
1 Subject → MUITOS observers
Mecanismo: uma LISTA + notificar
(for obs: obs.atualizar(...))
Exemplo: Canal avisa inscritos;
cliques de botão na GUI. Troca o COMO — o algoritmo —
sem mudar quem chama.
1 Contexto → 1 estratégia trocável
Mecanismo: um ATRIBUTO + delegar
(self.estrategia.calcular(...))
Exemplo: Carrinho delega o desconto;
sorted(key=...) da aula 18. ao vivo · vamos construir juntos
Partindo do diagrama do próximo slide, a gente implementa juntos um pedido de e-commerce que usa Strategy (pra calcular o frete por estratégia trocável) e Observer (pra avisar os interessados quando o status muda). Acompanhem a tradução de cada parte pra código, camada por camada.
Frete «abstract» △ FreteNormal/FreteExpresso (a estratégia de frete). Observador «abstract» △ PainelLojista/SMSCliente (os interessados). Pedido ◆── compõe um frete (Strategy) E uma lista de observadores (Observer).
Frete(ABC) com @abstractmethod calcular(peso), e FreteNormal (peso * 2.00) e FreteExpresso (peso * 2.00 + 20.00) implementando cada um o seu cálculo.
Observador(ABC) com @abstractmethod atualizar(pedido, status), e PainelLojista e SMSCliente, cada um reagindo do seu jeito ao novo status.
Pedido guarda self.frete (a estratégia) e self.observadores = [] (a lista). valor_frete() DELEGA ao frete; mudar_status() AVISA todos os observadores. Um objeto, os dois papéis.
ao vivo · vamos construir juntos
Partimos deste diagrama. Leia as caixas e os estereótipos — e nos próximos slides a gente traduz cada parte pra código, camada por camada.
Pedido ◆── Frete «abstract» △ Normal/Expresso é o Strategy (o pedido DELEGA o cálculo ao frete ativo); Pedido ◆── Observador «abstract» △ Painel/SMS é o Observer (o pedido AVISA a lista quando o status muda). Nos próximos slides, uma camada de cada vez.resolução · camada 1
Começamos pela estratégia de frete: a ABC Frete com calcular(peso) abstrato e as duas formas de cobrar. É o triângulo △ da esquerda do diagrama virando código.
Frete(ABC) com @abstractmethod é o «abstract» do diagrama (aula 10): define o contrato calcular(peso), comum a todas as estratégias de frete.Pedido vai trocar em runtime, logo à frente.from abc import ABC, abstractmethod
# Camada 1: Strategy — cálculo de frete (trocável)
class Frete(ABC):
@abstractmethod
def calcular(self, peso):
pass
class FreteNormal(Frete):
def calcular(self, peso):
return peso * 2.00
class FreteExpresso(Frete):
def calcular(self, peso):
return peso * 2.00 + 20.00 resolução · camada 2
Agora os interessados: a ABC Observador com atualizar(pedido, status) abstrato, e os dois que vão reagir quando o status mudar. É o triângulo △ da direita do diagrama.
Observador(ABC) define o contrato atualizar(pedido, status): todo observer sabe reagir a um aviso de mudança de status — cada um do seu jeito.PainelLojista escreve no painel interno; SMSCliente manda um SMS pro cliente. Mesma chamada, reações diferentes — o polimorfismo que o Subject vai explorar.# Camada 2: Observer — os interessados no status
class Observador(ABC):
@abstractmethod
def atualizar(self, pedido, status):
pass
class PainelLojista(Observador):
def atualizar(self, pedido, status):
print(f"[Painel] Pedido {pedido} -> {status}")
class SMSCliente(Observador):
def atualizar(self, pedido, status):
print(f"[SMS] Seu pedido {pedido} agora está: {status}") resolução · camada 3
A última camada junta os dois papéis num objeto só. O Pedido COMPÕE um frete (a estratégia, a que ele delega) e uma lista de observadores (os interessados, que ele avisa).
valor_frete() é o lado Strategy: DELEGA o cálculo a self.frete. Trocar a estratégia é pedido.frete = FreteExpresso() — sem mexer neste método.mudar_status() é o lado Observer: percorre self.observadores e AVISA todos. Um Subject, muitos observers — sem conhecer cada um pelo tipo.# Camada 3: o Pedido é Contexto (Strategy) E Subject (Observer)
class Pedido:
def __init__(self, codigo, peso, frete):
self.codigo = codigo
self.peso = peso
self.frete = frete # Strategy: a estratégia ativa
self.observadores = [] # Observer: a lista de interessados
def inscrever(self, obs):
self.observadores.append(obs)
def valor_frete(self):
return self.frete.calcular(self.peso) # DELEGA ao frete (Strategy)
def mudar_status(self, status):
for obs in self.observadores: # AVISA todos (Observer)
obs.atualizar(self.codigo, status) resolução · o código completo
As três camadas num arquivo só — é exatamente isto que você roda. Repare como Strategy e Observer convivem: o pedido delega o frete à estratégia ativa e avisa os observadores quando o status muda.
pedido.frete = FreteExpresso() é o Strategy em ação: o frete recalcula sem mexer no valor_frete(). O Contexto delega à estratégia ativa.pedido.mudar_status("ENVIADO") é o Observer em ação: um aviso só dispara as reações do painel e do SMS. A saída exata vem no próximo slide.from abc import ABC, abstractmethod
# Camada 1: Strategy — cálculo de frete
class Frete(ABC):
@abstractmethod
def calcular(self, peso):
pass
class FreteNormal(Frete):
def calcular(self, peso):
return peso * 2.00
class FreteExpresso(Frete):
def calcular(self, peso):
return peso * 2.00 + 20.00
# Camada 2: Observer — os interessados no status
class Observador(ABC):
@abstractmethod
def atualizar(self, pedido, status):
pass
class PainelLojista(Observador):
def atualizar(self, pedido, status):
print(f"[Painel] Pedido {pedido} -> {status}")
class SMSCliente(Observador):
def atualizar(self, pedido, status):
print(f"[SMS] Seu pedido {pedido} agora está: {status}")
# Camada 3: o Pedido é Contexto (Strategy) E Subject (Observer)
class Pedido:
def __init__(self, codigo, peso, frete):
self.codigo = codigo
self.peso = peso
self.frete = frete
self.observadores = []
def inscrever(self, obs):
self.observadores.append(obs)
def valor_frete(self):
return self.frete.calcular(self.peso)
def mudar_status(self, status):
for obs in self.observadores:
obs.atualizar(self.codigo, status)
# Tudo junto: Strategy delega o frete, Observer avisa o status
pedido = Pedido("A-100", 3.0, FreteNormal())
pedido.inscrever(PainelLojista())
pedido.inscrever(SMSCliente())
print(f"Frete normal: R$ {pedido.valor_frete():.2f}")
pedido.frete = FreteExpresso() # troca a estratégia em runtime
print(f"Frete expresso: R$ {pedido.valor_frete():.2f}")
pedido.mudar_status("ENVIADO") # avisa todos os observadores resolução · saída esperada
Rodando o pedido.py, é isto que aparece no terminal:
6.00 = 3.0 × 2.00 (normal) e 26.00 = 3.0 × 2.00 + 20.00 (expresso). O mesmo pedido, frete trocado em runtime.mudar_status("ENVIADO") disparou o painel E o SMS. Um aviso, dois interessados reagindo, cada um do seu jeito.Frete normal: R$ 6.00
Frete expresso: R$ 26.00
[Painel] Pedido A-100 -> ENVIADO
[SMS] Seu pedido A-100 agora está: ENVIADO recapitulando
| item | detalhe |
|---|---|
| Padrão comportamental | A família que cuida de COMO os objetos se comunicam e dividem responsabilidades. Observer e Strategy estão aqui. Contraste: os criacionais (aula 20) cuidam de COMO criar objetos. |
| Observer | Padrão comportamental: um Subject avisa vários Observers quando muda de estado. Um para muitos. Quando algo muda, AVISA vários interessados. |
| Subject (Observável) | No Observer, o objeto observado: mantém a lista de observers e os notifica ao mudar (ex.: o Canal, o Pedido). Tem inscrever()/cancelar() e notifica a lista. |
| notificar / atualizar | notificar = o Subject percorre a lista e chama o método de aviso; atualizar() = o método que cada Observer implementa pra reagir ao aviso. |
| Strategy | Padrão comportamental: encapsula cada algoritmo numa classe e deixa o Contexto trocar de algoritmo em runtime. Troca o COMO sem mudar quem chama. |
| Contexto | No Strategy, o objeto que guarda a estratégia ativa e DELEGA a ela (ex.: o Carrinho, o sorted). Não implementa o algoritmo — só chama o da estratégia. |
| estratégia intercambiável | Cada algoritmo num objeto com o mesmo método comum, trocável a qualquer momento (carrinho.desconto = ...). Pode ser uma classe ou, sem estado, só uma função. |
| delegar | No Strategy, o Contexto não calcula — passa o trabalho à estratégia ativa (self.desconto.calcular(...)). Delegar a UM ≠ notificar VÁRIOS (Observer). |
| composição | A base dos dois (aula 05): o Subject COMPÕE a lista de observers; o Contexto COMPÕE a estratégia ativa. Tem-um, não é-um. |
exercícios
Quatro exercícios em três níveis: um de Observer, um de Strategy, um de Observer a partir de um diagrama, e um desafio que junta os dois padrões. Cada um é INDIVIDUAL e termina em arquivo entregável. Comece em sala; termine o que faltar em casa. Mire pelo menos um de cada nível.
Crie uma classe Estoque (o Subject) com uma lista de assinantes e um método repor(produto) que AVISA todos quando um produto volta ao estoque. Faça uma ABC Assinante com o método abstrato atualizar(produto) e duas subclasses — AssinanteEmail e AssinanteApp — cada uma guardando um nome no __init__ e imprimindo do seu jeito (ex.: [E-mail] Fulano: Notebook voltou ao estoque!). Inclua um método inscrever() e um bloco de teste que inscreve 2 assinantes e chama repor("Notebook") uma vez (os dois devem ser avisados, por loop polimórfico). Entregue o arquivo .py. Foco: Subject com lista + notificar todos por polimorfismo.
Crie uma ABC CalculoFrete com o método abstrato calcular(valor_compra) e três estratégias: FreteGratis (devolve o valor sem alterar), FretePorRegiao(taxa) (soma uma taxa fixa recebida no __init__) e FretePercentual (soma 5% do valor). Crie um Checkout (o Contexto) que recebe uma estratégia e tem total(valor_compra) delegando a ela. No bloco de teste, troque a estratégia 3 vezes no MESMO checkout sobre uma compra de R$ 200,00. Saída de referência: Grátis R$ 200.00 · Por região (taxa 25) R$ 225.00 · Percentual R$ 210.00. Entregue o arquivo .py. Foco: Contexto que delega + estratégia trocável em runtime.
A partir do diagrama do próximo slide — um Leilao (o Subject) com uma lista de participantes, inscrever() e dar_lance(valor) que NOTIFICA todos os participantes; e Participante «abstract» com atualizar(valor), com ParticipanteApp e ParticipanteEmail ──△ Participante (herança), cada um reagindo do seu jeito (ex.: [App] Ana: novo lance de R$ 500.00) — implemente todas as classes em Python. Inclua um bloco de teste que inscreve 2 participantes e dá um lance (loop polimórfico — o leilão não sabe o tipo de cada participante). Entregue o arquivo .py. Foco: Observer de verdade — o Subject avisa a lista, cada observer reage por polimorfismo.
Escolha UM mini sistema: (a) uma estação meteorológica que AVISA vários displays quando a temperatura muda [Observer] e converte a temperatura entre °C e °F por estratégia trocável [Strategy] — a estação acumula os dois papéis, Subject e Contexto, como o Pedido fez em s31; ou (b) um jogo onde um evento AVISA vários sistemas (HUD, som, log) [Observer] e o dano é calculado por uma fórmula trocável (físico, mágico, crítico) [Strategy]. Primeiro DESENHE o diagrama de classes numa ferramenta (draw.io ou PlantUML) com: 1 Subject + observers (ABC) e 1 Contexto + estratégia (ABC), ambos com pelo menos 2 subclasses concretas (herança △). Depois IMPLEMENTE a partir do SEU diagrama: ABC com @abstractmethod nos dois contratos, __str__ nos produtos/observers pra identificar quem reagiu ao imprimir, e um teste que prova o Observer (um aviso → vários reagindo) e o Strategy (mesma operação, estratégia trocada em runtime), tudo por uso polimórfico sem isinstance. Entregue o diagrama em PDF e o código no arquivo .py. Foco: combinar Observer e Strategy no mesmo sistema (preparação pro projeto final da aula 25).
exercício 3 · do diagrama ao código
Este é o diagrama do exercício 3. Leia a relação Subject↔Observers — o Leilao e os Participantes — e traduza cada parte pra código Python.
Leilao) que ◆── compõe uma lista de observers (Participante «abstract» △ App/Email) e os AVISA ao dar_lance(). O losango ◆ é o leilão conhecendo seus participantes; cada um implementa atualizar(valor) do seu jeito.Vimos que ligar uma função a um botão é inscrever um observer. Na próxima, a gente coloca isso na tela: o Tkinter, a biblioteca de interface gráfica do Python. O command= de cada botão é exatamente o Observer de hoje — registrar quem reage ao clique.