[[ch_design_patterns]]
== Padrões de projetos com funções de primeira classe
:example-number: 0
:figure-number: 0

[quote, Ralph Johnson, co-autor do clássico "Padrões de Projetos"]
____
Conformidade a padrões não é uma medida de virtude.footnote:[De um slide na palestra "Root Cause Analysis of Some Faults in Design Patterns," (_Análise das Causas Básicas de Alguns Defeitos em Padrões de Projetos_), apresentada por Ralph Johnson no IME/CCSL da Universidade de São Paulo, em 15 de novembro de 2014.]
____

Em((("functions, design patterns with first-class", "dynamic languages and"))) engenharia de software, um
https://pt.wikipedia.org/wiki/Padr%C3%A3o_de_projeto_de_software[_padrão de projeto_] é uma receita genérica para solucionar um problema de design frequente.
Não é preciso conhecer padrões de projeto para acompanhar esse capítulo, vou explicar os padrões usados nos exemplos.

O uso de padrões de projeto em programação foi popularizado pelo livro seminal _Padrões de Projetos: Soluções Reutilizáveis de Software Orientados a Objetos_ (Addison-Wesley), de Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides—também conhecidos como "the Gang of Four" (_A Gangue dos Quatro_).
O livro é um catálogo de 23 padrões, cada um deles composto por arranjos de classes e exemplificados com código em C&#x2b;&#x2b;, mas assumidos como úteis também em outras linguagens orientadas a objetos.

Apesar dos padrões de projeto serem independentes da linguagem, isso não significa que todo padrão se aplica a todas as linguagens.
Por exemplo, o <<ch_generators>> vai mostrar que não faz sentido emular a receita do padrão https://fpy.li/10-2[Iterator (_Iterador_)] (EN) no Python, pois esse padrão está embutido na linguagem e pronto para ser usado, na forma de geradores—que não precisam de classes para funcionar, e exigem menos código que a receita clássica.

Os autores de _Padrões de Projetos_ reconhecem, na introdução, que a linguagem usada na implementação determina quais padrões são relevantes:

[quote]
____
A escolha da linguagem de programação é importante, pois ela influencia nosso ponto de vista. Nossos padrões supõe uma linguagem com recursos equivalentes aos de Smalltalk e do {cpp}—e essa escolha determina o que pode e o que não pode ser facilmente implementado.
Se tivéssemos presumido uma linguagem procedural, poderíamos ter incluído padrões de projetos chamados "Herança", "Encapsulamento" e "Polimorfismo".
Da mesma forma, alguns de nossos padrões são suportados diretamente por linguagens orientadas a objetos menos conhecidas. CLOS, por exemplo, tem multi-métodos, reduzindo a necessidade de um padrão como o Visitante.footnote:[_Visitor_, Citado da página 4 da edição em inglês de _Padrões de Projeto_.]
____

Em sua apresentação de 1996, https://fpy.li/norvigdp["Design Patterns in Dynamic Languages" (_Padrões de Projetos em Linguagens Dinâmicas_)] (EN), Peter Norvig afirma que 16 dos 23 padrões no _Padrões de Projeto_ original se tornam "invisíveis ou mais simples" em uma linguagem dinâmica (slide 9).
Ele está falando das linguagens Lisp e Dylan, mas muitos dos recursos dinâmicos relevantes também estão presentes no Python. Em especial, no contexto de linguagens com funções de primeira classe, Norvig sugere repensar os padrões clássicos conhecidos como Estratégia (_Strategy_), Comando (_Command_), Método Template (_Template Method_) e Visitante (_Visitor_).

O objetivo desse capítulo é mostrar como—em alguns casos—as funções podem realizar o mesmo trabalho das classes, com um código mais legível e mais conciso. Vamos refatorar uma implementaçao de Estratégia usando funções como objetos, removendo muito código redundante. Vamos também discutir uma abordagem similar para simplificar o padrão Comando.

=== Novidades neste capítulo

Movi((("functions, design patterns with first-class", "significant changes to"))) este capítulo para o final da Parte II, para poder então aplicar o decorador de registro na <<decorated_strategy>>, e também usar dicas de tipo nos exemplos.
A maior parte das dicas de tipo usadas nesse capítulo não são complicadas, e ajudam na legibilidade.

[[strategy_case_study]]
=== Estudo de caso: refatorando Estratégia

Estratégia((("functions, design patterns with first-class", "refactoring strategies", id="FDPrefactor10"))) é um bom exemplo de um padrão de projeto que pode ser mais simples em Python, usando funções como objetos de primeira classe. Na próxima seção vamos descrever e implementar Estratégia usando a estrutura "clássica" descrita em _Padrões de Projetos_. Se você estiver familiarizado com o padrão clássico, pode pular direto para <<pythonic_strategy>>, onde refatoramos o código usando funções, reduzindo significativamente o número de linhas.

