Padrões de Projeto em Python
Raul Coelho Cabral Antonio Miguel de Sousa Filho
[email protected] [email protected] Abstract—O presente artigo fala sobre os padrões de projeto público a 3ª linguagem "mais amada", de acordo com uma
(do inglês design pattern) que é uma solução geral para um pesquisa conduzida pelo site Stack Overflow em 2018, e está
problema que ocorre com frequência dentro de um determinado entre as 5 linguagens mais populares, de acordo com uma
contexto no projeto de software. Um padrão de projeto não
é uma solução pronta nem um projeto finalizado, mas sim pesquisa conduzida pela RedMonk.
um modo de abordar um problema já conhecido e estudado. Python suporta a maioria das técnicas da programação ori-
Este artigo aborda os padrões de projeto na linguagem de entada a objeto. Qualquer objeto pode ser usado para qualquer
programação Python que é ótima para aplicar padrões de tipo, e o código funcionará enquanto haja métodos e atributos
projeto, principalmente por ser orientada a objetos, ter tipagem
adequados. O conceito de objeto na linguagem é bastante
dinâmica, dentre outras coisas.
Index Terms—Padrões de projeto, python, design pattern. abrangente: classes, funções, números e módulos são todos
considerados objetos. Também há suporte para metaclasses,
I. I NTRODUÇÃO polimorfismo, e herança (inclusive herança múltipla). Há um
Neste artigo e descrito sobre Padrões de design, que são suporte limitado para variáveis privadas.
soluções para problemas recorrentes; diretrizes sobre como
lidar com certos problemas . Eles não são classes, pacotes ou C. Por que Python?
bibliotecas que você pode conectar ao seu aplicativo e aguardar Python é uma linguagem de script de código aberto. Possui
a mágica acontecer. Estas são, antes, orientações sobre como bibliotecas que oferecem suporte a uma variedade de padrões
lidar com certos problemas em determinadas situações. de design. A sintaxe do python é fácil de entender e usa
O arquiteto Christopher Alexander coloca que, um padrão palavras-chave em inglês.
de projeto deve ter, idealmente, as seguintes característi-
cas: Encapsulamento, Generalidade, Equilíbrio, Abstração,
II. PADRÕES G O F
Abertura e Combinatoriedade, sendo assim robusto para
resolver um problema pequeno ou grande e específico para De acordo com o livro: "Padrões de Projeto: soluções
resolver um problema singular. reutilizáveis de software orientado a objetos", os padrões
A. Segundo a Wikipédia "GoF" são divididos em 24 tipos. Em função dessa grande
quantidade de padrões, foi necessário classificá-los de acordo
Na engenharia de software, um padrão de design de software
com as suas finalidades.
é uma solução reutilizável geral para um problema comum
São 3 as classificações/famílias:
em um determinado contexto no design de software. Não é
um design final que pode ser transformado diretamente em • Padrões de criação: Os padrões de criação são aqueles
código-fonte ou máquina. É uma descrição ou modelo de como que abstraem e/ou adiam o processo criação dos objetos.
resolver um problema que pode ser usado em muitas situações Eles ajudam a tornar um sistema independente de como
diferentes. seus objetos são criados, compostos e representados. Um
padrão de criação de classe usa a herança para variar
B. Sobre a linguagem a classe que é instanciada, enquanto que um padrão
Python é uma linguagem de programação de alto nível, de criação de objeto delegará a instanciação para outro
interpretada, de script, imperativa, orientada a objetos, fun- objeto.
cional, de tipagem dinâmica e forte. Foi lançada por Guido • Padrões estruturais: Os padrões estruturais se preocu-
van Rossum em 1991. Atualmente possui um modelo de pam com a forma como classes e objetos são compostos
desenvolvimento comunitário, aberto e gerenciado pela or- para formar estruturas maiores. Os de classes utilizam a
ganização sem fins lucrativos Python Software Foundation. herança para compor interfaces ou implementações, e os
Apesar de várias partes da linguagem possuírem padrões e de objeto descrevem maneiras de compor objetos para
especificações formais, a linguagem como um todo não é obter novas funcionalidades.
formalmente especificada. O padrão de fato é a implementação • Padrões comportamentais: Os padrões de comporta-
CPython. mento se concentram nos algoritmos e atribuições de
Devido às suas características, ela é principalmente utilizada responsabilidades entre os objetos. Eles não descrevem
para processamento de textos, dados científicos e criação de apenas padrões de objetos ou de classes, mas também os
CGIs para páginas dinâmicas para a web. Foi considerada pelo padrões de comunicação entre os objetos.
III. PADRÕES C OMPORTAMENTAIS
A. Padrão Strategy
Na utilização do strategy podemos passar como parâmetro
uma função, ou melhor, uma estratégia, para outro método,
permitindo remover vários comandos condicionais, uma vez
que não precisamos verificar manualmente qual foi a estratégia
passada como parâmetro.
Nota: Diferença entre função e método: um método per-
tence a uma classe e uma função não pertence necessariamente Fig. 2. Implementação do Chain of Responsability em Java.
a uma classe.
Duck typing: é um estilo de tipagem em que os métodos e
propriedades de um objeto determinam a semântica válida, em Em java é usado um Middleware como mostra a figura2:
vez de sua herança de uma classe particular ou implementação para direcionar a responsabilidade para o próximo caso não
de uma interface explícita. seja o responsável por acatar essa responsabilidade.
Em python é usado um Handller para a mesma finalidade
B. Padrão State como mostra a figura 3:
O padrão State é uma solução para o problema de como
fazer o comportamento depender do estado.
• Defina uma classe "context" para apresentar uma única
interface para o mundo externo.
• Defina uma classe base abstrata do estado.
• Represente os diferentes "estados" da máquina de estados
como classes derivadas da classe base do Estado.
• Defina o comportamento específico do estado nas classes
derivadas do estado apropriadas.
• Mantenha um ponteiro para o "estado" atual na classe
"contexto".
• Para alterar o estado da máquina de estado, altere o
ponteiro "estado" atual.
Ao final o estado da classe será definida pela própria classe
através de requisições como mostra na Figura 1:
Fig. 3. Implementação do Chain of Responsability em Python.
D. Padrão Command
O padrão Command nos diz como criar “objetos de co-
mando” que encapsula uma solicitação para fazer algo em um
objeto específico. Basicamente todos os objetos de Comando
implementam a mesma interface, que consiste em um único
método normalmente chamado de execute().
Fig. 1. Implementação final do padrão state. O Padrão Command tem como definição encapsular uma
solicitação como um objeto, o que lhe permite parametrizar
outros objetos com diferentes solicitações, enfileirar ou regis-
C. Padrão Chain of Responsibility
trar solicitações e implementar recursos de cancelamento de
Nos permite aplicar uma lógica sequencial de forma operações.
dinâmica. Utilizando este padrão podemos deixar de usar Em python Na maioria das vezes, é usado como uma
diversos IFs e ELSEs por uma cadeia de classes que serão alternativa para retornos de chamada para parametrizar ele-
executadas em sequência. Isso facilita bastante a manutenção mentos da interface do usuário com ações. Também é usado
do código. Evita o acoplamento do remetente de uma solici- para tarefas de enfileiramento, rastreamento de histórico de
tação ao destinatário, dando a mais de um objeto a chance de operações etc.
lidar com a solicitação. 1) Diferença do Command para o Strategy: A ideia do
A implementação do Chain of Responsability em Python Command é abstrair um comando que deve ser executado, pois
não diferencia da implementação de Java em aspectos estru- não é possível executá-lo naquele momento (pois precisamos
turais ou lógicos, apenas a sintaxe da linguagem mudará. por em uma fila ou coisa do tipo). Já no Strategy, a ideia é
que você tenha uma estratégia (um algoritmo) para resolver G. Padrão Null Object
um problema.
A intenção de um Null Object é encapsular a ausência de um
objeto, fornecendo uma alternativa substituível que ofereça um
E. Padrão Observer
comportamento padrão sem fazer nada. Em suma, um design
O padrão observer nos permite passar uma lista de obser- em que "nada resultará em nada".
vadores, interessados pela criação de um objeto, para uma Em python a implementação desse padrão se torna uma
classe, e iterar sobre ela para rodar todos os observadores. interface idêntica à do AbstractObject para que um valor nulo
Assim, caso tenhamos um novo observador, basta incluí-lo na do objeto pode ser substituído por um objeto real. Implemente
lista de observadores. O padrão Observer é bastante comum sua interface para não fazer nada. O que exatamente significa
no código Python, especialmente nos componentes da GUI. fazer nada depende de que tipo de comportamento o cliente
Ele fornece uma maneira de reagir a eventos que acontecem está esperando.
em outros objetos sem acoplamento às suas classes.
Identificação: O padrão pode ser reconhecido por métodos
IV. PADRÕES C RIACIONAIS
de assinatura, que armazenam objetos em uma lista e por
chamadas para o método de atualização emitido para objetos A. Padrão Singleton
nessa lista.
O Singleton é um dos padrões mais simples e possui um
F. Padrão Template Method objetivo bem específico, garantir que apenas um objeto de uma
determinada classe seja criado.
É um padrão de design comportamental que define o
esqueleto de um algoritmo na superclasse, mas permite que • Garanta que uma classe tenha apenas uma instância e
as subclasses substituam etapas específicas do algoritmo sem forneça um ponto de acesso global a ela.
alterar sua estrutura. • Encapsulado "inicialização just-in-time" ou "inicialização
O padrão sugere que você divida um algoritmo em uma série no primeiro uso".
de etapas, transforme essas etapas em métodos e faça uma A figura 5 demonstra uma forma de implementação do sin-
série de chamadas para esses métodos em um único "método gleton em python:
de modelo". As etapas podem ser abstract ou ter algum padrão
implementação. Para usar o algoritmo, o cliente deve fornecer
sua própria subclasse, implementar todas as etapas abstratas e
substituir algumas opcionais, se necessário (mas não o próprio
método de modelo).
O padrão Template Method é bastante comum nas estruturas
Python. Os desenvolvedores costumam usá-lo para fornecer
aos usuários da estrutura um meio simples de estender a
funcionalidade padrão usando herança como aparece na Figura
4.
Fig. 5. Implementação do Singleton em Python
B. Padrão Prototype
Declare uma classe base abstrata que especifica um método
"clone" virtual puro e mantém um dicionário de todas as
classes derivadas concretas "clonáveis". Qualquer classe que
precise de um recurso "construtor polimórfico": deriva da
classe base abstrata, registra sua instância prototípica e imple-
menta a clone() operação.
A diferenciação de implementação do padrão de Java para
Python somente é a sintaxe da linguagem, a estrutura e lógica
continuam iguais.
O padrão Prototype está disponível no Python imediata-
mente com uma Cloneable interface.
Fig. 4. Demonstração do template method em Python Em python a utilização do recurso da linguagem como
mostra a Figura 6:
compatível com outras classes. Quando precisamos que uma
classe existente funcione com outras classes sem modificar seu
código fonte. Em python a sintaxe muda porém a estrutura do
padrão não. A Figura 8 demonstra um adaptador de XML
para JSON.
Fig. 6. Implementação do Prototype em Python
C. Padrão Builder
Com o padrão de projeto builder, podemos criar uma classe
builder, a qual é responsável por receber os parâmetros, veri-
ficar a validade dos parâmetros e até mesmo definir parâmetros
padrões quando necessário.
A própria linguagem Python disponibiliza formas de
parâmetros nomeados e parâmetros padrões, o que facilita
bastante e muitas vezes elimina a necessidade da utilização
do padrão builder. Porém, na linguagem Python, esse padrão
muitas vezes é desnecessário, já que parâmetros nomeados e
opcionais do construtor de classes, podem muitas vezes lidar Fig. 8. Diagrama demonstrando Adapter
com a complexidade de criação do objeto como mostra na
Figura 7:
B. Padrão Decorator
O Decorator é um padrão de design estrutural que per-
mite anexar novos comportamentos aos objetos, colocando-
os dentro de objetos especiais do invólucro que contém os
comportamentos. Os decoradores fornecem uma alternativa
flexível à subclasse para ampliar a funcionalidade.
Decorator x Adapter O adapter altera a interface de um
objeto existente, enquanto o Decorator aprimora um objeto
Fig. 7. Implementação do Builder em Python
sem alterar sua interface. Além disso, o Decorator suporta
composição recursiva, o que não é possível quando você usa
D. Injeção de Dependência o adaptador .
Em python a estrutura de injeção de dependência é algo que Em python O Decorator é bastante padrão no código Python,
o desenvolvedor do Python nunca precisaria, pois a injeção especialmente no código relacionado a fluxos. O decorator
de dependência pode ser implementada facilmente usando pode ser reconhecido por métodos de criação ou construtor
os fundamentos da linguagem. A própria linguagem oferece que aceitam objetos da mesma classe ou interface que uma
meios fáceis de fazer como o Typing falado acima e algumas classe atual.
bibliotecas como Dependency Injector A implementação final em python como mostra a Figura 9:
Porém essa biblioteca não é tão usada por ter uma imple-
mentação complicada e já um pouco obsoleta equiparando
com a própria maneira que o python “puro” faz a injeção
de dependência, bastando somente passar uma classe por
parâmetro como demonstrado na Figura 7 dessa forma funções
e metodos da classe Builder serão disponibilizados a classe
que injeta.
Fig. 9. Implementação do padrão Decorator em Python
V. PADRÕES E STRUTURAIS
A. Padrão Adapter
O padrão adapter é um padrão de projeto de software C. Padrão Facede
que permite a interface de uma classe existente ser utilizada O padrão de projeto Facade fornece uma interface simpli-
como uma outra interface. Este padrão permite embrulhar ficada (mas limitada) para um sistema complexo de classes,
algum objeto incompatível em um adaptador para tornar-lo biblioteca ou estrutura.
Embora o Facade diminua a complexidade geral do aplica- grupo de objetos devem ser tratados da mesma forma que uma
tivo, também ajuda a mover dependências indesejadas para um instância única de um objeto. O propósito do padrão composite
local. é compor objetos em estruturas de árvores que representam
hierarquias.
O Composite é bastante comum no código Python. É
frequentemente usado para representar hierarquias de compo-
nentes da interface do usuário ou o código que funciona com
gráficos. É fácil de reconhecer por métodos comportamentais,
levando uma instância do mesmo tipo abstrato / interface para
uma estrutura em árvore.
Fig. 12. Diagrama de Demonstração do Composite
Fig. 10. Diagrama demonstrando o padrão Facede F. Padrão Bridge
O padrão bridge preza pela composição em vez da herança.
O padrão Facade é comumente usado em aplicativos escritos O objetivo desse padrão é dissociar uma abstração de sua
em Python. É especialmente útil ao trabalhar com bibliotecas implementação para que os dois possam variar independen-
e APIs complexas como demonstrada na Figura 12 acima. temente.
A implementação têm a modificação somente da sintaxe da Em python é especialmente útil ao lidar com aplicativos
linguagem a estrutura e lógica mantém-se igual a do Java de plataforma cruzada, oferecer suporte a vários tipos de
D. Padrão Proxy servidores de banco de dados ou ao trabalhar com vários
provedores de API de um determinado tipo (por exemplo,
O Proxy serve de intermediário entre o solicitante (seeker) plataformas em nuvem, redes sociais etc.), como demonstra
e o provedor (provider). O solicitante é quem faz a requisição a Figura 13 abaixo :
e o provedor entrega os recursos em resposta à requisição.
É basicamente um wrapper ou um objeto agente que encap-
sula o objeto que está realmente servindo como demonstrado
na Figura 11.
Fig. 13. Diagrama de Demonstração do Padrão Bridge
Fig. 11. O proxy se disfarça como um objeto de banco de dados. Ele pode
lidar com inicialização lenta e cache de resultados sem o cliente ou o objeto G. Padrão Flyweight
de banco de dados real saber.
Permite que os programas suportem grandes quantidades de
Embora não seja frequentemente usado na maioria dos objetos, mantendo baixo o consumo de memória.
aplicativos Python, ainda é muito útil em alguns casos es- O padrão consegue isso compartilhando partes do estado do
peciais. É insubstituível quando você deseja adicionar alguns objeto entre vários objetos. Em outras palavras, o Flyweight
comportamentos adicionais a um objeto de alguma classe economiza RAM armazenando em cache os mesmos dados
existente sem alterar o código do cliente. usados por objetos diferentes.
Em python não há implementação fornecida pela linguagem
E. Padrão Composite para que demonstre o real funcionamento do padrão. Porém o
O padrão de projeto composite permite tratar objetos indi- diagrama mostrado na Figura 14 representa a forma como o
viduais de forma uniforme. Ele basicamente descreve que um Java implementa essa solução :
Fig. 14. Diagrama de Demonstração do Padrão Flyweight em Java
VI. C ONCLUSÃO
Ao longo desse artigo python demonstrou ser uma lin-
guagem de programação ótima para a aplicação dos padrões
de projeto por sua flexibilidade em comparação com outras
linguagens mais "rigidas", por conta que a maioria dos padrões
podem ser facilmente implementados com recursos da própria
linguagem. Os padrões de design tentam padronizar o vo-
cabulário de determinados designs, recorrentes em inúmeras
aplicações, como ja foi estudado a melhor forma de resolver
determinados problemas das aplicações foi com o uso dos
padrões de projeto que também servem como um "atalho" para
resolver esses problemas recorrentes.
R EFERENCES
[1] CPYTHON. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia
Foundation, 2019. Disponível em: <http://bit.ly/2klwZDi>. Acesso em:
14 jun. 2019.
[2] DESIGN PATTERNS. In: REFACTORING GURU. US: Refactoring
Guru, 2014-2019. Disponívl em: <http://bit.ly/2kINzgt>
[3] DESIGN PATTERNS. In: SOURCE MAKING. US: Source Making,
2019. Disponívl em: <http://bit.ly/2lOA9ja>
[4] DEPENDENCY INJECTOR. In: PYTHON.ORG, Python-Dependency-
Injector, 2017. Disponível em: <http://bit.ly/2lNDpeM/>
[5] PADRÃO DE PROJETO DE SOFTWARE. In: WIKIPÉDIA, a enci-
clopédia livre. Flórida: Wikimedia Foundation, 2019. Disponível em:
<http://bit.ly/2kM2Htx>.
[6] PYTHON. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia
Foundation, 2019. Disponível em: <http://bit.ly/2kkXNDE>.