Developer’s Day 2007 Vídeo no YouTube: http://bit.ly/okP7hh Traduzida para a PythonBrasil 7 por Luciano Ramalho com autorização de Alex Martelli gentilmente cedida em 23/08/2011
programadores do código cliente e também do sub-sistema (complexidade + rigidez) • Forças: um sub-sistema rico e complexo oferece muita funcionalidade útil; componentes clientes interagem com várias partes dessa funcionali- dade, de um jeito que está “fora de controle”
flexibilidade, os clientes ganham simplicidade • interpor um objeto/classe de “fachada”, expondo um sub-conjunto controlado da funcionalidade • clientes acessam somente a fachada • a fachada implementa a funcionalidade simples invocando o sub-sistema complexo Django: classe do domínio versus um monte de classes de modelo, para facilitar a api exposta às views
problema comum de projeto + estrutura de uma solução (+ vantagens e desvantagens, alternativas, ...) e: • um nome (mais fácil de lembrar/discutir) • “descrições de objetos e classes que se comunicam, customizados para resolver um problema geral de projeto em um contexto específico” (GOF) • não é: estrutura de dados, algoritmo, arquitetura específica de domínio, recurso de uma linguagem ou de uma biblioteca • tem que ser estudado no contexto de uma linguagem! • tem que fornecer Usos Conhecidos (UC / KU) Façade é uma exceção PP não são inventados, são descobertos por arqueólogos
• dbhash é uma fachada para o bsddb • acesso muito simplificado a uma parte da API • também atende à interface dbm (portanto, é também um exemplo do PP Adapter) • os.path: basename, dirname são fachada para split + acesso a item; isdir (etc.) fachada para os.stat + stat.S_ISDIR (etc.) • Façade é um PP estrutural (veremos outro, Adapter, depois; no exemplo do dbhash eles se fundem)
um problema comum de projeto + estrutura de uma solução (+ vantagens e desvantagens, alternativas, ...) e: • um nome (mais fácil de lembrar/discutir) • “descrições de objetos e classes que se comunicam customizadas para resolver um problema geral de projeto em um contexto específico” (GOF) • um PP não é: estrutura de dados, algoritmo, arquitetura específica de domínio, recurso de uma linguagem ou de uma biblioteca • tem que ser estudado no contexto de uma linguagem! • tem que fornecer Usos Conhecidos (UC / KU)
instanciar objetos • Estruturais: composição mútua de classes ou objetos (o Façade é um PP estrutural) • Comportamental: como classes e objetos interagem e dividem responsabilidades entre si • Cada um pode ser a nível de classe ou a nível de objeto
para uma implementação” (GOF) • isto é feito principalmente através de “tipagem pato” em Python – raramente com interfaces “formais” • semelhante a “polimorfismo baseado em assinatura” nas templates de C++
que herança de classes” (GOF) • em Python, segurar ou embrulhar • herdar apenas quando é realmente conveniente • expor todos os métodos da super classe (reusar + normalmente sobrescrever + talvez estender) • mas é um acoplamento muito forte!
sub-objeto S como um atributo (talvez uma propriedade) – só isso • usa-se self.S.method ou O.S.method • simples, direto, imediato, porém... acoplamento bem forte, frequentemente no eixo errado
nome privado) e delegar (de modo que se possa acessar diretamente O.metodo) • delegação explícita (def metodo(self...): self.S.metodo) • automática (via __getattr__) • acoplamento correto (Lei de Deméter) Evitar expressões do tipo a.b.c. porque isso limita a a expor um b que tem um
self._embrulhado = embrulhado self._bloqueios = bloqueios def __getattr__(self, nome): if nome in self._bloqueios: raise AttributeError, nome return getattr(self._embrulhado, nome) ... Herança não tem o poder de restringir!
operador new, a chamada new Foo() implica que Foo é uma classe concreta específica. Isso aumenta o acoplamento. • Python é mais flexível, porque Foo() pode ser a invocação de uma classe ou de uma função factory. O cliente não precisa saber.
uma classe • não pode ter subclasses, métodos especiais... • crie somente uma instância (sem restrição) • necessário definir exatamente quando criar • singleton (“Highlander”) • criar sub-classes não é tão tranquilo • monoestado (“Borg”) • Guido não curte “Queremos que apenas uma instância possa existir” Resolve 90% dos casos
hasattr(cls, '_inst'): cls._inst = super(Singleton, cls).__new__(cls, *a, **k) return cls._inst Subclasses são problemáticas, porém: class Foo(Singleton): pass class Bar(Foo): pass f = Foo(); b = Bar(); # ...???... problema intrínseco do Singleton
uma classe concreta específica” • injeção de dependência • nada se cria “dentro”, tudo vem “de fora” • e se múltiplas criações são necessárias? • subcategoria “Factory” de PPs • pode criar qq. coisa ou reusar o q. já existe • funções factory (e outros invocáveis) • métodos factory (podem ser sobrescritos) • classes factory abstratas
classe e de objetos) • Facade: simplificar a interface de um sub-sistema • ... e muitos outros que não vou abordar, como: • Brige: muitas implementações de uma abstração, muitas implementações de uma funcionalidade, sem codificação repetitiva • Decorator: reusar+ajustar sem herança • Proxy: desacoplar acesso/localização Subcategoria “Disfarçe/Adaptação:
σ oferece um protocolo diferente S (com um superconjunto da funcionalide de C) • código-adaptador α “se infiltra entre eles”: • para γ, α é um fornecedor (produz o protocolo C) • para σ, α é um cliente (consome o protocolo S) • “dentro”, α implementa C (por meio de chamadas apropriadas a S em σ)
like” (com muito código para gerenciar o buffer) • doctest.DocTestSuite: adapta testes doctest para unittest.TestSuite • dbhash: adapta bsddb para dbm • StringIO: adapta str ou unicode para “file-like” • shelve: adapta “dict limitado” (chaves e valores str, métodos básicos) para um dicionário completo • via pickle para qualquer coisa <-> string • + UserDict.DictMixin
código • classes mixin são uma ótima maneira de auxiliar na adaptação de protocolos ricos (implementam métodos avançados a partir de métodos fundamentais) • Adapter é encontrado em todos os níveis de complexidade • Em Python, _não_ se trata sempre de classes e suas instâncias (de modo algum!) -- muitas vezes _invocáveis_ são adaptados (via decorators e outras funções de ordem superior, closures, functools, ...)
protocolo exigido por código-cliente • ou, conquistar polimorfismo via homogeneidade • Facade trata de simplificar uma interface rica quando apenas um sub-conjunto é necessário • Facade frequentemente mascara um subsistema formado por muitas classes/ objetos, Adapter mascara apenas um objeto ou classe
um termo muito sobrecarregado • programação genérica em C++ • geração de documento a partir de esqueleto • ... • um nome melhor: self-delegation • auto-delegação • diretamente descritivo!
invoca “métodos gancho” • na ABC, métodos-gancho são abstratos • sub-classes concretas implementam os ganchos • código-cliente invoca o método organizador • em alguma referência à ABC (injetor, ou...) • que obviamente refere-se a uma sub-classe concreta
s = self.precmd(s) finis = self.docmd(s) finis = self.postcmd(finis,s) if finis: break self.postloop() Na implementação em Python, os métodos-gancho são implementados com pass na classe abstrata, porque Python é uma linguagem pragática e não ideológica.
estrutural” (sequenciamento etc.) • os “métodos gancho” realizam de fato as “ações elementares” • é frequentemente apropriado para fatorar comportamento comuns e variações • deixa claras as responsabilidades e colaborações entre objetos (classes): classe base invoca ganchos, sub-classes os implementam • aplica o “Hollywood Principle”: “don't call us, we'll call you” - não ligue para nós, nós te ligamos Diferença entre um framework e uma bibiblioteca: uma biblioteca é um conjunto de funções que vc chama, a organização fica por sua conta. Um framework tem os métodos de organização e que chamam os seus métodos.
quando fazem sentido, mas “obrigatórias” servem como boa documentação class TheBase(object): def doThis(self): # provide a default (often a no-op) pass def doThat(self): # or, force subclass to implement # (might also just be missing...) raise NotImplementedError
implementa todos os métodos ganchos • sub-classes podem customizar o comportamento • sem se preocupar com travas, timing... • comportamento default é FIFO, simples e útil • with no worry about locking, timing, ... • pode-se sobrescrever métodos gancho (__init__, qsize, _empty, _full, _put, _get) E... • ...dados (maxsize, queue), uma exclusividade Python!
“métodos gancho” em outra classe • UC: HTML formatter vs writer • UC: SAX parser vs handler • adiciona um eixo de variabilidade/flexibilidade • aproxima-se do PP Strategy: • Strategy: uma classe abstrata por ponto de decisão, e classes concretas indepententes • TM fatorado: classes abstratas e concretas mais “agrupadas”
(talvez descendente) em tempo de execução • descobrir que métodos existem • despachar apropriadamente (incluindo “catch-all” e/ou outros tratamento de erros)