==== Estratégia clássica

O((("refactoring strategies", "classic", id="RSclassic10")))((("classic refactoring strategy", id="classicref10")))((("Strategy pattern", id="stratpat10")))((("UML class diagrams", "Strategy design pattern"))) diagrama de classes UML na <<strategy_uml>> retrata um arranjo de classes exemplificando o padrão Estratégia.

[[strategy_uml]]
.Diagrama de classes UML para o processamento de descontos em um pedido, implementado com o padrão de projeto Estratégia.
image::../images/flpy_1001.png[Cálculos de desconto de um pedido como estratégias]

O padrão Estratégia é resumido assim em _Padrões de Projetos_:

[quote]
____
Define uma família de algoritmos, encapsula cada um deles, e os torna intercambiáveis. Estratégia permite que o algoritmo varie de forma independente dos clientes que o usam.
____

Um exemplo claro de Estratégia, aplicado ao domínio do ecommerce, é o cálculo de descontos em pedidos de acordo com os atributos do cliente ou pela inspeção dos itens do pedido.

Considere uma loja online com as seguintes regras para descontos:

* Clientes com 1.000 ou mais pontos de fidelidade recebem um desconto global de 5% por pedido.
* Um desconto de 10% é aplicado a cada item com 20 ou mais unidades no mesmo pedido.
* Pedidos com pelo menos 10 itens diferentes recebem um desconto global de 7%.

Para simplificar, vamos assumir que apenas um desconto pode ser aplicado a cada pedido.

O diagrama de classes UML para o padrão Estratégia aparece na <<strategy_uml>>. Seus participantes são:

Contexto (_Context_):: Oferece um serviço delegando parte do processamento para componentes intercambiáveis, que implementam algoritmos alternativos. No exemplo de ecommerce, o contexto é uma classe `Order`, configurada para aplicar um desconto promocional de acordo com um de vários algoritmos.

Estratégia (_Strategy_):: A interface comum dos componentes que implementam diferentes algoritmos. No nosso exemplo, esse papel cabe a uma classe abstrata chamada `Promotion`.

Estratégia concreta (_Concrete strategy_):: Cada uma das subclasses concretas de Estratégia. `FidelityPromo`, `BulkPromo`, e `LargeOrderPromo` são as três estratégias concretas implementadas.

O código no <<ex_classic_strategy>> segue o modelo da <<strategy_uml>>. Como descrito em _Padrões de Projetos_, a estratégia concreta é escolhida pelo cliente da classe de contexto. No nosso exemplo, antes de instanciar um pedido, o sistema deveria, de alguma forma, selecionar o estratégia de desconto promocional e passá-la para o construtor de `Order`. A seleção da estratégia está fora do escopo do padrão.

[[ex_classic_strategy]]
.Implementação da classe `Order` com estratégias de desconto intercambiáveis
====
[source, python]
----
include::../code/10-dp-1class-func/classic_strategy.py[tags=CLASSIC_STRATEGY]
----
====

Observe que no  <<ex_classic_strategy>>, programei `Promotion` como uma classe base abstrata (ABC), para usar o decorador `@abstractmethod` e deixar o padrão mais explícito.

O <<ex_classic_strategy_tests>> apresenta os doctests usados para demonstrar e verificar a operação de um módulo implementando as regras descritas anteriormente.

[[ex_classic_strategy_tests]]
.Amostra de uso da classe `Order` com a aplicação de diferentes promoções
====
[source, python]
----
include::../code/10-dp-1class-func/classic_strategy.py[tags=CLASSIC_STRATEGY_TESTS]
----
====
<1> Dois clientes: `joe` tem 0 pontos de fidelidade, `ann` tem 1.100.
<2> Um carrinho de compras com três itens.
<3> A promoção `FidelityPromo` não dá qualquer desconto para `joe`.
<4> `ann` recebe um desconto de 5% porque tem pelo menos 1.000 pontos.
<5> O `banana_cart` contém 30 unidade do produto `"banana"` e 10 maçãs.
<6> Graças à `BulkItemPromo`, `joe` recebe um desconto de $1,50 no preço das bananas.
<7> O `long_cart` tem 10 itens diferentes, cada um custando $1,00.
<8> `joe` recebe um desconto de 7% no pedido total, por causa da `LargerOrderPromo`.

////
PROD: I don't know why the console session in <<ex_classic_strategy_tests>> is not rendering with syntax coloring.
////

O <<ex_classic_strategy>> funciona perfeitamente bem, mas a mesma funcionalidade pode ser implementada com menos linhas de código em Python, se usarmos funções como objetos. Veremos como fazer isso na próxima seção.((("", startref="stratpat10")))((("", startref="classicref10")))((("", startref="RSclassic10")))

[[pythonic_strategy]]
==== Estratégia baseada em funções

