Design Orientado a Objetos
Visão Geral
Este pacote contém regras de design para bases de código escritas em estilo orientado a objetos, não conselhos universais. As convenções escritas do próprio projeto prevalecem sobre o pacote. Siga o idioma do módulo que está sendo modificado. Este skill dimensiona a tarefa e nomeia o skill que se encaixa.
Carregue o skill que este índice nomeia antes de responder: suas regras e exemplos estão lá, não aqui.
Quanto design uma tarefa justifica
| Tarefa | Usar | Ignorar |
|---|---|---|
| Uma resposta usada uma vez e depois descartada | Nada | Tudo |
| Um método em uma classe que existe | Seu nome e seu contrato | Remodelação |
| Uma funcionalidade dentro de um fluxo existente | O idioma que aquele fluxo usa | Novos limites |
| Uma nova classe ou módulo | O loop de design | Nada |
| Um novo contexto | O loop, depois seus limites | Nada |
| Um novo sistema do zero | O loop, contextos, depois estrutura | Nada |
Antes de projetar
Antes de projetar, declare o portão: uma regra que o software deve seguir, com um exemplo que ela aceita e um que rejeita. Por exemplo, "Um slot pode conter no máximo um agendamento." Ela aceita agendar um segundo paciente em um slot diferente e rejeita agendar um segundo paciente no mesmo slot. Se você consegue escrever a regra, comece. Se não consegue, o que falta não é design, e este pacote não pode fornecê-lo.
O loop de design
- Diga o que o software deve fazer nas palavras do domínio (falando a linguagem do domínio), e encontre o objeto que sabe o que cada comportamento precisa (atribuindo responsabilidades).
- Encontre o limite de consistência: o que deve ser verdadeiro junto em uma transação (projetando agregados).
- Decida onde cada regra vive: uma entidade, um objeto de valor ou um serviço de domínio (posicionando a lógica de domínio).
- Projete as mensagens que cada objeto responde (encapsulando estado e comportamento), e torne estados ilegais impossíveis de construir (tornando estados ilegais impossíveis).
- Inverta as dependências para que o domínio não saiba nada sobre o banco de dados ou o framework (invertendo dependências, em camadas no sistema).
- Nomeie as coisas nas palavras do domínio, e comente apenas o que o código não pode dizer (nomeando classes e métodos, comentando o que o código não pode dizer).
- Verifique o resultado contra as bandeiras vermelhas abaixo (revisando design de objetos).
Quão estritamente uma regra é aplicada
Toda regra principal neste pacote carrega um de três rótulos, e o rótulo diz quão estritamente ela é aplicada.
- Regra. Quebrá-la é um defeito. Nenhum framework a desculpa.
- Padrão. Faça isso a menos que uma condição declarada se aplique. A condição é sua própria frase, nunca uma cláusula final.
- Julgamento. Duas respostas são defensíveis. O skill diz o que inclina a balança.
Sintomas para skills
Sintomas apontam para o skill que os trata, e esta tabela cresce à medida que skills são adicionados.
| Sintoma | Skill |
|---|---|
| Qual classe deve fazer isso? | Atribuindo responsabilidades |
| Esta classe faz demais | Atribuindo responsabilidades |
| Uma mudança se propaga por muitas classes | Diagnosticando acoplamento |
| Quão fortemente essas duas partes estão ligadas? | Diagnosticando acoplamento |
| Isto é um objeto ou apenas dados? | Decidindo objeto ou dados |
| Nosso modelo de domínio é anêmico | Decidindo objeto ou dados |
| Outro código decide por este objeto | Encapsulando estado e comportamento |
| Setters permitem que este objeto quebre suas regras | Encapsulando estado e comportamento |
| Este objeto pode existir meio construído | Construindo objetos válidos |
| Este construtor recebe muitos argumentos | Construindo objetos válidos |
| Cada novo caso significa editar código funcional | Encapsulando o que varia |
| Quanta flexibilidade isto deve ter? | Encapsulando o que varia |
| Isto é um valor ou uma entidade? | Modelando valores e entidades |
| Strings e números brutos carregam regras de domínio | Modelando valores e entidades |
| Isto deve estender aquela classe ou mantê-la? | Escolhendo composição sobre herança |
| Mudar uma classe base quebra suas subclasses | Escolhendo composição sobre herança |
| Uma subclasse quebra o que seu pai promete | Honrando substituibilidade |
| Código verifica qual subtipo ele contém | Honrando substituibilidade |
| O mesmo switch em um tipo continua voltando | Substituindo condicionais por polimorfismo |
| Subclasses ou pattern matching? | Substituindo condicionais por polimorfismo |
| Código atravessa uma cadeia de objetos | Limitando o que um objeto sabe |
| Esta classe apenas encaminha chamadas | Limitando o que um objeto sabe |
| Como esta classe ou método deve ser chamado? | Nomeando classes e métodos |
| Nomes como Manager, Helper ou Util | Nomeando classes e métodos |
| Chamadores dependem de métodos de interface que nunca usam | Projetando interfaces |
| Interface ou classe base abstrata? | Projetando interfaces |
| Código de domínio depende de um banco de dados ou framework | Invertendo dependências |
| Onde os objetos são criados e conectados? | Invertendo dependências |
| Chamadores sabem como outra classe armazena ou formata dados | Escondendo implementação |
| Uma classe vaza seus erros internos ou etapas | Escondendo implementação |
| Flags ou campos opcionais permitem combinações impossíveis | Tornando estados ilegais impossíveis |
| Um tipo admite valores que o domínio nunca tem | Tornando estados ilegais impossíveis |
| As palavras do código diferem das da equipe | Falando a linguagem do domínio |
| Uma palavra significa coisas diferentes para duas equipes | Falando a linguagem do domínio |
| Quais objetos devem mudar juntos? | Projetando agregados |
| Salvamentos colidem ou uma carga puxa demais | Projetando agregados |
| Quais mudanças de status são permitidas? | Modelando ciclos de vida |
| Um objeto cancelado ou fechado muda novamente | Modelando ciclos de vida |
| Como os objetos de domínio devem ser armazenados e encontrados? | Projetando repositórios |
| O repositório tem um método find por tela | Projetando repositórios |
| Casos de uso repetem a mesma verificação de negócio | Posicionando a lógica de domínio |
| Serviço de domínio, método de entidade ou serviço de aplicação? | Posicionando a lógica de domínio |
| Outras partes devem reagir após uma mudança ser salva | Publicando eventos de domínio |
| Uma mensagem saiu para uma mudança que foi desfeita | Publicando eventos de domínio |
| Onde termina o modelo de uma equipe e começa o de outra? | Delimitando contextos |
| Quais partes são centrais e quais podemos comprar? | Delimitando contextos |
| Em qual direção as camadas devem depender? | Em camadas no sistema |
| Uma tela ou job não pode ser testado sem seu framework | Em camadas no sistema |
| Módulos dependem uns dos outros em um ciclo | Organizando módulos |
| Monólito modular ou serviços separados? | Organizando módulos |
| Uma mudança na nossa API quebra os aplicativos que a chamam | Cruzando limites do sistema |
| Como devemos versionar esta API ou mensagem? | Cruzando limites do sistema |
| A ferramenta de mapeamento molda nossas classes de domínio | Domando persistência |
| Uma lista executa uma consulta por linha | Domando persistência |
| Telas precisam de dados moldados de forma diferente do modelo | Separando leituras de escritas |
| Modelo de leitura, projeção ou event sourcing? | Separando leituras de escritas |
| MVC, MVP ou MVVM para esta tela? | Separando apresentação do domínio |
| Uma tela edita objetos de domínio diretamente | Separando apresentação do domínio |
| Qual padrão de design se encaixa neste problema? | Escolhendo um padrão |
| Um padrão foi adicionado antes do seu problema | Escolhendo um padrão |
| Estas classes de padrão poderiam ser uma função | Padrões que a linguagem fornece |
| Visitor ou um conjunto fechado com matching? | Padrões que a linguagem fornece |
| Um método retorna null quando não encontra nada | Projetando comportamento de falha |
| Recusas retornam como códigos que os chamadores ignoram | Projetando comportamento de falha |
| Threads compartilham um objeto e perdem ou misturam suas mudanças | Gerenciando estado compartilhado |
| Um lock ou um único dono para este estado? | Gerenciando estado compartilhado |
| Uma chamada para outro sistema trava ou falha no meio | Tornando o trabalho confiável |
| Uma tentativa fez o trabalho duas vezes | Tornando o trabalho confiável |
| Não conseguimos explicar um incidente a partir de nossos registros | Projetando o que registrar |
| O que devemos logar, contar ou auditar? | Projetando o que registrar |
| Uma revisão encontrou uma god class ou fluxo de lava | Reconhecendo antipadrões |
| Qual antipadrão devemos corrigir primeiro? | Reconhecendo antipadrões |
| Testes quebram embora o comportamento não tenha mudado | Testando código orientado a objetos |
| Mock, stub ou fake para este colaborador? | Testando código orientado a objetos |
| Testes dependem do tempo ou de valores aleatórios | Projetando para testabilidade |
| Código é tornado público apenas para que testes possam alcançá-lo | Projetando para testabilidade |
| Como reestruturamos este código com segurança? | Refatorando em direção ao design de objetos |
| Uma refatoração cresceu demais para terminar | Refatorando em direção ao design de objetos |
| Precisamos alterar código que não tem testes | Trabalhando com código legado |
| Substituir um sistema antigo peça por peça | Trabalhando com código legado |
| O que esta revisão deve verificar? | Revisando design de objetos |
| Quais descobertas da revisão bloqueiam um merge? | Revisando design de objetos |
| Ninguém lembra por que o código tem esta forma | Documentando decisões de design |
| O que devemos desenhar, e em que nível? | Documentando decisões de design |
| Comentários repetem o código ou o contradizem | Comentando o que o código não pode dizer |
| Onde explicamos por que este código parece estranho? | Comentando o que o código não pode dizer |
| Um hook ou marcador de framework contém uma regra de negócio | Trabalhando dentro de um framework |
| Até onde devemos seguir o framework? | Trabalhando dentro de um framework |
| Um cálculo também salva, envia ou muda suas entradas | Aplicando técnicas funcionais |
| Quais técnicas funcionais se encaixam em código de objeto? | Aplicando técnicas funcionais |
Skills de linguagem
Um skill de linguagem mantém as regras principais em uma linguagem e nunca as substitui. Um skill de linguagem base é para código sem framework.
oo-java, deobject-oriented-design-java: records, sealed types,equalsehashCode, exceções verificadas,Optionale classes fechadas para extensão.oo-kotlin, deobject-oriented-design-kotlin: data classes, sealed types, tipos anuláveis, delegação combye funções de extensão.oo-typescript, deobject-oriented-design-typescript: identificadores marcados, estado#private, classes abstratas, decorators, uniões discriminadas e uma raiz de composição.oo-javascript, deobject-oriented-design-javascript: c
…
[truncado para tradução]