Um padrão de projeto (design pattern) é uma solução pronta, testada e com nome próprio para um problema que se repete no design de software orientado a objetos. Ele não é código que você copia e cola: é uma RECEITA — um jeito de organizar classes que você adapta ao seu caso. Dizer "aqui é um Singleton" comunica a ideia inteira numa palavra.
Padrão é remédio pra dor real, não enfeite. Use quando o problema que ele resolve realmente aparece no seu código.
O Singleton garante que uma classe tenha UMA única instância no programa inteiro, e que todos usem a mesma. Resolve o problema de cópias dessincronizadas (cada parte do código criando a sua ConfigSistema, com estados divergentes). Ele controla QUANTAS instâncias existem: sempre uma só.
class ConfigSistema:
_instancia = None # atributo de classe
def __new__(cls):
if cls._instancia is None:
cls._instancia = super().__new__(cls)
cls._instancia.tema = "claro"
return cls._instancia
c1 = ConfigSistema()
c2 = ConfigSistema()
print(c1 is c2) # True — o mesmo objeto Armadilha: o Singleton é uma variável global disfarçada — dificulta testes e, em excesso, embaralha o código. Use só quando faz sentido existir UM e apenas um (config, log, pool de conexões).
A Factory centraliza num lugar só a decisão de QUAL classe instanciar, a partir de um texto. Resolve o problema do if/elif de criação espalhado por vários pontos do código — quando entra um tipo novo, você muda um lugar só. É o balcão da lanchonete: você pede pelo nome e recebe o objeto pronto.
from abc import ABC, abstractmethod
class Notificacao(ABC):
@abstractmethod
def enviar(self, mensagem):
pass
# (subclasses EmailNotificacao, SMSNotificacao... aqui)
def criar_notificacao(canal):
tabela = {"email": EmailNotificacao, "sms": SMSNotificacao}
if canal not in tabela:
raise ValueError(f"Canal desconhecido: {canal}")
return tabela[canal]() # pega a classe e instancia Singleton controla QUANTAS instâncias existem (uma só); o Simple Factory decide QUAL classe instanciar a partir de um parâmetro. Quando a criação varia por contexto, sobe-se pro Factory Method.
O Simple Factory (uma função central com dicionário {texto: Classe}) resolve "qual classe a partir de um parâmetro". Mas quando o mesmo produto precisa ser criado de jeitos diferentes dependendo do CONTEXTO (a calabresa paulistana vs. a carioca), a função central incha de combinações. O Factory Method — o padrão GoF — tira essa decisão da função e devolve pra quem conhece o contexto: a SUBCLASSE do criador.
from abc import ABC, abstractmethod
class Pizza(ABC):
@abstractmethod
def descricao(self):
pass
class Pizzaria(ABC): # o Criador
@abstractmethod
def criar_pizza(self): # o factory method
pass
def entregar(self):
pizza = self.criar_pizza() # usa o produto por polimorfismo
return f"Entregando: {pizza.descricao()}"
class PizzariaPaulistana(Pizzaria):
def criar_pizza(self):
return CalabresaPaulistana() Simple Factory basta na maioria dos casos (escolha por parâmetro, num lugar só). Factory Method entra quando a criação precisa variar por contexto. Não comece pelo complicado: suba o degrau só quando a dor aparecer.
Programadores resolvem os mesmos problemas de estrutura toda hora. Os padrões de projeto são as soluções que já viraram consenso. Hoje vemos os criacionais: Singleton (uma instância só) e as Factories — do Simple Factory (um parâmetro escolhe a classe) ao Factory Method (a subclasse do criador decide).
roteiro da aula
Solução pronta e testada pra um problema que reaparece no design de software. E o que são os criacionais.
Garantir que uma classe tenha UMA única instância no programa inteiro — e todo mundo usa a mesma.
O primeiro degrau: centralizar num lugar só a decisão de qual classe criar a partir de um parâmetro — matando o if/elif espalhado.
O destino (GoF): mover a decisão de qual objeto criar pra uma subclasse do criador, quando a criação varia por contexto.
ponte com a aula 19
@abstractmethod (10), polimorfismo (09), composição (05) e atributos de classe (03/06). o problema
definição
de onde vêm
panorama
| item | detalhe |
|---|---|
| Criacionais ⭐ | Cuidam de COMO criar objetos. Singleton e Factory (de hoje) estão aqui. É o nosso foco. |
| Estruturais | Cuidam de como montar objetos em estruturas maiores (ex.: Adapter, Composite). (Só citado.) |
| Comportamentais | Cuidam de como os objetos conversam e dividem responsabilidades (ex.: Observer, Strategy — aula 21). (Só citado.) |
singleton · definição
singleton · o problema
ConfigSistema(), você acaba com várias cópias soltas pelo programa. singleton · o problema
O mesmo programa, dois jeitos de criar a configuração: várias cópias soltas (o problema) e uma instância única compartilhada (o Singleton).
# cada tela cria a SUA cópia
config_a = ConfigSistema()
config_b = ConfigSistema()
config_a.tema = "escuro"
print(config_b.tema)
# claro ← b NÃO viu a mudança de a
# objetos diferentes, estados diferentes. # Singleton: sempre a MESMA instância
config_a = ConfigSistema()
config_b = ConfigSistema()
config_a.tema = "escuro"
print(config_b.tema)
# escuro ← b é o MESMO objeto que a
# mudou num, mudou em todos. singleton · ancorando a ideia
singleton · o código
O segredo é o __new__ — o método que o Python chama ANTES do __init__ pra criar o objeto. A gente intercepta e devolve sempre a mesma instância, guardada num atributo de classe.
_instancia é um atributo de CLASSE (aula 03/06): é compartilhado por todas as chamadas, o lugar perfeito pra guardar "a única instância".__new__ é o porteiro: se ainda não existe instância, cria uma; se já existe, devolve a mesma. Por isso config1 is config2 dá True.class ConfigSistema:
_instancia = None # atributo de CLASSE: guarda a única instância
def __new__(cls):
if cls._instancia is None:
cls._instancia = super().__new__(cls)
cls._instancia.tema = "claro"
return cls._instancia
config1 = ConfigSistema()
config2 = ConfigSistema()
print(config1 is config2) # True — é o MESMO objeto
config1.tema = "escuro"
print(config2.tema) # escuro — mudou nos dois, porque é um só
print(config1.tema) # escuro singleton · o código
Rodando o config.py, é isto que aparece — a prova de que existe uma instância só:
True: config1 e config2 são o MESMO objeto. Pedimos a configuração duas vezes e recebemos a mesma.config1.tema e config2.tema também mudou: como é um objeto só, a alteração aparece em todos os lugares que o usam.True
escuro
escuro singleton · na UML
O ConfigSistema que acabamos de codar, desenhado como caixa de classe (a notação da aula 19): dá pra marcar que ele é um Singleton sem ler uma linha de código.
- _instancia — a própria classe guardando a sua única instância. Guarde essa marca: você vai reencontrá-la no diagrama do estacionamento, mais à frente.singleton · variação
Há outra forma comum: em vez de mexer no __new__, expor um @classmethod get_instancia() (aula 06) que cria na primeira chamada e devolve a mesma daí em diante. Um Logger central é o caso clássico.
Logger() direto: pede-se sempre Logger.get_instancia(). O @classmethod recebe a própria classe (cls) e controla a criação.log1 e log2 são o mesmo objeto, as duas mensagens caem na MESMA lista — um log central que o programa inteiro compartilha.class Logger:
_instancia = None
def __init__(self):
self.mensagens = []
@classmethod
def get_instancia(cls):
if cls._instancia is None:
cls._instancia = cls()
return cls._instancia
def registrar(self, msg):
self.mensagens.append(msg)
log1 = Logger.get_instancia()
log2 = Logger.get_instancia()
print(log1 is log2) # True — sempre o mesmo Logger
log1.registrar("sistema iniciou")
log2.registrar("usuario logou")
print(log1.mensagens) # ['sistema iniciou', 'usuario logou'] singleton · amarrando com a POO
_instancia vive na classe, não no objeto — por isso todas as chamadas enxergam o mesmo valor. _ em _instancia sinaliza "detalhe interno, não mexa de fora". O controle da criação fica protegido dentro da classe. __new__ é primo do __init__ e do __str__ que você já usa — um método especial que o Python chama sozinho. is vs == (aula 03): is compara identidade (mesmo objeto na memória); == compara valor. O Singleton se prova com is. singleton · quando NÃO usar
factory · definição
factory · o problema
"calabresa", "margherita"...) você precisa criar a pizza certa. if sabor == "calabresa": ... elif sabor == "margherita": ... — e esse mesmo if/elif acaba COPIADO em vários pontos do código. factory · ancorando a ideia
new em nada. factory · simple factory
Começamos pela forma mais simples: o Simple Factory. Uma ABC Pizza (aula 10) com as subclasses; depois a Factory — uma FUNÇÃO central com um dicionário que mapeia o texto pra CLASSE (sem parênteses!) e cria a pizza num lugar só.
Calabresa sem (). A classe é um valor como qualquer outro — dá pra colocar num dicionário.tabela[sabor]() faz duas coisas: tabela[sabor] pega a classe certa, e o () a instancia. Se o sabor não existe, um ValueError claro.from abc import ABC, abstractmethod
class Pizza(ABC):
@abstractmethod
def descricao(self):
pass
class Calabresa(Pizza):
def descricao(self):
return "Calabresa com cebola"
class Margherita(Pizza):
def descricao(self):
return "Margherita com manjericão"
class Portuguesa(Pizza):
def descricao(self):
return "Portuguesa com ovo e ervilha"
# Simple Factory: uma FUNÇÃO central que mapeia o sabor -> a CLASSE (sem parênteses!)
def criar_pizza(sabor):
tabela = {
"calabresa": Calabresa,
"margherita": Margherita,
"portuguesa": Portuguesa,
}
if sabor not in tabela:
raise ValueError(f"Sabor desconhecido: {sabor}")
return tabela[sabor]() # os parênteses criam o objeto aqui factory · simple factory
Com a Factory pronta, o resto do programa fica limpo: pede a pizza pela Factory e trata todas como Pizza. O descricao() certo é chamado por polimorfismo (aula 09).
for não sabe qual classe concreta está usando — só chama .descricao(). Cada pizza responde do seu jeito: é o polimorfismo da aula 09.if sabor == ... aqui. Toda a decisão ficou DENTRO da Factory. Este código nunca precisa mudar quando entra um sabor novo.# polimorfismo: trato todas como Pizza, sem if/elif
for sabor in ["calabresa", "margherita", "portuguesa"]:
print(criar_pizza(sabor).descricao()) factory · simple factory
Rodando o pizzaria.py (a hierarquia + a Factory + o loop), sai:
descricao() — cada um devolve seu próprio texto. A Factory entregou o objeto certo; o polimorfismo fez o resto.QuatroQueijos e somar uma linha no dicionário da Factory. O loop acima não muda nem uma vírgula.Calabresa com cebola
Margherita com manjericão
Portuguesa com ovo e ervilha factory · o degrau
O Simple Factory é um interruptor só: um parâmetro escolhe a classe. Mas e se a MESMA pizza for feita de jeitos diferentes dependendo do CONTEXTO — a calabresa paulistana (massa fina) e a carioca (borda com catupiry)?
# pra variar por contexto, o parâmetro
# vira uma combinação que explode:
def criar_pizza(sabor, cidade):
if cidade == "sp" and sabor == "calabresa":
return CalabresaPaulistana()
elif cidade == "rj" and sabor == "calabresa":
return CalabresaCarioca()
# ... e cada cidade x sabor é mais um if
# o if/elif que a gente matou VOLTOU. A função central decide TUDO sozinha.
Quando o "como criar" varia por
contexto (cidade, filial, tema...),
ela vira um emaranhado de combinações.
Queríamos: cada CONTEXTO sabe fazer
a SUA pizza, sem inchar um lugar só.
Esse é o trabalho do Factory Method. factory · factory method (GoF)
O Factory Method (o padrão GoF) resolve isso: em vez de uma função central, criamos uma ABC Pizzaria (o Criador) com um criar_pizza() ABSTRATO. Cada subclasse de pizzaria sobrescreve esse método e devolve a SUA pizza. Quem decide qual produto é a SUBCLASSE — não um parâmetro.
Pizzaria(ABC) é o Criador: define o contrato criar_pizza() (abstrato) e um entregar() que USA o produto sem saber qual classe concreta veio.criar_pizza() e devolve a SUA pizza. A decisão de QUAL produto não está num parâmetro — está na escolha da subclasse de pizzaria.from abc import ABC, abstractmethod
class Pizza(ABC):
@abstractmethod
def descricao(self):
pass
class CalabresaPaulistana(Pizza):
def descricao(self):
return "Calabresa paulistana (massa fina, muita mussarela)"
class CalabresaCarioca(Pizza):
def descricao(self):
return "Calabresa carioca (borda com catupiry)"
class Pizzaria(ABC): # o Criador
@abstractmethod
def criar_pizza(self): # o factory method
pass
def entregar(self): # usa o produto sem saber o tipo concreto
pizza = self.criar_pizza()
return f"Entregando: {pizza.descricao()}"
class PizzariaPaulistana(Pizzaria):
def criar_pizza(self):
return CalabresaPaulistana()
class PizzariaCarioca(Pizzaria):
def criar_pizza(self):
return CalabresaCarioca() factory · factory method (GoF)
Cada contexto (cidade) é uma pizzaria diferente. O loop trata todas como Pizzaria e chama entregar() — cada uma cria e entrega a SUA pizza, por polimorfismo.
for não sabe (nem precisa) qual pizzaria concreta está usando — só chama entregar(). Cada uma cria a sua pizza pelo criar_pizza() sobrescrito.PizzariaMineira(Pizzaria) com seu próprio criar_pizza(). Nenhuma função central muda — a hierarquia só cresce com mais uma subclasse.for pizzaria in [PizzariaPaulistana(), PizzariaCarioca()]:
print(pizzaria.entregar())
# Saída no terminal:
# Entregando: Calabresa paulistana (massa fina, muita mussarela)
# Entregando: Calabresa carioca (borda com catupiry) factory · factory method (GoF)
São DUAS hierarquias paralelas: a do Criador (Pizzaria «abstract» ◁ as pizzarias) e a do Produto (Pizza «abstract» ◁ as pizzas). Cada criador concreto «creates» o seu produto.
factory · amarrando com a POO
Pizzaria é uma ABC com criar_pizza() abstrato; cada pizzaria HERDA e sobrescreve. É o mesmo contrato de classe abstrata de sempre. entregar() chama self.criar_pizza() sem saber a subclasse — o método-base usa o produto certo por polimorfismo. Sem ele, o padrão não existiria. @classmethod (aula 06): lá um construtor alternativo (from_string) já era uma Factory simples. Hoje a Factory subiu de nível: escolhe entre classes (Simple Factory) e até entre contextos (Factory Method). factory · escolhendo
Os dois são Factories, mas a escolha vem de lugares diferentes. Simple Factory: a escolha está num PARÂMETRO. Factory Method: a escolha está na SUBCLASSE do criador.
A escolha vem de um PARÂMETRO,
num lugar só (uma função central).
criar_pizza("calabresa")
Ótimo quando:
• o "como criar" NÃO varia por contexto
• você só precisa mapear texto -> classe
É o caso mais comum — e o mais simples. A escolha vem da SUBCLASSE do criador.
PizzariaPaulistana().entregar()
Use quando:
• o "como criar" varia por CONTEXTO
(cidade, filial, tema, plataforma)
• a criação cresce e ia inchar a função
Mais estrutura, em troca de extensão. factory · quando NÃO usar
if simples que aparece em UM lugar só, montar uma Factory é exagero — over-engineering. Um if direto resolve. fixando os três
Três criacionais, três perguntas diferentes. Grave: Singleton = QUANTAS instâncias; Simple Factory = um PARÂMETRO escolhe; Factory Method = a SUBCLASSE escolhe.
Pergunta: quantas instâncias existem?
Resposta: UMA só, sempre a mesma.
Mecanismo: __new__ / get_instancia
guardando _instancia.
Exemplo: ConfigSistema, Logger. Pergunta: qual classe instanciar?
Resposta: um PARÂMETRO escolhe,
numa função central.
Mecanismo: dicionário {texto: Classe}
+ ABC + polimorfismo.
Exemplo: criar_pizza(sabor). Pergunta: qual produto, por contexto?
Resposta: a SUBCLASSE do criador
decide, sobrescrevendo o método.
Mecanismo: ABC Criador + criar_X()
abstrato + subclasses.
Exemplo: PizzariaPaulistana(). ao vivo · vamos construir juntos
Partindo do diagrama do próximo slide, a gente implementa juntos um sistema de estacionamento que usa Factory (pra criar o veículo a partir de um texto) e Singleton (o painel de controle, único no estacionamento inteiro). Acompanhem a tradução de cada parte pra código.
Veiculo «abstract» é a base, com tarifa_hora() abstrato. Carro, Moto e Caminhao ──△ Veiculo (herança). PainelControle «singleton» é o painel único — uma instância só.
Veiculo(ABC) com @abstractmethod tarifa_hora(), e Carro (8,00), Moto (4,00), Caminhao (15,00) implementando cada um o seu valor.
criar_veiculo(tipo, placa) com o dicionário {"carro": Carro, ...} e um raise ValueError pro tipo desconhecido — a decisão de QUAL classe num lugar só.
PainelControle com __new__ e _instancia, começando com vagas_livres = 100 e um registrar_entrada() que desconta uma vaga — sempre no MESMO painel.
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.
resolução · camada 1
Começamos pela base da Factory: a ABC Veiculo com o método abstrato tarifa_hora() e as três subclasses, cada uma com a sua tarifa. É o triângulo △ do diagrama virando código.
Veiculo(ABC) com @abstractmethod é o estereótipo «abstract» do diagrama (aula 10): define o contrato tarifa_hora(), mas não pode ser instanciada direto.from abc import ABC, abstractmethod
# Camada 1: hierarquia de veículos (ABC + polimorfismo)
class Veiculo(ABC):
def __init__(self, placa):
self.placa = placa
@abstractmethod
def tarifa_hora(self):
pass
class Carro(Veiculo):
def tarifa_hora(self):
return 8.00
class Moto(Veiculo):
def tarifa_hora(self):
return 4.00
class Caminhao(Veiculo):
def tarifa_hora(self):
return 15.00
# cada subclasse responde à sua maneira (polimorfismo da aula 09)
carro = Carro("ABC-1A23")
moto = Moto("XYZ-9999")
caminhao = Caminhao("BRA-2E19")
print(f"{carro.placa}: R$ {carro.tarifa_hora():.2f}/h") # ABC-1A23: R$ 8.00/h
print(f"{moto.placa}: R$ {moto.tarifa_hora():.2f}/h") # XYZ-9999: R$ 4.00/h
print(f"{caminhao.placa}: R$ {caminhao.tarifa_hora():.2f}/h") # BRA-2E19: R$ 15.00/h resolução · camada 2
Agora a Factory: um dicionário {texto: Classe} e um ValueError pro tipo desconhecido. Recebe o texto, devolve o objeto certo — num lugar só.
()). tabela[tipo] pega a classe certa pelo texto, e (placa) a instancia passando a placa pro __init__.raise ValueError protege contra texto inválido: pedir "barco" falha com uma mensagem clara, em vez de devolver None silencioso.# Camada 2: a Factory de veículos
def criar_veiculo(tipo, placa):
tabela = {
"carro": Carro,
"moto": Moto,
"caminhao": Caminhao,
}
if tipo not in tabela:
raise ValueError(f"Tipo de veículo desconhecido: {tipo}")
return tabela[tipo](placa) # pega a classe e instancia resolução · camada 3
A última camada é o Singleton: o painel de controle do estacionamento, único no sistema inteiro. Usa __new__ + _instancia, igual ao ConfigSistema de antes.
__new__ + _instancia garantem um painel só: não importa quantas vezes alguém chame PainelControle(), é sempre o mesmo objeto.vagas_livres começa em 100 e cada registrar_entrada() desconta uma. Como o painel é único, a contagem fica certa no programa inteiro.# Camada 3: o painel de controle (Singleton)
class PainelControle:
_instancia = None
def __new__(cls):
if cls._instancia is None:
cls._instancia = super().__new__(cls)
cls._instancia.vagas_livres = 100
return cls._instancia
def registrar_entrada(self):
self.vagas_livres -= 1 resolução · o código completo
As três camadas num arquivo só — é exatamente isto que você roda. Repare como Factory e Singleton convivem: a Factory cria cada veículo, e o painel único conta as entradas.
criar_veiculo(tipo, ...) é a Factory; PainelControle() dentro do loop é o Singleton — sempre o mesmo painel, somando as 4 entradas no mesmo contador.is) e do estado compartilhado (as vagas). A saída exata vem no próximo slide.from abc import ABC, abstractmethod
# Camada 1: hierarquia de veículos (ABC + polimorfismo)
class Veiculo(ABC):
def __init__(self, placa):
self.placa = placa
@abstractmethod
def tarifa_hora(self):
pass
class Carro(Veiculo):
def tarifa_hora(self):
return 8.00
class Moto(Veiculo):
def tarifa_hora(self):
return 4.00
class Caminhao(Veiculo):
def tarifa_hora(self):
return 15.00
# Camada 2: a Factory de veículos
def criar_veiculo(tipo, placa):
tabela = {
"carro": Carro,
"moto": Moto,
"caminhao": Caminhao,
}
if tipo not in tabela:
raise ValueError(f"Tipo de veículo desconhecido: {tipo}")
return tabela[tipo](placa)
# Camada 3: o painel de controle (Singleton)
class PainelControle:
_instancia = None
def __new__(cls):
if cls._instancia is None:
cls._instancia = super().__new__(cls)
cls._instancia.vagas_livres = 100
return cls._instancia
def registrar_entrada(self):
self.vagas_livres -= 1
# Tudo junto: a Factory cria, o painel único conta
entradas = [
("carro", "ABC-1234"),
("moto", "DEF-5678"),
("caminhao", "GHI-9012"),
("carro", "JKL-3456"),
]
for tipo, placa in entradas:
veiculo = criar_veiculo(tipo, placa) # Factory cria o veículo certo
painel = PainelControle() # sempre o MESMO painel
painel.registrar_entrada()
print(f"{tipo} {veiculo.placa}: R$ {veiculo.tarifa_hora():.2f}/h")
p1 = PainelControle()
p2 = PainelControle()
print(f"Mesmo painel? {p1 is p2}")
print(f"Vagas livres: {p1.vagas_livres}") resolução · saída esperada
Rodando o estacionamento.py, é isto que aparece no terminal:
Mesmo painel? True prova o Singleton (p1 is p2); Vagas livres: 96 prova o estado compartilhado: as 4 entradas desceram de 100 no MESMO painel.carro ABC-1234: R$ 8.00/h
moto DEF-5678: R$ 4.00/h
caminhao GHI-9012: R$ 15.00/h
carro JKL-3456: R$ 8.00/h
Mesmo painel? True
Vagas livres: 96 recapitulando
| item | detalhe |
|---|---|
| Padrão de projeto | Solução pronta, testada e nomeada pra um problema recorrente no design de software OO. |
| GoF (Gang of Four) | Apelido dos 4 autores do livro Design Patterns (1994), que catalogou 23 padrões clássicos. "Padrão GoF" = um desses padrões oficiais. Simple Factory não é GoF — é um idiom. |
| Padrões criacionais | A família que cuida de COMO os objetos são criados. Singleton, Simple Factory e Factory Method estão aqui. |
| Singleton | Garante UMA única instância da classe no programa inteiro. Controla QUANTAS. Via __new__ + _instancia. |
| Simple Factory | Uma função/método central que devolve a classe certa a partir de um PARÂMETRO (dicionário {texto: Classe}). Mata o if/elif espalhado. É um idiom, não um padrão GoF. |
| Factory Method | Padrão GoF: um Criador abstrato com um método Factory que as SUBCLASSES sobrescrevem pra decidir o produto. A decisão vem da subclasse, não de um parâmetro. |
| __new__ | Método especial chamado ANTES do __init__ pra criar o objeto. O Singleton o intercepta pra devolver sempre o mesmo. |
| is vs == | is compara identidade (mesmo objeto na memória); == compara valor. O Singleton se prova com is. Aula 03. |
exercícios
Quatro exercícios em três níveis: um de Singleton, um de Simple Factory, um de Factory Method, e um desafio que junta os 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.
Implemente uma classe ConexaoBanco como Singleton, usando __new__ e _instancia. No __new__, inicialize consultas_feitas = 0. Adicione um método consultar() que soma 1 a esse contador. Inclua um bloco de teste que cria db1 e db2, imprime db1 is db2 (deve dar True), faz três consultas alternando entre db1 e db2, e imprime db1.consultas_feitas (deve dar 3 — o estado é compartilhado). Entregue o arquivo .py. Foco: provar a instância única com is e o estado compartilhado.
Crie uma ABC Pagamento com o método abstrato processar(valor) e três subclasses — PagamentoPix, PagamentoCartao e PagamentoBoleto — cada uma imprimindo do seu jeito (ex.: [PIX] processando R$ 50.00). Escreva a Factory criar_pagamento(tipo) com um dicionário {texto: Classe} e um raise ValueError pro tipo desconhecido. Inclua um bloco de teste que percorre ["pix", "cartao", "boleto"] e chama processar(50.00) em cada um (loop polimórfico, sem if/elif). Entregue o arquivo .py. Foco: dicionário de classes + ABC + polimorfismo.
A partir do diagrama do próximo slide — um Criador abstrato Exportador «abstract» com o método Factory criar_relatorio() e um método gerar() que usa self.criar_relatorio(); e ExportadorPDF, ExportadorExcel e ExportadorHTML ──△ Exportador (herança), cada um sobrescrevendo criar_relatorio() pra devolver seu próprio produto (RelatorioPDF, RelatorioExcel, RelatorioHTML, cada um com um conteudo() que devolve algo do seu formato, ex.: [PDF] relatório gerado) — implemente todas as classes em Python. Inclua um bloco de teste que percorre uma lista de exportadores e chama gerar() em cada um (loop polimórfico — quem chama não sabe qual exportador é). A saída deve ser: [PDF] relatório gerado · [Excel] relatório gerado · [HTML] relatório gerado. Entregue o arquivo .py. Foco: Factory Method de verdade — a decisão do produto mora na subclasse do criador, não num parâmetro.
Escolha UM mini sistema: (a) Loja virtual, (b) Jogo de RPG ou (c) App de transporte. Primeiro DESENHE o diagrama de classes numa ferramenta (draw.io ou PlantUML) com: 1 Singleton (estereótipo «singleton») e um Factory Method — um Criador abstrato com um método Factory sobrescrito por pelo menos 2 subclasses (herança △), cada uma criando um produto da sua própria hierarquia. Depois IMPLEMENTE a partir do SEU diagrama: o Singleton com __new__, o Criador como ABC com @abstractmethod, as subclasses sobrescrevendo o método Factory, __str__ no produto (pra identificar qual foi criado ao imprimir), e um teste que prova a instância única com is e usa os criadores num loop polimórfico. Entregue o diagrama em PDF e o código no arquivo .py. Foco: combinar Singleton e Factory Method 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 as duas hierarquias — o Criador Exportador e o Produto Relatorio — e traduza cada parte pra código Python.
Exportador) cujo método-fábrica criar_relatorio() cada subclasse sobrescreve, devolvendo um produto da hierarquia Relatorio. A seta tracejada «cria» liga cada criador ao seu produto.Vimos os criacionais (como CRIAR objetos). Na próxima, os COMPORTAMENTAIS: como os objetos se comunicam (Observer) e como trocar de algoritmo em tempo de execução (Strategy).