├── .github └── workflows │ └── action.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── comportamentais ├── chain_of_responsibility │ ├── README.md │ ├── descontos.py │ ├── main.py │ └── orcamento.py ├── command │ ├── README.md │ └── main.py ├── interpreter │ ├── README.md │ └── main.py ├── iterator │ ├── README.md │ └── main.py ├── mediator │ ├── README.md │ └── main.py ├── memento │ ├── README.md │ └── main.py ├── observer │ ├── README.md │ ├── main.py │ └── observadores.py ├── state │ ├── README.md │ └── main.py ├── strategy │ ├── README.md │ ├── impostos.py │ ├── main.py │ └── orcamento.py ├── template_method │ ├── README.md │ ├── impostos.py │ ├── main.py │ └── orcamento.py └── visitor │ ├── README.md │ ├── impressao.py │ └── main.py ├── criacao ├── abstract_factory │ ├── README.md │ └── main.py ├── builder │ ├── README.md │ ├── criador_de_nota_fiscal.py │ └── main.py ├── factory_method │ ├── README.md │ └── main.py ├── monostate │ ├── README.md │ └── main.py ├── prototype │ ├── README.md │ └── main.py └── singleton │ ├── README.md │ └── main.py ├── estruturais ├── adapter │ ├── README.md │ └── main.py ├── bridge │ ├── README.md │ └── main.py ├── composite │ ├── README.md │ └── main.py ├── decorator │ ├── README.md │ ├── impostos.py │ ├── main.py │ └── orcamento.py ├── facade │ ├── README.md │ └── main.py ├── flyweight │ ├── README.md │ └── main.py └── proxy │ ├── README.md │ └── main.py └── requirements-dev.txt /.github/workflows/action.yml: -------------------------------------------------------------------------------- 1 | name: Validate 2 | 3 | on: push 4 | 5 | jobs: 6 | check-links: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@main 10 | - uses: gaurav-nelson/github-action-markdown-link-check@v1 11 | with: 12 | use-quiet-mode: 'yes' 13 | check-code: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: actions/setup-python@v2 18 | with: 19 | python-version: '3.8' 20 | - run: | 21 | python -m pip install --upgrade pip 22 | pip install -r requirements-dev.txt 23 | - run: make codecheck 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.idea/ 2 | *.pyc 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribuindo 2 | 3 | Você pode contribuir com esse projeto de 3 formas: criando issues, abrindo Pull Requests (PR) ou revisando Pull Requests. 4 | 5 | ## Criando Issues 6 | 7 | Se você encontrar qualquer problema, tiver alguma sugestão ou gostaria que fosse incluído algum padrão novo, fique à vontade para criar uma nova issue. 8 | 9 | Antes de criar uma nova issue verifique se já existe outra issue semelhante e lembre-se de detalhar o problema encontrado ou a sugestão proposta. 10 | 11 | ## Abrindo Pull Requests 12 | 13 | Também é possível contribuir abrindo um Pull Request com a solução para algum problema ou a inclusão de um novo padrão de projeto. 14 | 15 | Ao abrir um PR lembre-se de incluir uma descrição relevante sobre as alterações realizadas e, em caso de inclusão de novos padrões, adicionar um link no README do projeto. 16 | 17 | ## Revisando Pull Requests 18 | 19 | Outra forma de contribuir com o projeto é revisando PRs abertos. Se você tem domínio sobre algum padrão de projeto, fique à vontade para auxiliar na revisão dos PRs abertos. 20 | 21 | No momento da revisão lembre-se de ser cordial/educado e, se possível, teste manualmente a alteração ou correção antes de aprovar o PR. 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | codecheck: ## Run code check (flake8, black and isort) 3 | @flake8 . 4 | @black . -l 79 --check --skip-string-normalization 5 | @isort . -rc -l 79 -m 3 -tc --check-only 6 | @unify --check-only --recursive ./* 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :computer: Padrões de Projeto em Python 2 | 3 | Material de estudo sobre padrões de projeto em Python com código, descrição e em pt-br :brazil: 4 | 5 | | | Padrões Comportamentais | Se concentram nos algoritmos e atribuições de responsabilidades | 6 | |-----------------------|-------------------------------|--------------------------------------------------------------------------| 7 | | [:link:][1] | [Chain of Responsibility][25] | Nos permite aplicar uma lógica sequencial de forma dinâmica | 8 | | [:cop:][2] | [Command][26] | Executa uma sequência de comandos em cima de algum dado | 9 | | [:speech_balloon:][3] | [Interpreter][27] | Quando precisamos interpretar diversas operações | 10 | | [:loop:][4] | [Iterator][28] | Uma maneira de acessar elementos de um objeto sem expor o conteúdo | 11 | | [:alien:][5] | [Mediator][29] | Encapsula a lógica de comunicação entre um conjunto de objetos | 12 | | [:floppy_disk:][6] | [Memento][30] | Guardar um estado que possa ser restaurado futuramente | 13 | | [:sunglasses:][7] | [Observer][31] | Criar uma lista de observadores interessados pela criação de um objeto | 14 | | [:anger:][8] | [State][32] | Define um conjunto de estados que possui uma ordem definida | 15 | | [:bulb:][9] | [Strategy][33] | Passa como parâmetro uma função (estratégia) para outro método | 16 | | [:ledger:][10] | [Template Method][34] | Classes ABC que abstraem métodos em comum entre diversas classes | 17 | | [:runner:][11] | [Visitor][35] | Permite navegar pelos elementos de uma estrutura de dados | 18 | 19 | | | Padrões de Criação | São aqueles que abstraem ou adiam o processo de criação dos objetos | 20 | |-----------------------------|------------------------|-----------------------------------------------------------------------------------| 21 | | [:hammer:][12] | [Abstract Factory][36] | Cria um ou mais métodos de fábrica para criar uma família de objetos | 22 | | [:construction_worker:][13] | [Builder][37] | Recebe parâmetros, verifica a validade e até definir parâmetros padrões | 23 | | [:factory:][14] | [Factory Method][38] | Permite expor métodos ao cliente para criar novos objetos | 24 | | :one: | [Monostate (Borg)][39] | Cria mais de uma instância da classe mas todos os objetos tem o mesmo estado | 25 | | [:sheep:][16] | [Prototype][40] | O padrão prototype é um padrão utilizado basicamente para clonar objetos | 26 | | [:gem:][17] | [Singleton][41] | Garante que apenas um objeto de uma determinada classe seja criado | 27 | 28 | | | Padrões Estruturais | Se preocupam com a forma como classes e objetos são compostos | 29 | |------------------------|---------------------|--------------------------------------------------------------------------------| 30 | | [:electric_plug:][18] | [Adapter][42] | Embrulha um objeto para torná-lo compatível com outras classes | 31 | | [:aerial_tramway:][19] | [Bridge][43] | Dissocia uma abstração de sua implementação para que possam variar | 32 | | [:herb:][20] | [Composite][44] | Permite tratar objetos individuais de forma uniforme | 33 | | [:art:][21] | [Decorator][45] | Permite compor/decorar os parâmetros de forma dinâmica | 34 | | [:package:][22] | [Facade][46] | Promove o desacoplamento da implementação com vários clientes | 35 | | [:leaves:][23] | [Flyweight][47] | Minimiza o uso de custos computacionais compartilhando dados entre objetos | 36 | | [:8ball:][24] | [Proxy][48] | Um objeto agente que encapsula o objeto que está realmente servindo | 37 | 38 | ## :dancers: Contribuindo 39 | 40 | Se você tem interesse em contribuir com o projeto :heart_eyes: por favor leia o documento [CONTRIBUTING](https://github.com/kelvins/design-patterns-python/blob/master/CONTRIBUTING.md). 41 | 42 | ## :book: Referências 43 | 44 | - [Curso Design Patterns Python I: Boas práticas de programação. Alura Online.](https://cursos.alura.com.br/course/design-patterns-python) 45 | - [Curso Design Patterns Python II: Boas práticas de programação. Alura Online.](https://cursos.alura.com.br/course/design-patterns-python-2) 46 | - [Aprendendo Padrões de Projeto em Python. Chetan Giridhar. Novatec.](https://novatec.com.br/livros/padroes-projeto-python/) 47 | - [Design Patterns for Humans](https://github.com/kamranahmedse/design-patterns-for-humans) 48 | - [Design Patterns: Refactoring Guru](https://refactoring.guru/design-patterns/python) 49 | - [Padrões de Projeto de Software](https://pt.wikipedia.org/wiki/Padr%C3%A3o_de_projeto_de_software) 50 | 51 | [1]: https://pt.wikipedia.org/wiki/Chain_of_Responsibility 52 | [2]: https://pt.wikipedia.org/wiki/Command 53 | [3]: https://pt.wikipedia.org/wiki/Interpreter 54 | [4]: https://pt.wikipedia.org/wiki/Iterador 55 | [5]: https://pt.wikipedia.org/wiki/Mediator 56 | [6]: https://pt.wikipedia.org/wiki/Memento_(inform%C3%A1tica) 57 | [7]: https://pt.wikipedia.org/wiki/Observer 58 | [8]: https://pt.wikipedia.org/wiki/State 59 | [9]: https://pt.wikipedia.org/wiki/Strategy 60 | [10]: https://pt.wikipedia.org/wiki/Template_Method 61 | [11]: https://pt.wikipedia.org/wiki/Visitor_Pattern 62 | [12]: https://pt.wikipedia.org/wiki/Abstract_Factory 63 | [13]: https://pt.wikipedia.org/wiki/Builder 64 | [14]: https://pt.wikipedia.org/wiki/Factory_Method 65 | [16]: https://pt.wikipedia.org/wiki/Prototype 66 | [17]: https://pt.wikipedia.org/wiki/Singleton 67 | [18]: https://pt.wikipedia.org/wiki/Adapter 68 | [19]: https://pt.wikipedia.org/wiki/Bridge_(padr%C3%A3o_de_projeto_de_software) 69 | [20]: https://pt.wikipedia.org/wiki/Composite 70 | [21]: https://pt.wikipedia.org/wiki/Decorator 71 | [22]: https://pt.wikipedia.org/wiki/Fa%C3%A7ade 72 | [23]: https://pt.wikipedia.org/wiki/Flyweight 73 | [24]: https://pt.wikipedia.org/wiki/Proxy_(padr%C3%B5es_de_projeto) 74 | 75 | [25]: comportamentais/chain_of_responsibility 76 | [26]: comportamentais/command 77 | [27]: comportamentais/interpreter 78 | [28]: comportamentais/iterator 79 | [29]: comportamentais/mediator 80 | [30]: comportamentais/memento 81 | [31]: comportamentais/observer 82 | [32]: comportamentais/state 83 | [33]: comportamentais/strategy 84 | [34]: comportamentais/template_method 85 | [35]: comportamentais/visitor 86 | [36]: criacao/abstract_factory 87 | [37]: criacao/builder 88 | [38]: criacao/factory_method 89 | [39]: criacao/monostate 90 | [40]: criacao/prototype 91 | [41]: criacao/singleton 92 | [42]: estruturais/adapter 93 | [43]: estruturais/bridge 94 | [44]: estruturais/composite 95 | [45]: estruturais/decorator 96 | [46]: estruturais/facade 97 | [47]: estruturais/flyweight 98 | [48]: estruturais/proxy 99 | -------------------------------------------------------------------------------- /comportamentais/chain_of_responsibility/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Chain of Responsibility 3 | 4 | O padrão de projeto Chain of Responsibility nos permite aplicar uma lógica sequencial de forma dinâmica. Utilizando este padrão podemos deixar de usar diversos IFs e ELSEs por uma cadeia de classes que serão executadas em sequência. Isso facilita bastante a manutenção do código. 5 | 6 | > A ideia do padrão é resolver problemas como esses: de acordo com o cenário, devemos realizar alguma ação. Ao invés de escrevermos código procedural, e deixarmos um único método descobrir o que deve ser feito, quebramos essas responsabilidades em várias diferentes classes, e as unimos como uma corrente. 7 | 8 | ## Quando usar? 9 | 10 | > O padrão Chain of Responsibility cai como uma luva quando temos uma lista de comandos a serem executados de acordo com algum cenário em específico, e sabemos também qual o próximo cenário que deve ser validado, caso o anterior não satisfaça a condição. Nesses casos, o Chain of Responsibility nos possibilita a separação de responsabilidades em classes pequenas e enxutas, e ainda provê uma maneira flexível e desacoplada de juntar esses comportamentos novamente. 11 | 12 | ## Desvantagens 13 | 14 | Os padrões Strategy e Chain of Responsibility, assim como outros, separam melhor as responsabilidades e deixam o código mais flexível porém, introduzem uma indireção, delegando o trabalho a outras classes, tornando o código mais complexo e consequentemente mais difícil de compreender. 15 | 16 | -------------------------------------------------------------------------------- /comportamentais/chain_of_responsibility/descontos.py: -------------------------------------------------------------------------------- 1 | class DescontoCincoItens: 2 | def __init__(self, proximo_desconto): 3 | self.__proximo_desconto = proximo_desconto 4 | 5 | def calcula(self, orcamento): 6 | if orcamento.total_itens > 5: 7 | return orcamento.valor * 0.1 8 | return self.__proximo_desconto.calcula(orcamento) 9 | 10 | 11 | class DescontoMaisDeQuinhentosReais: 12 | def __init__(self, proximo_desconto): 13 | self.__proximo_desconto = proximo_desconto 14 | 15 | def calcula(self, orcamento): 16 | if orcamento.valor > 500.0: 17 | return orcamento.valor * 0.07 18 | return self.__proximo_desconto.calcula(orcamento) 19 | 20 | 21 | class SemDesconto: 22 | def calcula(self, orcamento): 23 | return 0 24 | -------------------------------------------------------------------------------- /comportamentais/chain_of_responsibility/main.py: -------------------------------------------------------------------------------- 1 | from descontos import ( 2 | DescontoCincoItens, 3 | DescontoMaisDeQuinhentosReais, 4 | SemDesconto, 5 | ) 6 | 7 | 8 | class CalculadorDescontos: 9 | def calcula(self, orcamento): 10 | return DescontoCincoItens( 11 | DescontoMaisDeQuinhentosReais(SemDesconto()) 12 | ).calcula(orcamento) 13 | 14 | 15 | if __name__ == '__main__': 16 | 17 | from orcamento import Orcamento, Item 18 | 19 | orcamento = Orcamento() 20 | 21 | orcamento.adiciona_item(Item('item 0', 100.0)) 22 | orcamento.adiciona_item(Item('item 1', 100.0)) 23 | orcamento.adiciona_item(Item('item 2', 100.0)) 24 | orcamento.adiciona_item(Item('item 3', 100.0)) 25 | orcamento.adiciona_item(Item('item 4', 100.0)) 26 | orcamento.adiciona_item(Item('item 5', 100.0)) 27 | orcamento.adiciona_item(Item('item 6', 100.0)) 28 | orcamento.adiciona_item(Item('item 7', 100.0)) 29 | orcamento.adiciona_item(Item('item 8', 100.0)) 30 | orcamento.adiciona_item(Item('item 9', 100.0)) 31 | 32 | print(orcamento.valor) 33 | 34 | calculator = CalculadorDescontos() 35 | 36 | desconto = calculator.calcula(orcamento) 37 | 38 | print(f'Desconto calculado {desconto}') 39 | -------------------------------------------------------------------------------- /comportamentais/chain_of_responsibility/orcamento.py: -------------------------------------------------------------------------------- 1 | class Orcamento: 2 | def __init__(self): 3 | self.__itens = list() 4 | 5 | @property 6 | def valor(self): 7 | return sum([item.valor for item in self.__itens]) 8 | 9 | def obter_itens(self): 10 | return tuple(self.__itens) 11 | 12 | @property 13 | def total_itens(self): 14 | return len(self.__itens) 15 | 16 | def adiciona_item(self, item): 17 | self.__itens.append(item) 18 | 19 | 20 | class Item: 21 | def __init__(self, nome, valor): 22 | self.__nome = nome 23 | self.__valor = valor 24 | 25 | @property 26 | def valor(self): 27 | return self.__valor 28 | 29 | @property 30 | def nome(self): 31 | return self.__nome 32 | -------------------------------------------------------------------------------- /comportamentais/command/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Command 3 | 4 | Podemos utilizar o design pattern command quando precisamos executar uma sequência de comandos em cima de algum dado, por exemplo executar uma sequência de ações em um pedido. 5 | 6 | Para aplicar o padrão command, podemos utilizar uma classe abstrata para 'forçar' as classes filhas a implementar um método comum. 7 | 8 | ## Quando usar? 9 | 10 | Quando temos uma fila de dados para processar e precisamos ter uma 'conexão forte' com alguma operação que será utilizada sobre ele, utilizamos o padrão de projeto command. 11 | 12 | ## Diferença do Command para o Strategy 13 | 14 | > A ideia do Command é abstrair um comando que deve ser executado, pois não é possível executá-lo naquele momento (pois precisamos por em uma fila ou coisa do tipo). Já no Strategy, a ideia é que você tenha uma estratégia (um algoritmo) para resolver um problema. -------------------------------------------------------------------------------- /comportamentais/command/main.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | from datetime import date 3 | 4 | 5 | class Pedido: 6 | def __init__(self, cliente, valor): 7 | self.__cliente = cliente 8 | self.__valor = valor 9 | self.__status = 'NOVO' 10 | self.__data_finalizacao = None 11 | 12 | def paga(self): 13 | self.__status = 'PAGO' 14 | 15 | def finaliza(self): 16 | self.__status = 'FINALIZADO' 17 | self.__data_finalizacao = date.today() 18 | 19 | @property 20 | def cliente(self): 21 | return self.__cliente 22 | 23 | @property 24 | def valor(self): 25 | return self.__valor 26 | 27 | @property 28 | def status(self): 29 | return self.__status 30 | 31 | @property 32 | def data_finalizacao(self): 33 | return self.__data_finalizacao 34 | 35 | 36 | class Comando(metaclass=ABCMeta): 37 | @abstractmethod 38 | def executa(self): 39 | pass 40 | 41 | 42 | class PagaPedido(Comando): 43 | def __init__(self, pedido): 44 | self.__pedido = pedido 45 | 46 | def executa(self): 47 | self.__pedido.paga() 48 | 49 | 50 | class FinalizaPedido(Comando): 51 | def __init__(self, pedido): 52 | self.__pedido = pedido 53 | 54 | def executa(self): 55 | self.__pedido.finaliza() 56 | 57 | 58 | class FilaTrabalho: 59 | def __init__(self): 60 | self.__comandos = list() 61 | 62 | def adiciona(self, comando): 63 | self.__comandos.append(comando) 64 | 65 | def processa(self): 66 | for comando in self.__comandos: 67 | comando.executa() 68 | 69 | 70 | if __name__ == '__main__': 71 | 72 | pedido1 = Pedido('Joao', 200.0) 73 | pedido2 = Pedido('Ana', 400.0) 74 | 75 | fila = FilaTrabalho() 76 | 77 | comando1 = FinalizaPedido(pedido1) 78 | comando2 = PagaPedido(pedido1) 79 | comando3 = FinalizaPedido(pedido2) 80 | 81 | fila.adiciona(comando1) 82 | fila.adiciona(comando2) 83 | fila.adiciona(comando3) 84 | 85 | fila.processa() 86 | 87 | print(pedido1.status) 88 | print(pedido2.status) 89 | -------------------------------------------------------------------------------- /comportamentais/interpreter/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Interpreter 3 | 4 | O padrão interpreter pode ser utilizado quando precisamos interpretar diversas operações, como por exemplo em uma calculadora científica. 5 | 6 | **DSL**: Domain Specific Language 7 | 8 | ## Quando usar? 9 | 10 | > O padrão Interpreter é geralmente útil para interpretar DSLs. É comum que, ao ler a string (como por exemplo 2+3/4), o programa transforme-o em uma melhor estrutura de dados (como as nossas classes Expressao) e aí interprete essa árvore. 11 | 12 | É um padrão de projeto peculiar e com utilização bem específica. -------------------------------------------------------------------------------- /comportamentais/interpreter/main.py: -------------------------------------------------------------------------------- 1 | class Subtracao: 2 | def __init__(self, expressao_esquerda, expressao_direita): 3 | self.__expressao_esquerda = expressao_esquerda 4 | self.__expressao_direita = expressao_direita 5 | 6 | def avalia(self): 7 | return ( 8 | self.__expressao_esquerda.avalia() 9 | - self.__expressao_direita.avalia() 10 | ) 11 | 12 | 13 | class Soma: 14 | def __init__(self, expressao_esquerda, expressao_direita): 15 | self.__expressao_esquerda = expressao_esquerda 16 | self.__expressao_direita = expressao_direita 17 | 18 | def avalia(self): 19 | return ( 20 | self.__expressao_esquerda.avalia() 21 | + self.__expressao_direita.avalia() 22 | ) 23 | 24 | 25 | class Numero: 26 | def __init__(self, numero): 27 | self.__numero = numero 28 | 29 | def avalia(self): 30 | return self.__numero 31 | 32 | 33 | if __name__ == '__main__': 34 | expressao_esquerda = Soma(Numero(10), Numero(20)) 35 | expressao_direita = Soma(Numero(5), Numero(2)) 36 | expressao_conta = Soma(expressao_esquerda, expressao_direita) 37 | print(expressao_conta.avalia()) 38 | 39 | expressao_conta2 = Subtracao(Numero(100), Numero(70)) 40 | print(expressao_conta2.avalia()) 41 | -------------------------------------------------------------------------------- /comportamentais/iterator/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Iterator 3 | 4 | Um bom exemplo de iterator seria um rádio antigo, onde o usuário poderia começar em uma determinada estação e utilizar os botões next e previous para ir para outras estações. 5 | 6 | Basicamente o iterator disponibiliza uma maneira de acessar elementos de um objeto sem expor o conteúdo todo. 7 | 8 | ## Quando usar? 9 | 10 | O padrão de iterador desacopla algoritmos de contêineres. Em alguns casos, os algoritmos são necessariamente específicos do contêiner e, portanto, não podem ser desacoplados. 11 | -------------------------------------------------------------------------------- /comportamentais/iterator/main.py: -------------------------------------------------------------------------------- 1 | class RadioStation: 2 | def __init__(self, frequency): 3 | self.__frequency = frequency 4 | 5 | @property 6 | def frequency(self): 7 | return self.__frequency 8 | 9 | 10 | class StationList: 11 | def __init__(self): 12 | self.__stations = list() 13 | self.__counter = 0 14 | 15 | def add_station(self, radio_station): 16 | self.__stations.append(radio_station) 17 | 18 | def remove_station(self, frequency): 19 | for index in range(0, len(self.__stations)): 20 | if self.__stations[index].frequency == frequency: 21 | self.__stations.pop(index) 22 | break 23 | else: 24 | print('Radio station not found') 25 | 26 | def count(self): 27 | return len(self.__stations) 28 | 29 | def current(self): 30 | return self.__stations[self.__counter].frequency 31 | 32 | def key(self): 33 | return self.__counter 34 | 35 | def __next__(self): 36 | self.__counter += 1 37 | 38 | def rewind(self): 39 | self.__counter = 0 40 | 41 | 42 | if __name__ == '__main__': 43 | station_list = StationList() 44 | 45 | station_list.add_station(RadioStation(89)) 46 | station_list.add_station(RadioStation(101)) 47 | station_list.add_station(RadioStation(102)) 48 | station_list.add_station(RadioStation(103.2)) 49 | 50 | print(f'Stations: {station_list.count()}') 51 | station_list.remove_station(89) 52 | print(f'Stations: {station_list.count()}') 53 | 54 | print(f'Current Station: {station_list.current()}') 55 | next(station_list) 56 | print(f'Current Station: {station_list.current()}') 57 | -------------------------------------------------------------------------------- /comportamentais/mediator/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Mediator 3 | 4 | O padrão Mediator define um objeto que encapsula a lógica de comunicação entre um conjunto de objetos. Ele basicamente adiciona um terceiro objeto (mediador) que controla a interação entre dois objetos, assim cada objeto não precisa ter conhecimento da implementação do outro objeto. 5 | 6 | ## Quando usar? 7 | 8 | Quando precisamos realizar uma interação/comunicação entre dois ou mais objetos. 9 | -------------------------------------------------------------------------------- /comportamentais/mediator/main.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | from datetime import datetime 3 | 4 | 5 | class ChatRoomMediator(metaclass=ABCMeta): 6 | @abstractmethod 7 | def show_message(self, user, message): 8 | pass 9 | 10 | 11 | class ChatRoom(ChatRoomMediator): 12 | """Mediator""" 13 | 14 | def show_message(self, user, message): 15 | time = datetime.now() 16 | sender = user.name 17 | print(f'{time} [{sender}]: {message}') 18 | 19 | 20 | class User: 21 | def __init__(self, name, chat_mediator): 22 | self.name = name 23 | self.chat_mediator = chat_mediator 24 | 25 | def send(self, message): 26 | self.chat_mediator.show_message(self, message) 27 | 28 | 29 | if __name__ == '__main__': 30 | 31 | mediator = ChatRoom() 32 | 33 | john = User('John', mediator) 34 | jane = User('Jane', mediator) 35 | josh = User('Josh', mediator) 36 | 37 | john.send('Hi there!') 38 | jane.send('Hi!') 39 | john.send('How are you?') 40 | jane.send("I'm great, thanks!") 41 | josh.send('Hi guys!') 42 | -------------------------------------------------------------------------------- /comportamentais/memento/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Memento 3 | 4 | Neste exemplo poderíamos utilizar também o design pattern State, ou seja, podemos alcançar objetivos semelhantes com estes dois padrões. 5 | 6 | Podemos aplicar o memento sempre que desejamos guardar um estado que possa ser restaurado futuramente. 7 | 8 | ## Quando usar? 9 | 10 | Devemos utilizar o memento sempre que precisamos armazenar um histórico que possa ser restaurado. 11 | -------------------------------------------------------------------------------- /comportamentais/memento/main.py: -------------------------------------------------------------------------------- 1 | from datetime import date 2 | 3 | 4 | class Contrato: 5 | def __init__(self, data, cliente, tipo): 6 | self.data = data 7 | self.cliente = cliente 8 | self.tipo = tipo 9 | 10 | def avanca(self): 11 | if self.tipo == 'NOVO': 12 | self.tipo = 'EM ANDAMENTO' 13 | elif self.tipo == 'EM ANDAMENTO': 14 | self.tipo = 'ACERTADO' 15 | elif self.tipo == 'ACERTADO': 16 | self.tipo = 'CONCLUIDO' 17 | 18 | def salva_estado(self): 19 | # Não podemos passar o self para o Estado pois se o contrato fosse 20 | # alterado o estado anterior dele também seria alterado 21 | return Estado( 22 | Contrato(data=self.data, cliente=self.cliente, tipo=self.tipo) 23 | ) 24 | 25 | def restaura_estado(self, estado): 26 | self.cliente = estado.contrato.cliente 27 | self.data = estado.contrato.data 28 | self.tipo = estado.contrato.tipo 29 | 30 | 31 | class Estado: 32 | def __init__(self, contrato): 33 | self.__contrato = contrato 34 | 35 | @property 36 | def contrato(self): 37 | return self.__contrato 38 | 39 | 40 | class Historico: 41 | def __init__(self): 42 | self.__estados_salvos = list() 43 | 44 | def obtem_estado(self, indice): 45 | return self.__estados_salvos[indice] 46 | 47 | def adiciona_estado(self, estado): 48 | self.__estados_salvos.append(estado) 49 | 50 | 51 | if __name__ == '__main__': 52 | 53 | historico = Historico() 54 | 55 | contrato = Contrato(data=date.today(), cliente='Kelvin', tipo='NOVO') 56 | 57 | contrato.avanca() 58 | 59 | historico.adiciona_estado(contrato.salva_estado()) 60 | 61 | contrato.avanca() 62 | 63 | contrato.cliente = 'Joao da Silva' 64 | 65 | historico.adiciona_estado(contrato.salva_estado()) 66 | 67 | contrato.avanca() 68 | 69 | historico.adiciona_estado(contrato.salva_estado()) 70 | 71 | print(contrato.tipo) 72 | print(contrato.cliente) 73 | 74 | contrato.restaura_estado(historico.obtem_estado(0)) 75 | 76 | print(contrato.tipo) 77 | print(contrato.cliente) 78 | -------------------------------------------------------------------------------- /comportamentais/observer/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Observer 3 | 4 | O padrão observer nos permite passar uma lista de observadores, interessados pela criação de um objeto, para uma classe, e iterar sobre ela para rodar todos os observadores. 5 | 6 | Assim, caso tenhamos um novo observador, basta incluí-lo na lista de observadores. 7 | 8 | ## Quando usar? 9 | 10 | > Quando o acoplamento da nossa classe está crescendo, ou quando temos diversas ações diferentes a serem executadas após um determinado processo. Nestes casos, podemos implementar o Observer. 11 | 12 | > Ele permite que diversas ações sejam executadas de forma transparente à classe principal, reduzindo o acoplamento entre essas ações, facilitando a manutenção e evolução do código. 13 | -------------------------------------------------------------------------------- /comportamentais/observer/main.py: -------------------------------------------------------------------------------- 1 | from datetime import date 2 | 3 | 4 | class Item: 5 | def __init__(self, descricao, valor): 6 | self.__descricao = descricao 7 | self.__valor = valor 8 | 9 | @property 10 | def descricao(self): 11 | return self.__descricao 12 | 13 | @property 14 | def valor(self): 15 | return self.__valor 16 | 17 | 18 | class NotaFiscal: 19 | def __init__( 20 | self, 21 | razao_social, 22 | cnpj, 23 | itens, 24 | data_de_emissao=date.today(), 25 | detalhes='', 26 | observadores=list(), 27 | ): 28 | self.__razao_social = razao_social 29 | self.__cnpj = cnpj 30 | self.__data_de_emissao = data_de_emissao 31 | if len(detalhes) > 20: 32 | raise Exception( 33 | 'Detalhes da nota fiscal nao pode ter mais que 20 chars' 34 | ) 35 | self.__detalhes = detalhes 36 | self.__itens = itens 37 | 38 | for observador in observadores: 39 | observador(self) 40 | 41 | @property 42 | def razao_social(self): 43 | return self.__razao_social 44 | 45 | @property 46 | def cnpj(self): 47 | return self.__cnpj 48 | 49 | @property 50 | def data_de_emissao(self): 51 | return self.__data_de_emissao 52 | 53 | @property 54 | def detalhes(self): 55 | return self.__detalhes 56 | 57 | @property 58 | def itens(self): 59 | return self.__itens 60 | 61 | 62 | if __name__ == '__main__': 63 | 64 | from observadores import imprime, envia_por_email, salva_no_banco 65 | 66 | itens = [Item('ITEM A', 100), Item('ITEM B', 200)] 67 | 68 | nota_fiscal = NotaFiscal( 69 | razao_social='FHSA Limitada', 70 | cnpj='01928391827321', 71 | itens=itens, 72 | data_de_emissao=date.today(), 73 | detalhes='', 74 | observadores=[imprime, envia_por_email, salva_no_banco], 75 | ) 76 | -------------------------------------------------------------------------------- /comportamentais/observer/observadores.py: -------------------------------------------------------------------------------- 1 | def imprime(nota_fiscal): 2 | print(f'Imprimindo nota fiscal {nota_fiscal.cnpj}') 3 | 4 | 5 | def envia_por_email(nota_fiscal): 6 | print(f'Enviando nota fiscal {nota_fiscal.cnpj} por email') 7 | 8 | 9 | def salva_no_banco(nota_fiscal): 10 | print(f'Salvando nota fiscal {nota_fiscal.cnpj} no banco') 11 | -------------------------------------------------------------------------------- /comportamentais/state/README.md: -------------------------------------------------------------------------------- 1 | 2 | # State 3 | 4 | O design pattern State pode ser utilizado quando precisamos definir um conjunto de estados e os mesmos possuem uma ordem bem definida. 5 | 6 | ## Quando usar? 7 | 8 | > A principal situação que faz emergir o Design Pattern State é a necessidade de implementação de uma máquina de estados. Geralmente, o controle das possíveis transições entre estados são várias, também são complexas, fazendo com que a implementação não seja simples. O State auxilia a manter o controle dos estados simples e organizados, através da criação de classes que representem cada estado e sabendo controlar as transições entre eles. -------------------------------------------------------------------------------- /comportamentais/state/main.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class EstadoOrcamento(metaclass=ABCMeta): 5 | @abstractmethod 6 | def aplica_desconto_extra(self, orcamento): 7 | pass 8 | 9 | @abstractmethod 10 | def aprova(self, orcamento): 11 | pass 12 | 13 | @abstractmethod 14 | def reprova(self, orcamento): 15 | pass 16 | 17 | @abstractmethod 18 | def finaliza(self, orcamento): 19 | pass 20 | 21 | 22 | class EmAprovacao(EstadoOrcamento): 23 | def aplica_desconto_extra(self, orcamento): 24 | orcamento.adiciona_desconto_extra(orcamento.valor * 0.02) 25 | 26 | def aprova(self, orcamento): 27 | orcamento.estado_atual = Aprovado() 28 | 29 | def reprova(self, orcamento): 30 | orcamento.estado_atual = Reprovado() 31 | 32 | def finaliza(self, orcamento): 33 | raise Exception('Orcamentos em aprovacao nao pode ser finalizado') 34 | 35 | 36 | class Aprovado(EstadoOrcamento): 37 | def aplica_desconto_extra(self, orcamento): 38 | orcamento.adiciona_desconto_extra(orcamento.valor * 0.05) 39 | 40 | def aprova(self, orcamento): 41 | raise Exception('Orcamentos aprovado nao pode ser aprovado novamente') 42 | 43 | def reprova(self, orcamento): 44 | raise Exception('Orcamentos aprovado nao pode ser reprovado') 45 | 46 | def finaliza(self, orcamento): 47 | orcamento.estado_atual = Finalizado() 48 | 49 | 50 | class Reprovado(EstadoOrcamento): 51 | def aplica_desconto_extra(self, orcamento): 52 | raise Exception('Orcamentos reprovados nao recebem desconto extra') 53 | 54 | def aprova(self, orcamento): 55 | raise Exception('Orcamentos aprovado nao pode ser aprovado') 56 | 57 | def reprova(self, orcamento): 58 | raise Exception('Orcamentos aprovado nao pode ser reprovado novamente') 59 | 60 | def finaliza(self, orcamento): 61 | orcamento.estado_atual = Finalizado() 62 | 63 | 64 | class Finalizado(EstadoOrcamento): 65 | def aplica_desconto_extra(self, orcamento): 66 | raise Exception('Orcamentos finalizados nao recebem desconto extra') 67 | 68 | def aprova(self, orcamento): 69 | raise Exception('Orcamentos finalizado nao pode ser aprovado') 70 | 71 | def reprova(self, orcamento): 72 | raise Exception('Orcamentos finalizado nao pode ser reprovado') 73 | 74 | def finaliza(self, orcamento): 75 | raise Exception( 76 | 'Orcamentos finalizado nao pode ser finalizado novamente' 77 | ) 78 | 79 | 80 | class Orcamento: 81 | def __init__(self): 82 | self.__itens = list() 83 | self.estado_atual = EmAprovacao() 84 | self.__desconto_extra = 0 85 | 86 | def aprova(self): 87 | self.estado_atual.aprova(self) 88 | 89 | def reprova(self): 90 | self.estado_atual.reprova(self) 91 | 92 | def finaliza(self): 93 | self.estado_atual.finaliza(self) 94 | 95 | def adiciona_desconto_extra(self, desconto): 96 | self.__desconto_extra += desconto 97 | 98 | def aplica_desconto_extra(self): 99 | self.estado_atual.aplica_desconto_extra(self) 100 | 101 | @property 102 | def valor(self): 103 | total = 0.0 104 | for item in self.__itens: 105 | total += item.valor 106 | return total - self.__desconto_extra 107 | 108 | def obter_itens(self): 109 | return tuple(self.__itens) 110 | 111 | @property 112 | def total_itens(self): 113 | return len(self.__itens) 114 | 115 | def adiciona_item(self, item): 116 | self.__itens.append(item) 117 | 118 | 119 | class Item: 120 | def __init__(self, nome, valor): 121 | self.__nome = nome 122 | self.__valor = valor 123 | 124 | @property 125 | def valor(self): 126 | return self.__valor 127 | 128 | @property 129 | def nome(self): 130 | return self.__nome 131 | 132 | 133 | if __name__ == '__main__': 134 | 135 | orcamento = Orcamento() 136 | 137 | orcamento.adiciona_item(Item('item 0', 100.0)) 138 | orcamento.adiciona_item(Item('item 1', 50.0)) 139 | orcamento.adiciona_item(Item('item 2', 400.0)) 140 | 141 | print(orcamento.valor) 142 | 143 | orcamento.aprova() 144 | 145 | orcamento.aplica_desconto_extra() 146 | 147 | print(orcamento.valor) 148 | 149 | orcamento.finaliza() 150 | -------------------------------------------------------------------------------- /comportamentais/strategy/README.md: -------------------------------------------------------------------------------- 1 | # Padrão Strategy 2 | 3 | Ao utilizar este padrão podemos passar como parâmetro uma função, ou melhor, uma estratégia, para outro método. 4 | 5 | Isso permite remover vários comandos condicionais, uma vez que não precisamos verificar manualmente qual foi a estratégia passada como parâmetro. 6 | 7 | **Nota**: diferença entre função e método: um método pertence a uma classe e uma função não pertence necessariamente a uma classe. 8 | 9 | **Duck typing**: é um estilo de tipagem em que os métodos e propriedades de um objeto determinam a semântica válida, em vez de sua herança de uma classe particular ou implementação de uma interface explicita. Não importa a instância que passamos, contanto que ela tenha o método `calcula`. 10 | 11 | ## Quando usar? 12 | 13 | > O padrão Strategy é muito útil quando temos um conjunto de algoritmos similares, e precisamos alternar entre eles em diferentes pedaços da aplicação. O Strategy nos oferece uma maneira flexível para escrever diversos algoritmos diferentes, e de passar esses algoritmos para classes clientes que precisam deles. Esses clientes desconhecem qual é o algoritmo "real" que está sendo executado, e apenas manda o algoritmo rodar. Isso faz com que o código da classe cliente fique bastante desacoplado das implementações dos algoritmos, possibilitando assim com que esse cliente consiga trabalhar com N diferentes algoritmos sem precisar alterar o seu código. -------------------------------------------------------------------------------- /comportamentais/strategy/impostos.py: -------------------------------------------------------------------------------- 1 | class ISS: 2 | def calcula(self, orcamento): 3 | return orcamento.valor * 0.1 4 | 5 | 6 | class ICMS: 7 | def calcula(self, orcamento): 8 | return orcamento.valor * 0.06 9 | -------------------------------------------------------------------------------- /comportamentais/strategy/main.py: -------------------------------------------------------------------------------- 1 | from impostos import ICMS, ISS 2 | 3 | 4 | class CalculadorImpostos: 5 | def realiza_calculo(self, orcamento, imposto): 6 | imposto_calculado = imposto.calcula(orcamento) 7 | print(imposto_calculado) 8 | 9 | 10 | if __name__ == '__main__': 11 | 12 | from orcamento import Orcamento 13 | 14 | calculador = CalculadorImpostos() 15 | 16 | orcamento = Orcamento(500) 17 | 18 | calculador.realiza_calculo(orcamento, ISS()) 19 | calculador.realiza_calculo(orcamento, ICMS()) 20 | -------------------------------------------------------------------------------- /comportamentais/strategy/orcamento.py: -------------------------------------------------------------------------------- 1 | class Orcamento: 2 | def __init__(self, valor): 3 | self.__valor = valor 4 | 5 | @property 6 | def valor(self): 7 | return self.__valor 8 | -------------------------------------------------------------------------------- /comportamentais/template_method/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Template Method 3 | 4 | Ao utilizar este padrão, criamos classes abstratas para abstrair métodos em comum entre diversas classes. Devemos utilizar o decorator `@abstractmethod` para obrigar as classes filhas a implementar os métodos abstratos. 5 | 6 | Para utilizar classes abstratas em Python é preciso importar o módulo ABC: 7 | 8 | ```python 9 | from abc import ABCMeta, abstractmethod 10 | ``` 11 | 12 | ## Quando usar? 13 | 14 | Toda vez que temos uma estrutura semelhante que queremos aproveitar no código, podemos aplicar o padrão de projeto Template Method. 15 | 16 | > Com ele conseguimos definir em um nível mais macro a estrutura do algoritmo, deixando "buracos" que serão implementados de maneira diferente por cada uma das implementações específicas. 17 | 18 | Assim, reutilizamos o código ao invés de repeti-lo, facilitando a manutenção e futuras alterações, uma vez que cada classe tem sua responsabilidade bem separada. -------------------------------------------------------------------------------- /comportamentais/template_method/impostos.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class TemplateImpostosCondicional(metaclass=ABCMeta): 5 | def calcula(self, orcamento): 6 | if self.deve_usar_maxima_taxacao(orcamento): 7 | return self.maxima_taxacao(orcamento) 8 | return self.minima_taxacao(orcamento) 9 | 10 | @abstractmethod 11 | def deve_usar_maxima_taxacao(self, orcamento): 12 | pass 13 | 14 | @abstractmethod 15 | def maxima_taxacao(self, orcamento): 16 | pass 17 | 18 | @abstractmethod 19 | def minima_taxacao(self, orcamento): 20 | pass 21 | 22 | 23 | class ISS: 24 | def calcula(self, orcamento): 25 | return orcamento.valor * 0.1 26 | 27 | 28 | class ICMS: 29 | def calcula(self, orcamento): 30 | return orcamento.valor * 0.06 31 | 32 | 33 | class ICPP(TemplateImpostosCondicional): 34 | def deve_usar_maxima_taxacao(self, orcamento): 35 | return orcamento.valor > 500 36 | 37 | def maxima_taxacao(self, orcamento): 38 | return orcamento.valor * 0.07 39 | 40 | def minima_taxacao(self, orcamento): 41 | return orcamento.valor * 0.05 42 | 43 | 44 | class IKCV(TemplateImpostosCondicional): 45 | def __tem_item_maior_que_cem_reais(self, orcamento): 46 | for item in orcamento.obter_itens(): 47 | if item.valor > 100: 48 | return True 49 | return False 50 | 51 | def deve_usar_maxima_taxacao(self, orcamento): 52 | return orcamento.valor > 500 and self.__tem_item_maior_que_cem_reais( 53 | orcamento 54 | ) 55 | 56 | def maxima_taxacao(self, orcamento): 57 | return orcamento.valor * 0.1 58 | 59 | def minima_taxacao(self, orcamento): 60 | return orcamento.valor * 0.06 61 | -------------------------------------------------------------------------------- /comportamentais/template_method/main.py: -------------------------------------------------------------------------------- 1 | from impostos import ICMS, ICPP, IKCV, ISS 2 | 3 | 4 | class CalculadorImpostos: 5 | def realiza_calculo(self, orcamento, imposto): 6 | imposto_calculado = imposto.calcula(orcamento) 7 | print(imposto_calculado) 8 | 9 | 10 | if __name__ == '__main__': 11 | 12 | from orcamento import Orcamento, Item 13 | 14 | orcamento = Orcamento() 15 | 16 | orcamento.adiciona_item(Item('item 0', 50.0)) 17 | orcamento.adiciona_item(Item('item 1', 200.0)) 18 | orcamento.adiciona_item(Item('item 2', 250.0)) 19 | 20 | calculador = CalculadorImpostos() 21 | 22 | print('ISS e ICMS') 23 | calculador.realiza_calculo(orcamento, ISS()) 24 | calculador.realiza_calculo(orcamento, ICMS()) 25 | 26 | print('ICPP e IKCV') 27 | calculador.realiza_calculo(orcamento, ICPP()) 28 | calculador.realiza_calculo(orcamento, IKCV()) 29 | -------------------------------------------------------------------------------- /comportamentais/template_method/orcamento.py: -------------------------------------------------------------------------------- 1 | class Orcamento: 2 | def __init__(self): 3 | self.__itens = list() 4 | 5 | @property 6 | def valor(self): 7 | total = 0.0 8 | for item in self.__itens: 9 | total += item.valor 10 | return total 11 | 12 | def obter_itens(self): 13 | return tuple(self.__itens) 14 | 15 | @property 16 | def total_itens(self): 17 | return len(self.__itens) 18 | 19 | def adiciona_item(self, item): 20 | self.__itens.append(item) 21 | 22 | 23 | class Item: 24 | def __init__(self, nome, valor): 25 | self.__nome = nome 26 | self.__valor = valor 27 | 28 | @property 29 | def valor(self): 30 | return self.__valor 31 | 32 | @property 33 | def nome(self): 34 | return self.__nome 35 | -------------------------------------------------------------------------------- /comportamentais/visitor/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Visitor 3 | 4 | O visitor nos permite navegar pelos elementos de uma estrutura de dados. 5 | 6 | ## Quando usar? 7 | 8 | Sempre que precisamos navegar em uma estrutura (e.g. uma árvore de elementos) e queremos executar alguma ação, podemos utilizar o design pattern visitor. 9 | -------------------------------------------------------------------------------- /comportamentais/visitor/impressao.py: -------------------------------------------------------------------------------- 1 | class Impressao: 2 | def visita_soma(self, soma): 3 | print('(', end=' ') 4 | soma.expressao_esquerda.aceita(self) 5 | print('+', end=' ') 6 | soma.expressao_direita.aceita(self) 7 | print(')', end=' ') 8 | 9 | def visita_subtracao(self, subtracao): 10 | print('(', end=' ') 11 | subtracao.expressao_esquerda.aceita(self) 12 | print('-', end=' ') 13 | subtracao.expressao_direita.aceita(self) 14 | print(')', end=' ') 15 | 16 | def visita_numero(self, numero): 17 | print(numero.avalia(), end=' ') 18 | -------------------------------------------------------------------------------- /comportamentais/visitor/main.py: -------------------------------------------------------------------------------- 1 | class Subtracao: 2 | def __init__(self, expressao_esquerda, expressao_direita): 3 | self.__expressao_esquerda = expressao_esquerda 4 | self.__expressao_direita = expressao_direita 5 | 6 | def avalia(self): 7 | return ( 8 | self.__expressao_esquerda.avalia() 9 | - self.__expressao_direita.avalia() 10 | ) 11 | 12 | def aceita(self, visitor): 13 | visitor.visita_subtracao(self) 14 | 15 | @property 16 | def expressao_esquerda(self): 17 | return self.__expressao_esquerda 18 | 19 | @property 20 | def expressao_direita(self): 21 | return self.__expressao_direita 22 | 23 | 24 | class Soma: 25 | def __init__(self, expressao_esquerda, expressao_direita): 26 | self.__expressao_esquerda = expressao_esquerda 27 | self.__expressao_direita = expressao_direita 28 | 29 | def avalia(self): 30 | return ( 31 | self.__expressao_esquerda.avalia() 32 | + self.__expressao_direita.avalia() 33 | ) 34 | 35 | def aceita(self, visitor): 36 | visitor.visita_soma(self) 37 | 38 | @property 39 | def expressao_esquerda(self): 40 | return self.__expressao_esquerda 41 | 42 | @property 43 | def expressao_direita(self): 44 | return self.__expressao_direita 45 | 46 | 47 | class Numero: 48 | def __init__(self, numero): 49 | self.__numero = numero 50 | 51 | def avalia(self): 52 | return self.__numero 53 | 54 | def aceita(self, visitor): 55 | visitor.visita_numero(self) 56 | 57 | 58 | if __name__ == '__main__': 59 | 60 | from impressao import Impressao 61 | 62 | expressao_esquerda = Soma(Numero(10), Numero(20)) 63 | expressao_direita = Soma(Numero(5), Numero(2)) 64 | expressao_conta = Soma(expressao_esquerda, expressao_direita) 65 | 66 | impressao = Impressao() 67 | expressao_conta.aceita(impressao) 68 | 69 | print('') 70 | 71 | expressao_esquerda = Subtracao(Numero(100), Numero(20)) 72 | expressao_direita = Soma(Numero(5), Numero(5)) 73 | expressao_conta = Soma(expressao_esquerda, expressao_direita) 74 | expressao_conta.aceita(impressao) 75 | -------------------------------------------------------------------------------- /criacao/abstract_factory/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Abstract Factory 3 | 4 | - O método Abstract Factory contém um ou mais métodos de fábrica para criar uma família de objetos relacionados. 5 | - Usa composição para delegar a responsabilidade de criar objetos de outra classe. 6 | - O método Abstract Factory diz respeito a criar famílias de produtos relacionados. 7 | 8 | ## Quando usar? 9 | 10 | Pode ser utilizado quando precisamos criar uma família de objetos relacionados sem especificar a classe concreta. 11 | 12 | Enquanto o Factory Method adia a criação da instância para as subclasses, o objetivo do método Abstract Factory é criar famílias de objetos relacionados. 13 | -------------------------------------------------------------------------------- /criacao/abstract_factory/main.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class PizzaFactory(metaclass=ABCMeta): 5 | @abstractmethod 6 | def create_veg_pizza(self): 7 | pass 8 | 9 | @abstractmethod 10 | def create_non_veg_pizza(self): 11 | pass 12 | 13 | 14 | class IndianPizzaFactory(PizzaFactory): 15 | def create_veg_pizza(self): 16 | return DeluxeVeggiePizza() 17 | 18 | def create_non_veg_pizza(self): 19 | return ChickenPizza() 20 | 21 | 22 | class USPizzaFactory(PizzaFactory): 23 | def create_veg_pizza(self): 24 | return MexicanVegPizza() 25 | 26 | def create_non_veg_pizza(self): 27 | return HamPizza() 28 | 29 | 30 | class VegPizza(metaclass=ABCMeta): 31 | @abstractmethod 32 | def prepare(self, veg_pizza): 33 | pass 34 | 35 | 36 | class NonVegPizza(metaclass=ABCMeta): 37 | @abstractmethod 38 | def serve(self, veg_pizza): 39 | pass 40 | 41 | 42 | class DeluxeVeggiePizza(VegPizza): 43 | def prepare(self): 44 | print('Prepare', type(self).__name__) 45 | 46 | 47 | class ChickenPizza(NonVegPizza): 48 | def serve(self, veg_pizza): 49 | print( 50 | type(self).__name__, 51 | 'is served with Chicken on', 52 | type(veg_pizza).__name__, 53 | ) 54 | 55 | 56 | class MexicanVegPizza(VegPizza): 57 | def prepare(self): 58 | print('Prepare', type(self).__name__) 59 | 60 | 61 | class HamPizza(NonVegPizza): 62 | def serve(self, veg_pizza): 63 | print( 64 | type(self).__name__, 65 | 'is served with Ham on', 66 | type(veg_pizza).__name__, 67 | ) 68 | 69 | 70 | class PizzaStore: 71 | def make_pizzas(self): 72 | for factory in [IndianPizzaFactory(), USPizzaFactory()]: 73 | non_veg_pizza = factory.create_non_veg_pizza() 74 | veg_pizza = factory.create_veg_pizza() 75 | veg_pizza.prepare() 76 | non_veg_pizza.serve(veg_pizza) 77 | 78 | 79 | if __name__ == '__main__': 80 | pizza = PizzaStore() 81 | pizza.make_pizzas() 82 | -------------------------------------------------------------------------------- /criacao/builder/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Builder 3 | 4 | Com o design pattern builder, podemos criar uma classe builder, a qual é responsável por receber os parâmetros, verificar a validade dos parâmetros e até mesmo definir parâmetros padrões quando necessário. 5 | 6 | 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. 7 | 8 | ## Quando usar? 9 | 10 | > Sempre que tivermos um objeto complexo de ser criado, que possui diversos atributos, ou que possui uma lógica de criação complicada, podemos esconder tudo isso em um Builder. 11 | 12 | > Porém, na linguagem Python, esse pattern muitas vezes é desnecessário, já que parâmetros nomeados e opcionais do construtor de classes podem muitas vezes lidar com a complexidade de criação do objeto. 13 | -------------------------------------------------------------------------------- /criacao/builder/criador_de_nota_fiscal.py: -------------------------------------------------------------------------------- 1 | from datetime import date 2 | 3 | from main import NotaFiscal 4 | 5 | 6 | class CriadorNotaFiscal: 7 | def __init__(self): 8 | self.__razao_social = None 9 | self.__cnpj = None 10 | self.__data_de_emissao = None 11 | self.__detalhes = None 12 | self.__itens = None 13 | 14 | def com_razao_social(self, razao_social): 15 | self.__razao_social = razao_social 16 | return self 17 | 18 | def com_cnpj(self, cnpj): 19 | self.__cnpj = cnpj 20 | return self 21 | 22 | def com_data_de_emissao(self, data_de_emissao): 23 | self.__data_de_emissao = data_de_emissao 24 | return self 25 | 26 | def com_itens(self, itens): 27 | self.__itens = itens 28 | return self 29 | 30 | def com_detalhes(self, detalhes): 31 | self.__detalhes = detalhes 32 | return self 33 | 34 | def constroi(self): 35 | if self.__razao_social is None: 36 | raise Exception('Razao social deve ser preenchida') 37 | 38 | if self.__cnpj is None: 39 | raise Exception('CNPJ deve ser preenchido') 40 | 41 | if self.__itens is None: 42 | raise Exception('Os itens devem ser preenchidos') 43 | 44 | if self.__data_de_emissao is None: 45 | self.__data_de_emissao = date.today() 46 | 47 | if self.__detalhes is None: 48 | self.__detalhes = '' 49 | 50 | return NotaFiscal( 51 | razao_social=self.__razao_social, 52 | cnpj=self.__cnpj, 53 | itens=self.__itens, 54 | data_de_emissao=self.__data_de_emissao, 55 | detalhes=self.__detalhes, 56 | ) 57 | -------------------------------------------------------------------------------- /criacao/builder/main.py: -------------------------------------------------------------------------------- 1 | from datetime import date 2 | 3 | 4 | class Item: 5 | def __init__(self, descricao, valor): 6 | self.__descricao = descricao 7 | self.__valor = valor 8 | 9 | @property 10 | def descricao(self): 11 | return self.__descricao 12 | 13 | @property 14 | def valor(self): 15 | return self.__valor 16 | 17 | 18 | class NotaFiscal: 19 | def __init__( 20 | self, 21 | razao_social, 22 | cnpj, 23 | itens, 24 | data_de_emissao=date.today(), 25 | detalhes='', 26 | ): 27 | self.__razao_social = razao_social 28 | self.__cnpj = cnpj 29 | self.__data_de_emissao = data_de_emissao 30 | if len(detalhes) > 20: 31 | raise Exception( 32 | 'Detalhes da nota fiscal nao pode ter mais que 20 chars' 33 | ) 34 | self.__detalhes = detalhes 35 | self.__itens = itens 36 | 37 | @property 38 | def razao_social(self): 39 | return self.__razao_social 40 | 41 | @property 42 | def cnpj(self): 43 | return self.__cnpj 44 | 45 | @property 46 | def data_de_emissao(self): 47 | return self.__data_de_emissao 48 | 49 | @property 50 | def detalhes(self): 51 | return self.__detalhes 52 | 53 | @property 54 | def itens(self): 55 | return self.__itens 56 | 57 | 58 | if __name__ == '__main__': 59 | 60 | from criador_de_nota_fiscal import CriadorNotaFiscal 61 | 62 | itens = [Item('ITEM A', 100), Item('ITEM B', 200)] 63 | 64 | nota_fiscal = NotaFiscal( 65 | razao_social='FHSA Limitada', 66 | cnpj='01928391827321', 67 | itens=itens, 68 | data_de_emissao=date.today(), 69 | detalhes='', 70 | ) 71 | 72 | nota_fiscal_criada_com_builder = ( 73 | CriadorNotaFiscal() 74 | .com_razao_social('FHSA Limitada') 75 | .com_cnpj('01928391827321') 76 | .com_itens(itens) 77 | .constroi() 78 | ) 79 | -------------------------------------------------------------------------------- /criacao/factory_method/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Factory Method 3 | 4 | - Expõe um método ao cliente para criar os objetos. 5 | - Usa herança e subclasses para definir o objeto a ser criado. 6 | - O Factory Method é usado para criar um produto. 7 | 8 | ## Quando usar? 9 | 10 | Podemos usar o padrão factory method quando precisamos criar um mesmo objeto em diversos lugares do código, por exemplo, criar um perfil de usuário em diversas classes. 11 | 12 | ## Diferença entre o Factory Method e o Builder 13 | 14 | O builder nos auxilia na criação de um objeto mas estamos no controle, podemos criar um objeto de diversas formas. 15 | Já o padrão factory, nos dá um objeto pronto, com os parâmetros já definidos. -------------------------------------------------------------------------------- /criacao/factory_method/main.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class Section(metaclass=ABCMeta): 5 | @abstractmethod 6 | def describe(self): 7 | pass 8 | 9 | 10 | class PersonalSection(Section): 11 | def describe(self): 12 | print('Personal Section') 13 | 14 | 15 | class AlbumSection(Section): 16 | def describe(self): 17 | print('Album Section') 18 | 19 | 20 | class PatentSection(Section): 21 | def describe(self): 22 | print('Patent Section') 23 | 24 | 25 | class PublicationSection(Section): 26 | def describe(self): 27 | print('Publication Section') 28 | 29 | 30 | class Profile(metaclass=ABCMeta): 31 | def __init__(self): 32 | self.sections = list() 33 | self.create_profile() 34 | 35 | @abstractmethod 36 | def create_profile(self): 37 | pass 38 | 39 | def get_sections(self): 40 | return [type(s).__name__ for s in self.sections] 41 | 42 | def add_section(self, section): 43 | self.sections.append(section) 44 | 45 | 46 | class Linkedin(Profile): 47 | def create_profile(self): 48 | self.add_section(PersonalSection()) 49 | self.add_section(PatentSection()) 50 | self.add_section(PublicationSection()) 51 | 52 | 53 | class Facebook(Profile): 54 | def create_profile(self): 55 | self.add_section(PersonalSection()) 56 | self.add_section(AlbumSection()) 57 | 58 | 59 | if __name__ == '__main__': 60 | 61 | linkedin = Linkedin() 62 | facebook = Facebook() 63 | 64 | print('Creating Profile...', type(linkedin).__name__) 65 | print('Profile has sections --', linkedin.get_sections()) 66 | 67 | print('Creating Profile...', type(facebook).__name__) 68 | print('Profile has sections --', facebook.get_sections()) 69 | -------------------------------------------------------------------------------- /criacao/monostate/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Monostate (Borg) 3 | 4 | Proposto por Alex Martelli, o padrão _Monostate_ também conhecido como _Borg_ é uma variação do padrão [_Singleton_](../singleton). 5 | Alex sugere que os desenvolvedores devem se preocupar com o estado e o comportamento e não com a identidade do objeto. 6 | Este padrão permite criar mais de uma instância (objeto) de uma classe porém todos os objetos terão o mesmo estado, se comportando assim como um _Singleton_. 7 | 8 | ## Quando usar? 9 | 10 | Quando precisamos criar apenas um objeto de uma determinada classe. 11 | Um exemplo seria quando precisamos de apenas um objeto para conexão com o banco de dados. -------------------------------------------------------------------------------- /criacao/monostate/main.py: -------------------------------------------------------------------------------- 1 | class Monostate: 2 | 3 | __shared_state = {'1': '2'} 4 | 5 | def __init__(self): 6 | self.x = 1 7 | self.__dict__ = self.__shared_state 8 | 9 | 10 | if __name__ == '__main__': 11 | m1 = Monostate() 12 | m2 = Monostate() 13 | m1.x = 4 14 | print(m1) 15 | print(f'X value: {m1.x}') 16 | print(m2) 17 | print(f'X value: {m2.x}') 18 | -------------------------------------------------------------------------------- /criacao/prototype/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Prototype 3 | 4 | O padrão prototype é um padrão utilizado basicamente para clonar objetos. 5 | 6 | ## Quando usar? 7 | 8 | O padrão prototype permite copiar objetos existentes e modificar eles da maneira que desejar sem ter o trabalho de criar e configurar um objeto do princípio. 9 | 10 | Ele deve ser utilizado quando queremos um objeto exatamente igual ou quando o custo de criação de um objeto for grande. 11 | -------------------------------------------------------------------------------- /criacao/prototype/main.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | 4 | class Sheep: 5 | def __init__(self, name, category='Mountain Sheep'): 6 | self.name = name 7 | self.category = category 8 | 9 | 10 | if __name__ == '__main__': 11 | original_sheep = Sheep('Jolly') 12 | print(original_sheep.name) 13 | print(original_sheep.category) 14 | 15 | cloned_sheep = copy.deepcopy(original_sheep) 16 | cloned_sheep.name = 'Dolly' 17 | print(cloned_sheep.name) 18 | print(cloned_sheep.category) 19 | -------------------------------------------------------------------------------- /criacao/singleton/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Singleton 3 | 4 | O design pattern singleton é um dos padrões mais simples e possui um objetivo bem específico, garantir que apenas um objeto de uma determinada classe seja criado. 5 | 6 | ## Quando usar? 7 | 8 | Quando precisamos criar apenas um objeto de uma determinada classe. 9 | Um exemplo seria quando precisamos de apenas um objeto para conexão com o banco de dados. -------------------------------------------------------------------------------- /criacao/singleton/main.py: -------------------------------------------------------------------------------- 1 | class MetaSingleton(type): 2 | 3 | _instances = {} 4 | 5 | def __call__(cls, *args, **kwargs): 6 | if cls not in cls._instances: 7 | cls._instances[cls] = super(MetaSingleton, cls).__call__( 8 | *args, **kwargs 9 | ) 10 | return cls._instances[cls] 11 | 12 | 13 | class Logger(metaclass=MetaSingleton): 14 | def __init__(self, x): 15 | self.x = x 16 | 17 | 18 | class Singleton1: 19 | 20 | __instance = None 21 | 22 | def __new__(cls, nome): 23 | if Singleton1.__instance is None: 24 | Singleton1.__instance = object.__new__(cls) 25 | Singleton1.__instance.__nome = nome 26 | return Singleton1.__instance 27 | 28 | @property 29 | def nome(self): 30 | return self.__nome 31 | 32 | 33 | class Singleton2: 34 | def __new__(cls, nome): 35 | if not hasattr(cls, 'instance'): 36 | cls.instance = super(Singleton2, cls).__new__(cls) 37 | cls.instance.__nome = nome 38 | return cls.instance 39 | 40 | @property 41 | def nome(self): 42 | return self.__nome 43 | 44 | 45 | if __name__ == '__main__': 46 | 47 | foo = Singleton1('Maria') 48 | print(foo.nome) 49 | print(foo) 50 | 51 | bar = Singleton1('Joao') 52 | print(bar.nome) 53 | print(bar) 54 | 55 | print(foo is bar) 56 | 57 | foo = Singleton2('Maria') 58 | print(foo.nome) 59 | print(foo) 60 | 61 | bar = Singleton2('Joao') 62 | print(bar.nome) 63 | print(bar) 64 | 65 | print(foo is bar) 66 | 67 | # Example using metaclass 68 | logger1 = Logger(1) 69 | logger2 = Logger(2) 70 | 71 | print(logger1) 72 | print(logger1.x) 73 | print(logger2) 74 | print(logger2.x) 75 | -------------------------------------------------------------------------------- /estruturais/adapter/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Adapter 3 | 4 | O padrão adapter é um design pattern de software que permite a interface de uma classe existente ser utilizada como uma outra interface. 5 | 6 | Este padrão permite embrulhar algum objeto incompatível em um adaptador para torna-lo compatível com outras classes. 7 | 8 | ## Quando usar? 9 | 10 | Quando precisamos que uma classe existente funcione com outras classes sem modificar seu código fonte. 11 | 12 | ## Exemplo 13 | 14 | Suponha que temos um jogo onde existe um caçador de leões. 15 | 16 | Neste caso, temos uma classe base Lion que implementa o método rugido (roar) e é herdada pelas outras classes relacionadas a leões. 17 | 18 | Porém, agora temos um novo animal, um cão selvagem. Neste caso, o cão não tem rugido (roar) e sim latido (bark). Sendo assim, precisamos criar um adaptador que herde de Lion mas que chame o método latido em vez de rugido. 19 | 20 | Para facilitar o entendimento, veja o exemplo de código em Python. 21 | -------------------------------------------------------------------------------- /estruturais/adapter/main.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class Lion(metaclass=ABCMeta): 5 | @abstractmethod 6 | def roar(self): 7 | pass 8 | 9 | 10 | class AfricanLion(Lion): 11 | def roar(self): 12 | print('African Lion') 13 | 14 | 15 | class AsianLion(Lion): 16 | def roar(self): 17 | print('Asian Lion') 18 | 19 | 20 | class Hunter: 21 | def hunt(self, lion): 22 | lion.roar() 23 | 24 | 25 | class WildDog: 26 | def bark(self): 27 | print('Wild Dog') 28 | 29 | 30 | class WildDogAdapter(Lion): 31 | def __init__(self, wild_dog): 32 | self.__wild_dog = wild_dog 33 | 34 | def roar(self): 35 | self.__wild_dog.bark() 36 | 37 | 38 | if __name__ == '__main__': 39 | 40 | african_lion = AfricanLion() 41 | asian_lion = AsianLion() 42 | wild_dog = WildDog() 43 | 44 | wild_dog_adapter = WildDogAdapter(wild_dog) 45 | 46 | hunter = Hunter() 47 | hunter.hunt(african_lion) 48 | hunter.hunt(asian_lion) 49 | hunter.hunt(wild_dog_adapter) 50 | -------------------------------------------------------------------------------- /estruturais/bridge/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Bridge 3 | 4 | O padrão bridge preza pela composição em vez da herança. O objetivo desse padrão é dissociar uma abstração de sua implementação para que os dois possam variar independentemente. 5 | 6 | ## Quando usar? 7 | 8 | Para entender quando devemos utilizar o padrão bridge, vejamos o seguinte exemplo (implementado no código): 9 | 10 | Suponha que temos um site com diferentes páginas e você deve proporcionar ao usuário a opção de mudar o tema do site. Uma ideia básica seria criar várias cópias de cada página com cada opção de tema, mas assim as duas hierarquias ficam correlacionadas (as páginas e os temas) e se um ou outro for alterado será preciso atualizar em todos os lugares. 11 | 12 | O padrão `bridge` nos permite resolver esse problema com certa elegância. Podemos criar hierarquias independentes para as páginas e para os temas e então simplesmente compor elas, neste caso, carregando o tema para cada página. 13 | -------------------------------------------------------------------------------- /estruturais/bridge/main.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class Theme(metaclass=ABCMeta): 5 | @abstractmethod 6 | def color(self): 7 | pass 8 | 9 | 10 | class DarkTheme(Theme): 11 | def color(self): 12 | return 'Dark Black' 13 | 14 | 15 | class LightTheme(Theme): 16 | def color(self): 17 | return 'Off White' 18 | 19 | 20 | class AquaTheme(Theme): 21 | def color(self): 22 | return 'Light Blue' 23 | 24 | 25 | class WebPage(metaclass=ABCMeta): 26 | @abstractmethod 27 | def content(self): 28 | pass 29 | 30 | 31 | class About(WebPage): 32 | def __init__(self, theme): 33 | self.__theme = theme 34 | 35 | def content(self): 36 | return f'About page in {self.__theme.color()}' 37 | 38 | 39 | class Careers(WebPage): 40 | def __init__(self, theme): 41 | self.__theme = theme 42 | 43 | def content(self): 44 | return f'Careers page in {self.__theme.color()}' 45 | 46 | 47 | if __name__ == '__main__': 48 | dark_theme = DarkTheme() 49 | 50 | about = About(dark_theme) 51 | careers = Careers(dark_theme) 52 | 53 | print(about.content()) 54 | print(careers.content()) 55 | -------------------------------------------------------------------------------- /estruturais/composite/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Composite 3 | 4 | O padrão `composite` permite tratar objetos individuais de forma uniforme. Ele basicamente descreve que um grupo de objetos devem ser tratados da mesma forma que uma instância única de um objeto. 5 | 6 | O propósito do padrão `composite` é compor objetos em estruturas de árvores que representam hierarquias. 7 | 8 | # Quando usar? 9 | 10 | Todas as organizações são compostas por colaboradores. Cada colaborador possui as mesmas características como salário, responsabilidades, pode ou não ter algum superior, pode ou não ter subordinados. 11 | -------------------------------------------------------------------------------- /estruturais/composite/main.py: -------------------------------------------------------------------------------- 1 | class Colaborador: 2 | def __init__(self, nome, salario): 3 | self.__nome = nome 4 | self.salario = salario 5 | 6 | @property 7 | def nome(self): 8 | return self.__nome 9 | 10 | 11 | class Desenvolvedor(Colaborador): 12 | def __init__(self, nome, salario, outro_parametro=None): 13 | super(Desenvolvedor, self).__init__(nome, salario) 14 | self.__outro_parametro = outro_parametro 15 | 16 | @property 17 | def outro_parametro(self): 18 | return self.__outro_parametro 19 | 20 | 21 | class Designer(Colaborador): 22 | def __init__(self, nome, salario, mais_um_parametro=None): 23 | super(Designer, self).__init__(nome, salario) 24 | self.__mais_um_parametro = mais_um_parametro 25 | 26 | @property 27 | def mais_um_parametro(self): 28 | return self.__mais_um_parametro 29 | 30 | 31 | class Organizacao: 32 | def __init__(self): 33 | self.__colaboradores = list() 34 | 35 | def add_colaborador(self, colaborador): 36 | self.__colaboradores.append(colaborador) 37 | 38 | def total_salarios(self): 39 | salarios = 0 40 | for colaborador in self.__colaboradores: 41 | salarios += colaborador.salario 42 | return salarios 43 | 44 | 45 | if __name__ == '__main__': 46 | joao = Desenvolvedor('Joao da Silva', 1800) 47 | carla = Designer('Carla Camila', 1900) 48 | 49 | organizacao = Organizacao() 50 | organizacao.add_colaborador(joao) 51 | organizacao.add_colaborador(carla) 52 | 53 | print(f'Total salários: {organizacao.total_salarios()}') 54 | -------------------------------------------------------------------------------- /estruturais/decorator/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Decorator 3 | 4 | Ao utilizar o design pattern decorator, podemos compor/decorar os parâmetros de forma dinâmica. Neste exemplo, podemos compor múltiplos impostos em um mesmo orçamento apenas passando um imposto para o construtor do anterior. 5 | 6 | ## Quando usar? 7 | 8 | > Sempre que percebemos que temos comportamentos que podem ser formados por comportamentos de outras classes envolvidas em uma mesma hierarquia, como foi o caso dos impostos, que podem ser composto por outros impostos. O Decorator introduz a flexibilidade na composição desses comportamentos, bastando escolher no momento da instanciação, quais instancias serão utilizadas para realizar o trabalho. 9 | 10 | ## Decorator do Python 11 | 12 | Ao utilizar o recurso de decorator da linguagem Python, podemos chamar outra função ou método (neste caso o wrapper do IPVX) antes de executar uma função ou método, e assim compor os resultados. 13 | 14 | ```python 15 | def IPVX(metodo_ou_funcao): 16 | # Chama o calculo do imposto ISS, pega o resultado e soma com R$ 50,00 17 | # O wrapper eh chamado antes do calcula do ISS 18 | # Recebe o self do ISS, onde o decorator esta aplicado 19 | # metodo_ou_funcao sera o metodo calcula 20 | def wrapper(self, orcamento): 21 | return metodo_ou_funcao(self, orcamento) + 50.0 22 | return wrapper 23 | 24 | 25 | class ISS(Imposto): 26 | 27 | @IPVX 28 | def calcula(self, orcamento): 29 | 30 | return orcamento.valor * 0.1 + self.calculo_do_outro_imposto(orcamento) 31 | ``` 32 | 33 | A diferença entre o design pattern decorator e o decorator do Python é que o decorator do Python fica "fixo" no código, já o design pattern decorator, podemos compor os decorators em tempo de execução. 34 | -------------------------------------------------------------------------------- /estruturais/decorator/impostos.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class Imposto: 5 | def __init__(self, outro_imposto=None): 6 | self.__outro_imposto = outro_imposto 7 | 8 | def calculo_do_outro_imposto(self, orcamento): 9 | if self.__outro_imposto is not None: 10 | return self.__outro_imposto.calcula(orcamento) 11 | return 0 12 | 13 | @abstractmethod 14 | def calcula(self, orcamento): 15 | pass 16 | 17 | 18 | class TemplateImpostosCondicional(Imposto, metaclass=ABCMeta): 19 | def calcula(self, orcamento): 20 | if self.deve_usar_maxima_taxacao(orcamento): 21 | return self.maxima_taxacao( 22 | orcamento 23 | ) + self.calculo_do_outro_imposto(orcamento) 24 | else: 25 | return self.minima_taxacao( 26 | orcamento 27 | ) + self.calculo_do_outro_imposto(orcamento) 28 | 29 | @abstractmethod 30 | def deve_usar_maxima_taxacao(self, orcamento): 31 | pass 32 | 33 | @abstractmethod 34 | def maxima_taxacao(self, orcamento): 35 | pass 36 | 37 | @abstractmethod 38 | def minima_taxacao(self, orcamento): 39 | pass 40 | 41 | 42 | def IPVX(metodo_ou_funcao): 43 | # Chama o calculo do imposto ISS, pega o resultado e soma com R$ 50,00 44 | # O wrapper eh chamado antes do calcula do ISS 45 | # Recebe o self do ISS, onde o decorator esta aplicado 46 | # metodo_ou_funcao sera o metodo calcula 47 | def wrapper(self, orcamento): 48 | return metodo_ou_funcao(self, orcamento) + 50.0 49 | 50 | return wrapper 51 | 52 | 53 | class ISS(Imposto): 54 | @IPVX 55 | def calcula(self, orcamento): 56 | return orcamento.valor * 0.1 + self.calculo_do_outro_imposto(orcamento) 57 | 58 | 59 | class ICMS(Imposto): 60 | def calcula(self, orcamento): 61 | return orcamento.valor * 0.06 + self.calculo_do_outro_imposto( 62 | orcamento 63 | ) 64 | 65 | 66 | class ICPP(TemplateImpostosCondicional): 67 | def deve_usar_maxima_taxacao(self, orcamento): 68 | return orcamento.valor > 500 69 | 70 | def maxima_taxacao(self, orcamento): 71 | return orcamento.valor * 0.07 72 | 73 | def minima_taxacao(self, orcamento): 74 | return orcamento.valor * 0.05 75 | 76 | 77 | class IKCV(TemplateImpostosCondicional): 78 | def __tem_item_maior_que_cem_reais(self, orcamento): 79 | for item in orcamento.obter_itens(): 80 | if item.valor > 100: 81 | return True 82 | return False 83 | 84 | def deve_usar_maxima_taxacao(self, orcamento): 85 | return orcamento.valor > 500 and self.__tem_item_maior_que_cem_reais( 86 | orcamento 87 | ) 88 | 89 | def maxima_taxacao(self, orcamento): 90 | return orcamento.valor * 0.1 91 | 92 | def minima_taxacao(self, orcamento): 93 | return orcamento.valor * 0.06 94 | -------------------------------------------------------------------------------- /estruturais/decorator/main.py: -------------------------------------------------------------------------------- 1 | from impostos import ICMS, ICPP, IKCV, ISS 2 | 3 | 4 | class CalculadorImpostos: 5 | def realiza_calculo(self, orcamento, imposto): 6 | imposto_calculado = imposto.calcula(orcamento) 7 | print(imposto_calculado) 8 | 9 | 10 | if __name__ == '__main__': 11 | 12 | from orcamento import Orcamento, Item 13 | 14 | orcamento = Orcamento() 15 | 16 | orcamento.adiciona_item(Item('item 0', 50.0)) 17 | orcamento.adiciona_item(Item('item 1', 200.0)) 18 | orcamento.adiciona_item(Item('item 2', 250.0)) 19 | 20 | calculador = CalculadorImpostos() 21 | 22 | print('ISS e ICMS') 23 | calculador.realiza_calculo(orcamento, ISS()) 24 | calculador.realiza_calculo(orcamento, ICMS()) 25 | 26 | print('ISS com ICMS') 27 | calculador.realiza_calculo(orcamento, ISS(ICMS())) 28 | 29 | print('ICPP e IKCV') 30 | calculador.realiza_calculo(orcamento, ICPP()) 31 | calculador.realiza_calculo(orcamento, IKCV()) 32 | 33 | print('ICPP com IKCV') 34 | calculador.realiza_calculo(orcamento, ICPP(IKCV())) 35 | -------------------------------------------------------------------------------- /estruturais/decorator/orcamento.py: -------------------------------------------------------------------------------- 1 | class Orcamento: 2 | def __init__(self): 3 | self.__itens = list() 4 | 5 | @property 6 | def valor(self): 7 | return sum([item.valor for item in self.__itens]) 8 | 9 | def obter_itens(self): 10 | return tuple(self.__itens) 11 | 12 | @property 13 | def total_itens(self): 14 | return len(self.__itens) 15 | 16 | def adiciona_item(self, item): 17 | self.__itens.append(item) 18 | 19 | 20 | class Item: 21 | def __init__(self, nome, valor): 22 | self.__nome = nome 23 | self.__valor = valor 24 | 25 | @property 26 | def valor(self): 27 | return self.__valor 28 | 29 | @property 30 | def nome(self): 31 | return self.__nome 32 | -------------------------------------------------------------------------------- /estruturais/facade/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Facade 3 | 4 | O padrão Facade (Façade) oferece uma interface unificada para um conjunto de interfaces em um subsistema e define uma interface de alto nível que ajuda o cliente a usar o subsistema de forma fácil. 5 | 6 | Este padrão promove o desacoplamento da implementação com vários clientes. 7 | 8 | ## Quando usar? 9 | 10 | Quando queremos abstrair a complexidade de subsistemas, criando uma interface unificada que nos permite acessá-los. 11 | -------------------------------------------------------------------------------- /estruturais/facade/main.py: -------------------------------------------------------------------------------- 1 | class Hotelier: 2 | def __init__(self): 3 | print('Arranging the Hotel for Marriage? --') 4 | 5 | def __is_available(self): 6 | print('Is the Hotel free for the event on given day?') 7 | return True 8 | 9 | def book_hotel(self): 10 | if self.__is_available(): 11 | print('Registered the Booking\n') 12 | 13 | 14 | class Florist: 15 | def __init__(self): 16 | print('Flowers decoration for the event? --') 17 | 18 | def set_flower_requirements(self): 19 | print('Carnations, Roses and Lilies would be used for decorations\n') 20 | 21 | 22 | class Caterer: 23 | def __init__(self): 24 | print('Food Arrangements for the event? --') 25 | 26 | def set_cuisine(self): 27 | print('Chinese & Continental Cuisine to be served\n') 28 | 29 | 30 | class Musician: 31 | def __init__(self): 32 | print('Musical arrangements for the marriage? --') 33 | 34 | def set_music_type(self): 35 | print('Jazz and Classical will be played\n') 36 | 37 | 38 | class EventManager: 39 | def __init__(self): 40 | print('Event Manager:: Let me talk to the folks\n') 41 | 42 | @staticmethod 43 | def arrange(): 44 | hotelier = Hotelier() 45 | hotelier.book_hotel() 46 | 47 | florist = Florist() 48 | florist.set_flower_requirements() 49 | 50 | caterer = Caterer() 51 | caterer.set_cuisine() 52 | 53 | musician = Musician() 54 | musician.set_music_type() 55 | 56 | 57 | class Client: 58 | def __init__(self): 59 | print('Client:: Whoa! Marriage Arrangements???!!!') 60 | 61 | def ask_event_manager(self): 62 | print("Client:: Let's contact the event manager\n") 63 | em = EventManager() 64 | em.arrange() 65 | 66 | def __del__(self): 67 | print('Client:: Thanks to Event Manager, all preparations done!') 68 | 69 | 70 | if __name__ == '__main__': 71 | client = Client() 72 | client.ask_event_manager() 73 | -------------------------------------------------------------------------------- /estruturais/flyweight/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Flyweight 3 | 4 | O `flyweight` é um padrão que tem como objetivo minimizar o uso de memória ou custos computacionais compartilhando a maior quantidade de dados entre objetos similares. 5 | 6 | ## Quando usar? 7 | 8 | Quando queremos minimizar o uso de recursos computacionais, compartilhando os dados ou os objetos. 9 | Considere uma loja que vende chás. A pessoa responsável por fazer o chá geralmente faz mais do que uma xícara de chá e guarda o resto para vender para outros consumidores, poupando assim recursos como gás, entre outros. O padrão `flyweight` é exatamente sobre isso, poupar recursos. 10 | -------------------------------------------------------------------------------- /estruturais/flyweight/main.py: -------------------------------------------------------------------------------- 1 | class KarakTea: 2 | def __init__(self, tea_type): 3 | self.__tea_type = tea_type 4 | 5 | @property 6 | def tea_type(self): 7 | return self.__tea_type 8 | 9 | 10 | class TeaMaker: 11 | def __init__(self): 12 | self.__available_tea = dict() 13 | 14 | def make(self, preference): 15 | if preference not in self.__available_tea: 16 | self.__available_tea[preference] = KarakTea(preference) 17 | return self.__available_tea[preference] 18 | 19 | 20 | class TeaShop: 21 | def __init__(self, tea_maker): 22 | self.__orders = dict() 23 | self.__tea_maker = tea_maker 24 | 25 | def take_order(self, tea_type, table): 26 | if table not in self.__orders: 27 | self.__orders[table] = list() 28 | self.__orders[table].append(self.__tea_maker.make(tea_type)) 29 | 30 | def serve(self): 31 | for table, orders in self.__orders.items(): 32 | print('Serving tea to table {}'.format(table)) 33 | 34 | 35 | if __name__ == '__main__': 36 | tea_maker = TeaMaker() 37 | shop = TeaShop(tea_maker) 38 | 39 | shop.take_order('red tea', 1) 40 | shop.take_order('red tea more sugar', 2) 41 | shop.take_order('red tea more milk', 3) 42 | 43 | shop.serve() 44 | -------------------------------------------------------------------------------- /estruturais/proxy/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Proxy 3 | 4 | O Proxy serve de intermediário entre o solicitante (_seeker_) e o provedor (_provider_). O solicitante é quem faz a requisição e o provedor entrega os recursos em resposta à requisição. 5 | 6 | O Proxy é basicamente um _wrapper_ ou um objeto agente que encapsula o objeto que está realmente servindo. 7 | 8 | ## Quando usar? 9 | 10 | Quando possuímos procedimentos complexos e precisamos fornecer uma interface simples para eles. Também pode ser utilizado para fornecer uma camada de segurança aos objetos. Pode também oferecer uma interface local para objetos remotos em servidores diferentes. 11 | 12 | ## Diferença entre o Proxy e o Facade 13 | 14 | | Proxy | Facade | 15 | |:--------------------------------------------------------------------------------------------:|:------------------------------------------------------------:| 16 | | Oferece um substituto ou um placeholder para outro objeto a fim de controlar o acesso a ele. | Oferece uma interface para subsistemas grande de classes. | 17 | | Um objeto Proxy tem a mesma interface do objeto-alvo e armazena referências a esses objetos. | Minimiza a comunicação e as dependências entre subsistemas. | 18 | | Atua como um intermediário entre o cliente e o objeto encapsulado. | Um objeto Facade oferece uma interface única e simplificada. | -------------------------------------------------------------------------------- /estruturais/proxy/main.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class Payment(metaclass=ABCMeta): 5 | @abstractmethod 6 | def do_pay(self): 7 | pass 8 | 9 | 10 | class Bank(Payment): 11 | def __init__(self): 12 | self.card = None 13 | self.account = None 14 | 15 | def __get_account(self): 16 | self.account = self.card 17 | 18 | return self.account 19 | 20 | def __has_founds(self): 21 | print( 22 | f'Bank:: Check if Account {self.__get_account()} has enough funds' 23 | ) 24 | return True 25 | 26 | def set_card(self, card): 27 | self.card = card 28 | 29 | def do_pay(self): 30 | if self.__has_founds(): 31 | print('Bank:: Paying the merchant') 32 | return True 33 | else: 34 | print('Bank:: Sorry, not enough funds') 35 | return False 36 | 37 | 38 | class DebitCard(Payment): 39 | def __init__(self): 40 | self.bank = Bank() 41 | 42 | def do_pay(self): 43 | card = eval(input('Proxy:: Punch in Card Number: ')) 44 | self.bank.set_card(card) 45 | return self.bank.do_pay() 46 | 47 | 48 | class You: 49 | def __init__(self): 50 | print('You:: Lets buy the Denim shirt!') 51 | self.debit_card = DebitCard() 52 | self.is_purchased = None 53 | 54 | def make_payment(self): 55 | self.is_purchased = self.debit_card.do_pay() 56 | 57 | def __del__(self): 58 | if self.is_purchased: 59 | print('You:: Wow! Denim shirt is mine :-)') 60 | else: 61 | print('You:: I should earn more :(') 62 | 63 | 64 | if __name__ == '__main__': 65 | you = You() 66 | you.make_payment() 67 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | flake8==3.7.9 2 | black==24.3.0 3 | isort==4.3.21 4 | unify==0.5 5 | --------------------------------------------------------------------------------