Cada((("refactoring strategies", "function-oriented", id="RSfunction10")))((("function-oriented refactoring strategy", id="funcorient01"))) estratégia concreta no <<ex_classic_strategy>> é uma classe com um único método, `discount`.
Além disso, as instâncias de estratégia não tem nenhum estado (nenhum atributo de instância).
Você poderia dizer que elas se parecem muito com funções simples, e estaria certa.
O <<ex_strategy>> é uma refatoração do <<ex_classic_strategy>>,
substituindo as estratégias concretas por funções simples e removendo a classe abstrata `Promo`.
São necessários apenas alguns pequenos ajustes na classe `Order`.footnote:[Precisei reimplementar `Order` com `@dataclass` devido a um bug no Mypy. Você pode ignorar esse detalhe, pois essa classe funciona também com `NamedTuple`, exatamente como no <<ex_classic_strategy>>.
Quando `Order` é uma `NamedTuple`, o Mypy 0.910 encerra com erro ao verificar a dica de tipo para `promotion`.
Tentei acrescentar `# type ignore` àquela linha específica, mas o erro persistia. Entretanto, se `Order` for criada com `@dataclass`, o Mypy trata corretamente a mesma dica de tipo.
O https://fpy.li/10-3[Issue #9397] não havia sido resolvido em 19 de julho de 2021, quando essa nota foi escrita. Espero que o problema tenha sido solucionado quando você estiver lendo isso. NT: Aparentemente foi resolvido. O Issue #9397 gerou o https://github.com/python/mypy/issues/12629[Issue #12629], fechado com indicação de solucionado em agosto de 2022, o último comentário indicando que a opção de linha de comando `--enable-recursive-aliases` do Mypy evita os erros relatados).]

[[ex_strategy]]
.A classe `Order` com as estratégias de descontos implementadas como funções
====
[source, python]
----
include::../code/10-dp-1class-func/strategy.py[tags=STRATEGY]
----
====
<1> Essa dica de tipo diz: `promotion` pode ser `None`, ou pode ser um invocável que recebe uma `Order` como argumento e devolve um `Decimal`.
<2> Para calcular o desconto, chama o invocável `self.promotion`, passando `self` como um argumento. Veja a razão disso logo abaixo.
<3> Nenhuma classe abstrata.
<4> Cada estratégia é uma função.

.Por que self.promotion(self)?
[TIP]
====
Na classe `Order`, `promotion` não é um método. É um atributo de instância que por acaso é invocável. Então a primeira parte da expressão, `self.promotion`, busca aquele invocável. Mas, ao invocá-lo, precisamos fornecer uma instância de `Order`, que neste caso é `self`.
Por isso `self` aparece duas vezes na expressão.

A <<methods_are_descriptors_sec>> vai explicar o mecanismo que vincula automaticamente métodos a instâncias. Mas isso não se aplica a `promotion`, pois ela não é um método.
====

O código no <<ex_strategy>> é mais curto que o do <<ex_classic_strategy>>. Usar a nova `Order` é também um pouco mais simples, como mostram os doctests no <<ex_strategy_tests>>.

[[ex_strategy_tests]]
.Amostra do uso da classe `Order` com as promoções como funções
====
[source, python]
----
include::../code/10-dp-1class-func/strategy.py[tags=STRATEGY_TESTS]
----
====
<1> Mesmos dispositivos de teste do <<ex_classic_strategy>>.
<2> Para aplicar uma estratégia de desconto a uma `Order`, basta passar a função de promoção como argumento.
<3> Uma função de promoção diferente é usada aqui e no teste seguinte.

////
PROD: Again, I don't know why the console session in <<ex_strategy_tests>> is not rendering with syntax coloring.
////

Observe os textos explicativos do <<ex_strategy_tests>>&mdash;não há necessidade de instanciar um novo objeto `promotion` com cada novo pedido: as funções já estão disponíveis para serem usadas.

É interessante notar que no _Padrões de Projetos_, os autores sugerem que:
"Objetos Estratégia muitas vezes são bons "peso mosca" (_flyweight_)".footnote:[veja a página 323 da edição em inglês de _Padrões de Projetos_.]
Uma definição do padrão _Peso Mosca_ em outra parte daquele texto afirma:
"Um _peso mosca_ é um objeto compartilhado que pode ser usado em múltiplos contextos simultaneamente."footnote:[Ibid., p. 196.]
O compartilhamento é recomendado para reduzir o custo da criação de um novo objeto concreto de estratégia, quando a mesma estratégia é aplicada repetidamente a cada novo contexto—no nosso exemplo, a cada nova instância de `Order`.
Então, para contornar uma desvantagem do padrão Estratégia—seu custo durante a execução—os autores recomendam a aplicação de mais outro padrão.
Enquanto isso, o número de linhas e custo de manutenção de seu código vão se acumulando.

Um caso de uso mais espinhoso, com estratégias concretas complexas mantendo estados internos, pode exigir a combinação de todas as partes dos padrões de projeto Estratégia e Peso Mosca.
Muitas vezes, porém, estratégias concretas não têm estado interno; elas lidam apenas com dados vindos do contexto. Neste caso, não tenha dúvida, use as boas e velhas funções ao invés de escrever classes de um só metodo implementando uma interface de um só método declarada em outra classe diferente.
Uma função pesa menos que uma instância de uma classe definida pelo usuário, e não há necessidade do Peso Mosca, pois cada função da estratégia é criada apenas uma vez por processo Python, quando o módulo é carregado.
Uma função simples também é um "objeto compartilhado que pode ser usado em múltiplos contextos simultaneamente".

Uma vez implementado o padrão Estratégia com funções, outras possibilidades nos ocorrem. Suponha que você queira criar uma "meta-estratégia", que seleciona o melhor desconto disponível para uma dada `Order`.
Nas próximas seções vamos estudar as refatorações adicionais para implementar esse requisito, usando abordagens que se valem de funções e módulos vistos como objetos.((("", startref="RSfunction10")))((("", startref="funcorient01")))


==== Escolhendo a melhor estratégia: uma abordagem simples

Dados((("refactoring strategies", "choosing the best"))) os mesmos clientes e carrinhos de compras dos testes no <<ex_strategy_tests>>, vamos agora acrescentar três testes adicionais ao  <<ex_strategy_best_tests>>.

[[ex_strategy_best_tests]]
.A funcão `best_promo` aplica todos os descontos e devolve o maior
====
[source, python]
----
include::../code/10-dp-1class-func/strategy_best.py[tags=STRATEGY_BEST_TESTS]
----
====
<1> `best_promo` selecionou a `larger_order_promo` para o cliente `joe`.
<2> Aqui `joe` recebeu o desconto de `bulk_item_promo`, por comprar muitas bananas.
<3> Encerrando a compra com um carrinho simples, `best_promo` deu à cliente fiel `ann` o desconto da `fidelity_promo`.

A implementação de `best_promo` é muito simples. Veja o <<ex_strategy_best>>.

[[ex_strategy_best]]
.`best_promo` encontra o desconto máximo iterando sobre uma lista de funções
====
[source, python]
----
include::../code/10-dp-1class-func/strategy_best.py[tags=STRATEGY_BEST]
----
====
<1> `promos`: lista de estratégias implementadas como funções.
<2> `best_promo` recebe uma instância de `Order` como argumento, como as outras funções `*_promo`.
<3> Usando uma expressão geradora, aplicamos cada uma das funções de `promos` a `order`, e devolvemos o maior desconto encontrado.

O <<ex_strategy_best>> é bem direto: `promos` é uma `list` de funções. Depois que você se acostuma à ideia de funções como objetos de primeira classe, o próximo passo é notar que construir estruturas de dados contendo funções muitas vezes faz todo sentido.

Apesar do <<ex_strategy_best>> funcionar e ser fácil de ler, há alguma duplicação que poderia levar a um bug sutil: para adicionar uma nova estratégia, precisamos escrever a função e lembrar de incluí-la na lista `promos`. De outra forma a nova promoção só funcionará quando passada explicitamente como argumento para `Order`, e não será considerada por `best_promotion`.

Vamos examinar algumas soluções para essa questão.

==== Encontrando estratégias em um módulo

Módulos((("refactoring strategies", "finding strategies in modules", id="RSfind10"))) também são objetos de primeira classe no Python, e a biblioteca padrão oferece várias funções para lidar com eles. A((("functions", "globals() function")))((("globals() function"))) função embutida `globals` é descrita assim na documentação de Python:

`globals()`:: Devolve um dicionário representando a tabela de símbolos globais atual. Isso é sempre o dicionário do módulo atual (dentro de uma função ou método, esse é o módulo onde a função ou método foram definidos, não o módulo de onde são chamados).

O <<ex_strategy_best2>> é uma forma um tanto _hacker_ de usar `globals` para ajudar `best_promo` a encontrar automaticamente outras funções `*_promo` disponíveis.

[[ex_strategy_best2]]
.A lista `promos` é construída a partir da introspecção do espaço de nomes global do módulo
====
[source, python]
----
include::../code/10-dp-1class-func/strategy_best2.py[tags=STRATEGY_BEST2]
----
====
<1> Importa as funções de promoções, para que fiquem disponíveis no espaço de nomes global.footnote:[Tanto o flake8 quanto o VS Code reclamam que esses nomes são importados mas não são usados. Por definição, ferramentas de análise estática não conseguem entender a natureza dinâmica de Python. Se seguirmos todos os conselhos dessas ferramentas, logo estaremos escrevendo programas austeros e prolixos similares aos de Java, mas com a sintaxe de Python.]
<2> Itera sobre cada item no `dict` devolvido por `globals()`.
<3> Seleciona apenas aqueles valores onde o nome termina com o sufixo `_promo` e...
<4> ...filtra e remove a própria `best_promo`, para evitar uma recursão infinita quando `best_promo` for invocada.
<5> Nenhuma mudança em `best_promo`.

Outra forma de coletar as promoções disponíveis seria criar um módulo e colocar nele todas as funções de estratégia, exceto `best_promo`.

No <<ex_strategy_best3>>, a única mudança significativa é que a lista de funções de estratégia é criada pela introspecção de um módulo separado chamado `promotions`. Veja que o <<ex_strategy_best3>> depende da importação do módulo `promotions` bem como de `inspect`, que fornece funções de introspecção de alto nível.


[[ex_strategy_best3]]
.A lista `promos` é construída a partir da introspecção de um novo módulo, `promotions`
====
[source, python]
----
include::../code/10-dp-1class-func/strategy_best3.py[tags=STRATEGY_BEST3]
----
====

A função `inspect.getmembers` devolve os atributos de um objeto—neste caso, o módulo `promotions`—opcionalmente filtrados por um predicado (uma função booleana). Usamos
`inspect.isfunction` para obter apenas as funções do módulo.

O <<ex_strategy_best3>> funciona independente dos nomes dados às funções; tudo o que importa é que o módulo `promotions` contém apenas funções que, dado um pedido, calculam os descontos. Claro, isso é uma suposição implícita do código. Se alguém criasse uma função com uma assinatura diferente no módulo `promotions`, `best_promo` geraria um erro ao tentar aplicá-la a um pedido.

Poderíamos acrescentar testes mais estritos para filtrar as funções, por exemplo inspecionando seus argumentos. O ponto principal do <<ex_strategy_best3>> não é oferecer uma solução completa, mas enfatizar um uso possível da introspecção de módulo.

Uma alternativa mais explícita para coletar dinamicamente as funções de desconto promocional seria usar um decorador simples. É nosso próximo tópico.((("", startref="FDPrefactor10")))((("", startref="RSfind10")))


[[decorated_strategy]]
=== Padrão Estratégia aperfeiçoado com um decorador

Lembre-se((("functions, design patterns with first-class", "decorator-enhanced strategy pattern", id="FDPdecorator10")))((("refactoring strategies", "decorator-enhanced pattern", id="RSdecorator10")))((("decorator-enhanced strategy pattern", id="decenh10"))) que nossa principal objeção ao <<ex_strategy_best>> foi a repetição dos nomes das funções em suas definições e na lista `promos`, usada pela função `best_promo` para determinar o maior desconto aplicável. A repetição é problemática porque alguém pode acrescentar uma nova função de estratégia promocional e esquecer de adicioná-la manualmente à lista `promos`—caso em que `best_promo` vai silenciosamente ignorar a nova estratégia, introduzindo no sistema um bug sutil. O <<ex_strategy_best31>> resolve esse problema com a técnica vista na <<registration_deco_sec>>.

[[ex_strategy_best31]]
.A lista `promos` é preenchida pelo decorador `promotion`
====
[source, python]
----
include::../code/10-dp-1class-func/strategy_best4.py[tags=STRATEGY_BEST4]
----
====
<1> A lista `promos` é global no módulo, e começa vazia.
<2> `promotion` é um decorador de registro: ele devolve a função `promo` inalterada, após inserí-la na lista `promos`.
<3> Nenhuma mudança é necessária em `best_promo`, pois ela se baseia na lista `promos`.
<4> Qualquer função decorada com `@promotion` será adicionada a `promos`.

Essa solução tem várias vantagens sobre aquelas apresentadas anteriormente:

* As funções de estratégia de promoção não precisam usar nomes especiais—não há necessidade do sufixo `_promo`.
* O decorador `@promotion` realça o propósito da função decorada, e também torna mais fácil desabilitar temporariamente uma promoção: basta transformar a linha do decorador em comentário.
* Estratégias de desconto promocional podem ser definidas em outros módulos, em qualquer lugar do sistema, desde que o decorador `@promotion` seja aplicado a elas.

Na próxima seção vamos discutir Comando (_Command_)—outro padrão de projeto que é algumas vezes implementado via classes de um só metodo, quando funções simples seriam suficientes.((("", startref="decenh10")))((("", startref="RSdecorator10")))((("", startref="FDPdecorator10")))


=== O padrão Comando

Comando((("functions, design patterns with first-class", "Command pattern", id="FDPcommand10")))((("Command pattern", id="cmmd10")))((("refactoring strategies", "Command pattern", id="RScmmnd10")))((("UML class diagrams", "Command design pattern"))) é outro padrão de projeto que pode ser simplificado com o uso de funções passadas como argumentos. A <<command_uml>> mostra o arranjo das classes nesse padrão.

[[command_uml]]
.Diagrama de classes UML para um editor de texto controlado por menus, implementado com o padrão de projeto Comando. Cada comando pode ter um receptor (_receiver_) diferente: o objeto que implementa a ação. Para `PasteCommand`, o receptor é Document. Para `OpenCommand`, o receptor á a aplicação.
image::../images/flpy_1002.png[Aplicação do padrão Comando a um editor de texto]

O objetivo de Comando é desacoplar um objeto que invoca uma operação (o _invoker_ ou remetente) do objeto fornecedor que implementa aquela operação (o _receiver_ ou receptor). No exemplo em _Padrões de Projetos_, cada remetente é um item de menu em uma aplicação gráfica, e os receptors são o documento sendo editado ou a própria aplicação.

A ideia é colocar um objeto `Command` entre os dois, implementando uma interface com um único método, `execute`, que chama algum método no receptor para executar a operação desejada. Assim, o remetente não precisa conhecer a interface do receptor, e receptors diferentes podem ser adaptados com diferentes subclasses de `Command`. O remetente é configurado com um comando concreto, e o opera chamando seu método `execute`. Observe na <<command_uml>> que `MacroCommand` pode armazenar um sequência de comandos; seu método `execute()` chama o mesmo método em cada comando armazenado.

Citando _Padrões de Projetos_, "Comandos são um substituto orientado a objetos para _callbacks_." A pergunta é: precisamos de um substituto orientado a objetos para _callbacks_? Algumas vezes sim, mas nem sempre.

Em vez de dar ao remetente uma instância de `Command`, podemos simplesmente dar a ele uma função. Em vez de chamar `command.execute()`, o remetente pode apenas chamar `command()`. O `MacroCommand` pode ser programado como uma classe que implementa `+__call__+`. Instâncias de `MacroCommand` seriam invocáveis, cada uma mantendo uma lista de funções para invocação futura, como implementado no <<ex_macro_command>>.


[[ex_macro_command]]
.Cada instância de `MacroCommand` tem uma lista interna de comandos
====
[source, python]
----
class MacroCommand:
    """A command that executes a list of commands"""

    def __init__(self, commands):
        self.commands = list(commands)  # <1>

    def __call__(self):
        for command in self.commands:  # <2>
            command()
----
====
<1> Criar uma nova lista com os itens do argumento `commands` garante que ela seja iterável e mantém uma cópia local de referências a comandos em cada instância de `MacroCommand`.
<2> Quando uma instância de `MacroCommand` é invocada, cada comando em `self.commands` é chamado em sequência.

Usos mais avançados do padrão Comando—para implementar "desfazer", por exemplo—podem exigir mais que uma simples função de _callback_. Mesmo assim, Python oferece algumas alternativas que merecem ser consideradas:

* Uma instância invocável como `MacroCommand` no <<ex_macro_command>> pode manter qualquer estado que seja necessário, e oferecer outros métodos além de `+__call__+`.

* Uma clausura pode ser usada para manter o estado interno de uma função entre invocações.

Isso encerra nossa revisão do padrão Comando usando funções de primeira classe.
Por alto, a abordagem aqui foi similar à que aplicamos a Estratégia:
substituir as instâncias de uma classe participante que implementava uma interface de método único por invocáveis.
Afinal, todo invocável de Python implementa uma interface de método único, e esse método se chama
`+__call__+`.((("", startref="RScmmnd10")))((("", startref="cmmd10")))((("", startref="FDPcommand10")))


[[design_patterns_summary]]
=== Resumo do Capítulo

Como((("functions, design patterns with first-class", "overview of"))) apontou Peter Norvig alguns anos após o surgimento do clássico _Padrões de Projetos_, "16 dos 23 padrões têm implementações qualitativamente mais simples em Lisp ou Dylan que em {cpp}, pelo menos para alguns usos de cada padrão" (slide 9 da apresentação de Norvig, https://fpy.li/10-4["Design Patterns in Dynamic Languages" presentation] (_Padrões de Projetos em Linguagens Dinâmicas_)). Python compartilha alguns dos recursos dinâmicos das linguagens Lisp e Dylan, especialmente funções de primeira classe, nosso foco nesse capítulo.

Na mesma palestra citada no início deste capítulo, refletindo sobre o 20º aniversário de _Padrões de Projetos: Soluções Reutilizáveis de Software Orientados a Objetos_, Ralph Johnson afirmou que um dos defeitos do livro é: "Excesso de ênfase nos padrões como linhas de chegada, em vez de como etapas em um processo de design".footnote:["Root Cause Analysis of Some Faults in Design Patterns" (_Análise das Causas Básicas de Alguns Defeitos em Padrões de Projetos_), palestra apresentada por Johnson no IME/CCSL da Universidade de São Paulo, em 15 de novembro de 2014.] Neste capítulo usamos o padrão Estratégia como ponto de partida: uma solução que funcionava, mas que simplificamos usando funções de primeira classe.

Em muitos casos, funções ou objetos invocáveis oferecem um caminho mais natural para implementar _callbacks_ em Python que a imitação dos padrões Estratégia ou Comando como descritos por Gamma, Helm, Johnson, e Vlissides em _Padrões de Projetos_. A refatoração de Estratégia e a discussão de Comando nesse capítulo são exemplos de uma ideia mais geral: algumas vezes você pode encontrar uma padrão de projeto ou uma API que exigem que seus componentes implementem uma interface com um único método, e aquele método tem um nome que soa muito genérico, como "executar", "rodar" ou "fazer". Tais padrões ou APIs podem frequentemente ser implementados em Python com menos código repetitivo, usando funções como objetos de primeira classe.

[[dp_further]]
=== Para saber mais

A "Receita 8.21. Implementando o Padrão Visitante" (_Receipt 8.21. Implementing the Visitor Pattern_) no((("functions, design patterns with first-class", "further reading on"))) pass:[<a class="orm:hideurl" href="https://fpy.li/pycook3"><em>Python Cookbook</em>, 3ª ed.</a>] (EN), mostra uma implementação elegante do padrão Visitante, na qual uma classe `NodeVisitor` trata métodos como objetos de primeira classe.

Sobre o tópico mais geral de padrões de projetos, a oferta de leituras para o programador Python não é tão numerosa quando aquela disponível para as comunidades de outras linguagens.

_Learning Python Design Patterns_ ("Aprendendo os Padrões de Projeto de Python"), de Gennadiy Zlobin (Packt), é o único livro inteiramente dedicado a padrões em Python que encontrei. Mas o trabalho de Zlobin é muito breve (100 páginas) e trata de apenas 8 dos 23 padrões de projeto originais.

_Expert Python Programming_ ("Programação Avançada em Python"), de Tarek Ziadé (Packt), é um dos melhores livros de Python de nível intermediário, e seu capítulo final, "Useful Design Patterns" (_Padrões de Projetos Úteis_), apresenta vários dos padrões clássicos de uma perspectiva pythônica.

Alex Martelli já apresentou várias palestras sobre padrões de projetos em Python. Há um vídeo de sua https://fpy.li/10-5[apresentação na EuroPython] (EN) e um https://fpy.li/10-6[conjunto de slides em seu site pessoal] (EN). Ao longo dos anos, encontrei diferentes jogos de slides e vídeos de diferentes tamanhos, então vale a pena tentar uma busca mais ampla com o nome dele e as palavras "Python Design Patterns". Um editor me contou que Martelli está trabalhando em um livro sobre esse assunto. Eu certamente comprarei meu exemplar assim que estiver disponível.

Há muitos livros sobre padrões de projetos no contexto de Java mas, dentre todos eles, meu preferido é _Head First Design Patterns_ ("Mergulhando de Cabeça nos Padrões de Projetos"), 2ª ed., de Eric Freeman e Elisabeth Robson (O'Reilly). Esse volume explica 16 dos 23 padrões clássicos. Se você gosta do estilo amalucado da série _Head First_ e precisa de uma introdução a esse tópico, vai adorar esse livro. Ele é centrado em Java, mas a segunda edição foi atualizada para refletir a introdução de funções de primeira classe naquela linguagem, tornando alguns dos exemplos mais próximos de código que escreveríamos em Python.

Para um olhar moderno sobre padrões, do ponto de vista de uma linguagem dinâmica com _duck typing_ e funções de primeira classe, _Design Patterns in Ruby_ ("Padrões de Projetos em Ruby") de Russ Olsen (Addison-Wesley) traz muitas ideias aplicáveis também ao Python. A despeito de suas muitas diferenças sintáticas, no nível semântico Python e Ruby estão mais próximos entre si que de Java ou do {cpp}.


Em https://fpy.li/norvigdp["Design Patterns in Dynamic Languages" (_Padrões de Projetos em Linguagens Dinâmicas_)] (slides), Peter Norvig mostra como funções de primeira classe (e outros recursos dinâmicos) tornam vários dos padrões de projeto originais mais simples ou mesmo desnecessários.

A "Introdução" do _Padrões de Projetos_ original, de Gamma et al. já vale o preço do livro—mais até que o catálogo de 23 padrões, que inclui desde receitas muito importantes até algumas raramente úteis. Alguns princípios de projetos de software muito citados, como "Programe para uma interface, não para uma implementação" e "Prefira a composição de objetos à herança de classe", vem ambos daquela introdução.

A aplicação de padrões a projetos se originou com o arquiteto Christopher Alexander et al., e foi apresentada  no livro _A Pattern Language_ ("Uma Linguagem de Padrões") (Oxford University Press).
A ideia de Alexander é criar um vocabulário padronizado, permitindo que equipes compartilhem decisões comuns em projetos de edificações.
M. J. Dominus wrote https://fpy.li/10-7[“‘Design Patterns’ Aren't” (_Padrões de Projetos Não São_)],
uma curiosa apresentação de slides acompanhada de um texto, argumentando que a visão original de Alexander sobre os padrões é mais profunda e mais humanista e também aplicável à engenharia de software.

.Ponto de vista
****
Python((("functions, design patterns with first-class", "Soapbox discussion")))((("Soapbox sidebars", "design patterns"))) tem funções de primeira classe e tipos de primeira classe, e Norvig afima que esses recursos afetam 10 dos 23 padrões (no slide 10 de https://fpy.li/norvigdp["Design Patterns in Dynamic Languages" (_Padrões de Projetos em Linguagens Dinâmicas_)]). No <<ch_closure_decorator>>, vimos que Python também tem funções genéricas (na <<generic_functions>>), uma forma limitada dos multi-métodos do CLOS, que Gamma et al. sugerem como uma maneira mais simples de implementar o padrão clássico Visitante (_Visitor_). Norvig, por outro lado, diz (no slide 10) que os multi-métodos simplificam o padrão Construtor (_Builder_). Ligar padrões de projetos a recursos de linguagens não é uma ciência exata.

Em cursos a redor do mundo todo, padrões de projetos são frequentemente ensinados usando exemplos em Java. Ouvi mais de um estudante dizer que eles foram levados a crer que os padrões de projeto originais são úteis qualquer que seja a linguagem usada na implementação. A verdade é que os 23 padrões "clássicos" de _Padrões de Projetos_ se aplicam muito bem ao Java, apesar de terem sido apresentados principalmente no contexto do {cpp}—no livro, alguns deles têm exemplos em Smalltalk. Mas isso não significa que todos aqueles padrões podem ser aplicados de forma igualmente satisfatória a qualquer linguagem. Os autores dizem explicitamente, logo no início de seu livro, que "alguns de nossos padrões são suportados diretamente por linguagens orientadas a objetos menos conhecidas" (a citação completa apareceu na primeira página deste capítulo).

A bibliografia de Python sobre padrões de projetos é muito pequena, se comparada à existente para Java, {cpp} ou Ruby. Na <<dp_further>>, mencionei _Learning Python Design Patterns_ ("Aprendendo Padrões de Projeto de Python"), de Gennadiy Zlobin, que foi publicado apenas em novembro de 2013. Para se ter uma ideia, _Design Patterns in Ruby_ ("Padrões de Projetos em Ruby"), de Russ Olsen, foi publicado em 2007 e tem 384 páginas—284 a mais que a obra de Zlobin.

Agora que Python está se tornando cada vez mais popular no ambiente acadêmico, podemos esperar que novos livros sobre padrões de projetos sejam escritos no contexto de nossa linguagem. Além disso, o Java 8 introduziu referências a métodos e funções anônimas, e esses recursos muito esperados devem incentivar o surgimento de novas abordagens aos padrões em Java—reconhecendo que, à medida que as linguagens evoluem, nosso entendimento sobre a forma de aplicação dos padrões de projetos clássicos deve também evoluir.

[role="soapbox-title"]
O __call__ selvagem

Enquanto((("Soapbox sidebars", "&#x005F;&#x005F;call&#x005F;&#x005F;", secondary-sortas="call")))((("&#x005F;&#x005F;call&#x005F;&#x005F;"))) trabalhávamos juntos para dar os toques finais a este livro, o revisor técnico Leonardo Rochael pensou:

Se funções têm um método `+__call__+`, e métodos também são invocáveis, será que os métodos
`+__call__+` também tem um método `+__call__+`?

Não sei se a descoberta dele tem alguma utilidade, mas eis um fato engraçado:

[source, python]
----
>>> def turtle():
...     return 'eggs'
...
>>> turtle()
'eggs'
>>> turtle.__call__()
'eggs'
>>> turtle.__call__.__call__()
'eggs'
>>> turtle.__call__.__call__.__call__()
'eggs'
>>> turtle.__call__.__call__.__call__.__call__()
'eggs'
>>> turtle.__call__.__call__.__call__.__call__.__call__()
'eggs'
>>> turtle.__call__.__call__.__call__.__call__.__call__.__call__()
'eggs'
>>> turtle.__call__.__call__.__call__.__call__.__call__.__call__.__call__()
'eggs'
----

https://fpy.li/10-8[Turtles all the way down ]footnote:[NT: Literalmente "_Tartarugas até embaixo_" ou algo como "_Tartarugas até onde a vista alcança_" ou "_Uma torre infinita de tartarugas_". Curiosamente, um livro com esse nome foi publicado no Brasil com o título "Mil vezes adeus", na tradição brasileira (especialmente para filmes) de traduzir nomes de obras de forma preguiçosa ou aleatória.]


****

