Slide 1

Slide 1 text

WORKSHOP DE QUALIDADE Qualidade de Código: Um resumo prático do que todo dev precisa saber

Slide 2

Slide 2 text

AVISOS Não dá pra cobrir tudo "Resumo prático" Qualidade envolve bastante teoria Experiência vem com o tempo

Slide 3

Slide 3 text

AVISOS Não existe apenas um jeito certo A prática vai te mostrar o seu jeito! Diferente é bom! Faz pensar!

Slide 4

Slide 4 text

AGENDA MANHÃ 1. Resumão do Clean Code 2. Object Calisthenics 3. PHP Defensivo

Slide 5

Slide 5 text

AGENDA TARDE 1. Panorama sobre Arquitetura de So ware 2. TDD raiz na prática 3. SOLID com outros olhos

Slide 6

Slide 6 text

FORMATO Interrompa quando quiser Contribua se puder Pequena pausa de 10 a 15 min entre temas

Slide 7

Slide 7 text

CONSIDERAÇÕES Workshop em guias/temas auto-explicativos Temas que mudaram a minha visão Garantia Vitalícia da Camaradagem

Slide 8

Slide 8 text

PARTIU!

Slide 9

Slide 9 text

CLEAN CODE resumão pra quem comprou mas não leu o livro!

Slide 10

Slide 10 text

LIVRO Clean Code A Handbook of Agile So ware Cra smanship Robert C. Martin (Uncle Bob)

Slide 11

Slide 11 text

AGENDA Resumo dos principais tópicos Não dá pra cobrir tudo Focado em PHP quando possível! Alguns detalhes vão ser explicados mais à frente

Slide 12

Slide 12 text

CAPÍTULO 1 Clean Code "You are reading this book for two reasons. First, you are a programmer. Second, you want to be a better programmer. Good. We need better programmers."

Slide 13

Slide 13 text

(bootstrap time!)

Slide 14

Slide 14 text

"CODE-SENSE" "A programmer without "code-sense" can look at a messy module and recognize the mess but will have no idea what to do about it. A programmer with "code-sense" will look at a messy module and see options and variations." Uncle Bob

Slide 15

Slide 15 text

SEGUNDO UNCLE BOB: Escrever código limpo requer disciplina O "code-sense" é a chave! Alguns de nós nascem com o "code-sense" Outros só com a prática!

Slide 16

Slide 16 text

ALGUNS PONTOS Passamos mais tempo lendo código Sucesso relacionado com o entendimento (o entendimento diminui ao longo do tempo)

Slide 17

Slide 17 text

CAPÍTULO 2 Meaningful Names "Names are everywhere in so ware. We name our variables, our functions, our arguments, classes, and packages. We name our source files and the directories that contain them. We name and name and name."

Slide 18

Slide 18 text

EXPRESSE INTENÇÃO AO DAR NOMES Use nomes pronunciáveis / Evite abreviações

Slide 19

Slide 19 text

class DtaRcrd102 { private DateTime $genymdhsm; private DateTime $modymdhms; private string $pszqint = "102"; } class Customer { private DateTime $generationTimestamp; private DateTime $modificarionTimestamp; private string $recordId = "102"; }

Slide 20

Slide 20 text

NOMES "BUSCÁVEIS" / CONSTANTES for ($i = 0; $i < 5; $i++) { // ... } for ($i = 0; $i < WORK_DAYS_PER_WEEK; $i++) { // ... }

Slide 21

Slide 21 text

EVITE PREFIXOS/SUFIXOS DESNECESSÁRIOS (As pessoas geralmente "aprendem" a ignorar prefixos e sufixos muito rapidamente.) $txtFirstName = $controller­>request()­>get('txt_FirstName');

Slide 22

Slide 22 text

INTERFACES E IMPLEMENTAÇÕES Evitar informação demais pro usuário (dev) (estou passando uma interface ou classe concreta?) PSR Naming Conventions | Symfony UserInterface, AbstractResolver, ReusableTrait

Slide 23

Slide 23 text

EVITE O "MAPA MENTAL" Não obrigue o usuário a tentar entender o que está escrito Buscar entender pelo contexto é muito mais demorado!

Slide 24

Slide 24 text

NOMES DE CLASSES "Classes and objects should have noun or noun phrase names like Customer, WikiPage, Account, and AddressParser. Avoid words like Manager, Processor, Data, or Info in the name of a class. A class name should not be a verb." (resumindo: nome de classe = substantivo)

Slide 25

Slide 25 text

NOMES DE MÉTODOS Devem ser verbos Utilize Named Constructors (mais significado) Accessors (get), Mutators (set) e predicates (is)

Slide 26

Slide 26 text

UMA PALAVRA POR CONCEITO Nunca misture palavras de conceitos similares fetch, retrieve, get DeviceController, DeviceManager

Slide 27

Slide 27 text

USE NOMES PRESENTES EM PADRÕES Todo dev sabe (ou deveria) saber Design Patterns Facilita o entendimento sobre a solução implementada AccountVisitor, ConsoleDecorator, UserFactory

Slide 28

Slide 28 text

NÃO ADICIONE CONTEXTO DESNECESSÁRIO Contexto deve ficar no namespace Nome de classe deve resumir o que ela faz GSDAMailingAddress, GSDAAccountAddress

Slide 29

Slide 29 text

CAPÍTULO 3 Functions "In the early days of programming we composed our systems of routines and subroutines. Nowadays only the functions survives from those early days. Functions are the first line of organization in a any program."

Slide 30

Slide 30 text

FUNÇÕES/MÉTODOS PEQUENOS Máximo 20 linhas. Se possível menos. Máximo 120 caracteres por linha. Se possível 80.

Slide 31

Slide 31 text

BLOCOS E INDENTAÇÃO Cada bloco de uma linha (chamada de método) Métodos com 1 ou 2 níveis de indentação Muito mais fáceis de entender

Slide 32

Slide 32 text

FAÇA UMA COISA Devem fazer apenas uma coisa (e fazer bem) Nomes muito grande podem necessitar ser refatorados Palavras de alerta: And, Or, If

Slide 33

Slide 33 text

"STEPDOWN RULE" Leitura fluente como texto if (!$user = $this­>findUser()) { // ... } $this­>save($user); $user = $this­>findUser(); if (!$user) { // ... } $this­>save($user);

Slide 34

Slide 34 text

private function something(): bool { return !$this­>foo() || $this­>bar(); } private function something(): bool { if (!$this­>foo()) { return false; } if ($this­>bar()) { return true; } return false; }

Slide 35

Slide 35 text

switch Tolerados se aparecerem apenas uma vez Pensar em AbstractFactory se mais de uma vez Cuidado para não ferir SRP

Slide 36

Slide 36 text

USE NOMES DESCRITIVOS Word's principle: "You know you are working on clean code when each routine turns out to be pretty much what you expected." Não tenha medo de escrever nomes longos Escreva exatamente o que está sendo feito

Slide 37

Slide 37 text

ARGUMENTOS Nenhum é sempre melhor 1 ou 2 aceitáveis / 3 deve ser evitado (aumento do número de argumentos dificulta escrever testes)

Slide 38

Slide 38 text

"Flag Arguments" nunca (boolean) Deixa claro que faz mais de uma coisa Refatore para um método para cada coisa

Slide 39

Slide 39 text

Encapsule argumentos em objetos (dá mais significado ao argumento) private function makeCircle(float $x, float $y): Circle { // ... } private function makeCircle(Point $point): Circle { // ... }

Slide 40

Slide 40 text

Verbos e palavras-chave (dá mais significado ao argumento) public function find(Email $email): User { // ... } public function findUserByEmail(Email $email): User { // ... }

Slide 41

Slide 41 text

PREFIRA EXCEPTIONS São melhores que códigos de erros try/catch em método separado $user = $this­>getLoggedUser(); public function getLoggedUser(): ?User { try { return $this­>userResolver­>user(); } catch (EmptyTokenException $e) { return null; } }

Slide 42

Slide 42 text

CAPÍTULO 4 Comentários "Don't comment bad code - rewrite it." Brian W. Kernighan e P. J. Plaugher

Slide 43

Slide 43 text

"Truth can only be found in one place: the code. Only the code can truly tell you what it does."

Slide 44

Slide 44 text

No content

Slide 45

Slide 45 text

COMENTÁRIOS ACEITÁVEIS Legal Comments Clarification (formato de datas) Comentários TODO Referência a issues (ticket Jira)

Slide 46

Slide 46 text

CONSIDERAÇÕES Remova comentários que não adicionam nada (docblock) Referências internas Críticas ou sugestões

Slide 47

Slide 47 text

/** * Returns the day of the month. * * @return int the day of the month. */ public function getDayOfMonth(): int { return $this­>dayOfMonth; }

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

/** The first name */ private string $firstName; /** The last name */ private string $lastName; /* Added by Junior */ $response = new InputStreamResponse(); $response­>setBody($formatter­>getResultStream()); // $resultStream = $formatter­>getResultStream(); // $reader = new StreamReader($resultStram);

Slide 50

Slide 50 text

CAPÍTULO 5 Formatação Visual importa sim!

Slide 51

Slide 51 text

Escolhe um code style e o siga Agrupe blocos de linhas no mesmo contexto Declare variáveis próximo da utilização Variáveis de instância sempre no topo da classe

Slide 52

Slide 52 text

Funções dependentes próximas Não alinhe horizontalmente class FitNesseExpediter implements ResponseSender { private Socket $socket; private InputStream $input; private OutputStream $output; private Request $request; private Response $response; private FitNesseContext $context; private float $requestParsingTimeLimit; protected float $requestProgress; }

Slide 53

Slide 53 text

Indentação. Nem precisa falar, né?

Slide 54

Slide 54 text

CAPÍTULO 6 Objetos e Estrutura de Dados "There is a reason that we keep our variables private. We don't want anyone else to depende on them. We want to keep the freedom to change their type or implementation on a whim or an impulse."

Slide 55

Slide 55 text

Esconder implementação expões abstrações Manipular a essência dos dados por abstrações OOP permite adicionar novas classes sem mudar as existentes

Slide 56

Slide 56 text

LEI DE DEMETER "Fale com amigos, não com estranhos." Encapsule o máximo possível. Exponha apenas o necessário.

Slide 57

Slide 57 text

DTO (DATA TRANSFER OBJECTS) Classes puras somente para transferir dados Imutável. Somente getters.

Slide 58

Slide 58 text

class Address implements ResponseSender { private string $street; private string $city; private Country $country; public function __construct( string $street, string $city, Country $country ) { $this­>street = $street; $this­>city = $city; $this­>country = $country; } // Only getters }

Slide 59

Slide 59 text

CAPÍTULO 7 Error Handling "...things can go wrong, and when they do, we as programmers are responsible for making sure that our code does what it needs to do."

Slide 60

Slide 60 text

Exceptions sempre. Código de erro nunca! Escreva o bloco try/catch primeiro (facilita pensar em todas as possíveis exceções)

Slide 61

Slide 61 text

Exceptions como primeira opção Trate a exceção na origem public function resolveUser(): User { $user = $this­>findInRequest(); if (!$user) { throw new UserNotFoundException(); } return $user; } try { $user = $resolver­>resolveUser(); } catch (UserNotFoundException $e) { return new Response(/**/); }

Slide 62

Slide 62 text

DEFINA CONTEXTO public static function byEmail(string $email): self { return new self( sprintf('User not found with the email %s', $email) ); } throw UserNotFoundException::byEmail($user­>getEmail());

Slide 63

Slide 63 text

NULL RETURN Evite retornar null Dispare exception e trate na origem Use Null Objects

Slide 64

Slide 64 text

NÃO PASSE NULL COMO PARÂMETRO Perde consistência Adiciona complexidade (mais ifs)

Slide 65

Slide 65 text

CAPÍTULO 9 Unit Tests "The Agile and TDD movements have encouraged many programmers to write automated unit tests, and more are joining their ranks every day. But in the mad rush to add testing to our discipline, many programmers have missed one of the more subtle, and important, points of writing good tests."

Slide 66

Slide 66 text

AS 3 LEIS DO TDD 1. Você não deve escrever código antes de ter escrito um teste pra ele que irá falhar. 2. Você não deve escrever mais de um teste unitário que seja suficiente para passar. 3. Você não deve escrever mais código do que o necessário para fazer o teste passar.

Slide 67

Slide 67 text

TESTES ANTES OU DEPOIS? Antes! Ciclo 1, 2, 3 que irá cobrir todos os casos possíveis, incluindo de falhas. Escreva um teste e o faça falhar! Adicione aquele if maroto e faça o teste passar!

Slide 68

Slide 68 text

CLEAN TESTS? Clean Code também para testes Crie "helpers" em forma de trait para adicionar funcionalidades extras trait JwtAuthenticationTrait { use HttpRequestsTrait; public function loginUser(User $user): void { $serializedToken = $this­>generateTokenForUser($user); $this­>request­>withHeader( 'HTTP_AUTHORIZATION', sprintf('Bearer %s', $serializedToken) ); } }

Slide 69

Slide 69 text

DICAS PARA CLEAN TESTS Apenas um assert por teste (customize!) (nomes de testes mais claros) Apenas um conceito por teste (cada caso possível é um novo teste)

Slide 70

Slide 70 text

Teste com usuário admin + teste com não admin public function test_only_admins_can_change_user_type(): void {} public function test_admin_users_can_change_user_type(): void {} public function test_non_admin_users_cannot_change_user_type(): void

Slide 71

Slide 71 text

"FIRST" RULE Fast: testes devem ser rápidos Independent: não devem compartilhar detalhes Repeatable: independentes de ambiente Self-Validating: ou passou ou não passou (boolean) Timely: escreva testes sempre antes (depois + difícil de testar)

Slide 72

Slide 72 text

BÔNUS! Regra dos 3As (Arrange, Act, Assert) public function test_admin_users_can_change_user_type(): void { // Arrange $admin = $this­>makeAdminUser(); $user = $this­>makeNormalUser(['type' => User::TYPE_MEMBER]); $this­>loginAsUser($admin); // Act $formatter = new Formatter(); $user = $formatter­>format($user); // Assert $this­>assertEquals(User::TYPE_MEMBER, $user­>getType()); }

Slide 73

Slide 73 text

CAPÍTULO 10 Classes "Let's talk about Clean Classes"

Slide 74

Slide 74 text

ENCAPSULAMENTO private por padrão protected quando necessário (herança ou testes) Foco sempre em encapsular primeiro! (use abstrações)

Slide 75

Slide 75 text

CLASSES PEQUENAS Evite "God Classes" (SRP) Tamanho é determinado pelo nome! (evite Manager ou Processor) Caso feliz: ter um único método público (SRP) (facilita muito abstração)

Slide 76

Slide 76 text

COESÃO Busque sempre alta coesão (alto relacionamento entre métodos e propriedades) Poucas variáveis de instância por classe (favorece SRP!)

Slide 77

Slide 77 text

COESÃO ENTRE CLASSES Use e abuse de Dependency Injection (favorece SRP!) Favorece DRY (Don't Repeat Yourself)

Slide 78

Slide 78 text

"COMPOSITION OVER INHERITANCE" Mais fácil com classes menores Nova funcionalidade? Nova classe + Dependency Injection da anterior!

Slide 79

Slide 79 text

Nada de TokenManager ou coisas do tipo! OCP - Open-close Principle final class JwtTokenGenerator implements TokenGeneratorInterface {} final class IntegrationTokenGenerator implements TokenGeneratorInterface { private TokenGeneratorInterface $tokenGenerator; public function __construct(TokenGeneratorInterface $tokenGenerator) { $this­>tokenGenerator = $tokenGenerator; } }

Slide 80

Slide 80 text

CAPÍTULO 12 Emergent Design "What if there were four simple rules that you could follow that would help you create good designs as you worked?"

Slide 81

Slide 81 text

4 REGRAS DO SIMPLE DESIGN RULE

Slide 82

Slide 82 text

REGRA 1 RODE TODOS OS TESTES Continuamente (Loop 1, 2, 3) Incrementalmente (Continuous Integration)

Slide 83

Slide 83 text

REGRA 2 A 4 REFATORE Remova código, não só adicione! "The fact that we have these tests eliminates the fear that cleaning up the code will break it!" TDD (testes ANTES! coverage!)

Slide 84

Slide 84 text

REGRA 2: ELIMINE DUPLICIDADE "Duplication is the primary enemy of a well-designed system!" Use e abuse de SRP!

Slide 85

Slide 85 text

REGRA 3: GARANTA EXPRESSIVIDADE Escolha de bons nomes Utilização de Design Patterns Facilite a vida do próximo dev! Ele pode ser você!

Slide 86

Slide 86 text

REGRA 4: MINIMIZE CLASSES E MÉTODOS Regra menos importante! Não exagere na SRP! Não tenha classes ou métodos demais

Slide 87

Slide 87 text

OBRIGADO!

Slide 88

Slide 88 text

OBJECT CALISTHENICS 9 regras que vão mudar sua forma de programar

Slide 89

Slide 89 text

QUALIDADE DE CÓDIGO

Slide 90

Slide 90 text

CLEAN CODE / SOLID

Slide 91

Slide 91 text

Você realmente escreve códigos 100% SOLID? #sinceridade

Slide 92

Slide 92 text

SOBRE SOLID: É um objetivo muito ousado Exige muitos conceitos de OOP Envolve muita experiência em abstração Exige um time muito "Maduro "

Slide 93

Slide 93 text

SOLID não é um processo SOLID é o objetivo final... ...de um processo muito longo

Slide 94

Slide 94 text

COMO COMEÇAR O PROCESSO?

Slide 95

Slide 95 text

É impossível aplicar SOLID em uma classe "gorda"

Slide 96

Slide 96 text

No content

Slide 97

Slide 97 text

Sua classe precisa de uma dieta

Slide 98

Slide 98 text

OBJECT CALISTHENICS

Slide 99

Slide 99 text

Jeff Bay, 2008 The Thoughtworks Anthology

Slide 100

Slide 100 text

9 REGRAS/EXERCÍCIOS para deixar seus objetos em forma

Slide 101

Slide 101 text

ATENÇÃO Não se engane Algumas regras são bem difíceis Aplique com cautela Não seja um extremista Invista tempo em cada regra Algumas regras são dependentes

Slide 102

Slide 102 text

REGRA 1 Apenas um nível de indentação por método

Slide 103

Slide 103 text

MOTIVOS Evitar efeito hadouken Facilitar leitura do código Promover SRP (Single Responsibility Principle)

Slide 104

Slide 104 text

public function notify(DateInterval $dateInterval): void { $posts = $this­>postsRepository ­>findByDateInterval($dateInterval); foreach ($posts as $post) { $totals = $this­>report­>calculateTotals($post); foreach ($post­>authors() as $author) { $this­>message­>sendTo($author, $totals); } } } 1 2 3 4 5 6 7 8 9 10 11 12

Slide 105

Slide 105 text

$totals = $this­>report­>calculateTotals($post); foreach ($post­>authors() as $author) { $this­>message­>sendTo($author, $totals); } public function notify(DateInterval $dateInterval): void 1 { 2 $posts = $this­>postsRepository 3 ­>findByDateInterval($dateInterval); 4 5 foreach ($posts as $post) { 6 7 8 9 10 } 11 } 12

Slide 106

Slide 106 text

$this­>notifyAuthors($post); private function notifyAuthors(Post $post): void { $totals = $this­>report ­>calculateTotals($post); foreach ($post­>authors() as $author) { $this­>message ­>sendTo($author, $totals); } } public function notify(DateInterval $dateInterval): void 1 { 2 $posts = $this­>postsRepository 3 ­>findByDateInterval($dateInterval); 4 5 foreach ($posts as $post) { 6 7 } 8 } 9 10 11 12 13 14 15 16 17 18 19 20

Slide 107

Slide 107 text

REGRA 2 Não use ELSE

Slide 108

Slide 108 text

MOTIVOS Porque else não serve para nada Porque você não precisa do else Na verdade você nunca precisou do else

Slide 109

Slide 109 text

private function authorizeUser(User $user): bool { if ($user­>isAdmin()) { $result = true; } else { $result = false; } return $result; } private function authorizeUser(User $user): bool { if ($user­>isAdmin()) { return true; } return false; // Ou só return $user­>isAdmin() }

Slide 110

Slide 110 text

private function response(Request $request): Response { if ($request­>expectsJson()) { $response = $this­>createJsonResponse(); } else { $response = $this­>createHtmlResponse(); } return $this­>formatResponse($response); }

Slide 111

Slide 111 text

private function response(Request $request): Response { $response = $this­>buildFromRequest($request); return $this­>formatResponse($response); } private function buildFromRequest(Request $request): Response { if ($request­>expectsJson()) { return $this­>createJsonResponse(); } return $this­>createHtmlResponse(); }

Slide 112

Slide 112 text

REGRA 3 Envolva seus tipos primitivos

Slide 113

Slide 113 text

MOTIVOS Tipos primitivos são muito permissivos Não possuem nenhum significado/comportamento Encapsulamento Value Objects em DDD

Slide 114

Slide 114 text

public function charge(int $amount, string $currency): string { $this­>paymentProvider­>chargeUser( $this­>user­>id(), $amount, $currency ); return $this­>paymentProvider ­>confirmationNumber(); }

Slide 115

Slide 115 text

class Money { private int $amount; private Currency $currency; public function __construct(int $amount, string $currency) { $this­>amount = $amount; $this­>currency = new Currency($currency); } public function amount(): int { return $this­>amount; } public function currencyAbbreviation(): string { return $this­>currency ­>abbreviation(); } }

Slide 116

Slide 116 text

class Currency { private string $abbreviation; public function __construct(string $abbreviation) { if (!$this­>abbreviationIsValid($abbreviation)) { throw new InvalidCurrencyAbbreviationException; } $this­>abbreviation = strtoupper($abbreviation); } public function abbreviation(): string { return $this­>abbreviation; } // Code }

Slide 117

Slide 117 text

public function charge(Money $price): string { $this­>paymentProvider­>chargeUser( $this­>user­>id(), $price­>amount(), $price­>currencyAbbreviation(), ); return $this­>paymentProvider ­>confirmationNumber(); }

Slide 118

Slide 118 text

REGRA 4 Envolva suas collections em classes

Slide 119

Slide 119 text

MOTIVOS Te força a usar SRP Agrupa ações semelhantes na mesma classe

Slide 120

Slide 120 text

CONCEITO Manipular objectos similares em um só lugar Somente uma propriedade por classe collection Métodos realizam ações no conjunto de objectos

Slide 121

Slide 121 text

class ActiveUsersCollection implement Iterator { private array $users = []; public function __construct(array $users) { $this­>users = $users; } public function filterByActivationProcess(): self { // Code } }

Slide 122

Slide 122 text

REGRA 5 Use apenas um ponto (­>) por linha

Slide 123

Slide 123 text

MOTIVOS Facilita a leitura Lei de Demeter (fale com amigos) Reforça o Open-close principle

Slide 124

Slide 124 text

EXCEÇÕES Fluent Interfaces / Chaining Pattern $builder­>where(...)­>order(...)­>limit(...)­>get()

Slide 125

Slide 125 text

class RemoteCompany { private CountriesCollection $countries; public function addCountries(EmployeesCollection $employees): void { foreach ($employees as $employee) { $this­>countries­>add( $employee­>address­>country­>code(); ) } } } 1 2 3 4 5 6 7 8 9 10 11 12 13

Slide 126

Slide 126 text

class Employee { public function addToCollection(CountriesCollection $countries): void { $countries­>add( $this ­>address ­>addToCollection($countries) ); } }

Slide 127

Slide 127 text

class RemoteCompany { private CountriesCollection $countries; public function addCountries(EmployeesCollection $employees): void { foreach ($employees as $employee) { $employee­>addToCollection( $this­>countries ); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13

Slide 128

Slide 128 text

REGRA 6 Não abrevie

Slide 129

Slide 129 text

MOTIVOS Aumentar entendimento do contexto Porque abreviação é uma merda Porque eu não sou obrigado a entender

Slide 130

Slide 130 text

No content

Slide 131

Slide 131 text

EXEMPLOS $cfClient quer dizer $cloudflareClient $rsp quer dizer $requestServiceProvider $mgSrv quer dizer $mongoService

Slide 132

Slide 132 text

DICA Nomes estão ficando muito grandes? Reveja SRP! Sua classe/método pode estar fazendo coisa demais

Slide 133

Slide 133 text

REGRA 7 Mantenha todas as classes pequenas

Slide 134

Slide 134 text

MOTIVOS Mais fáceis de manter SRP Porque classe grande é uma merda

Slide 135

Slide 135 text

REGRINHAS Classes com até 50 linhas 150 linhas (PHP) Pacotes com no máximo 10 arquivos

Slide 136

Slide 136 text

REGRA 8 Não tenha mais que duas variáveis de instância em sua classe

Slide 137

Slide 137 text

No content

Slide 138

Slide 138 text

MOTIVOS SRP Porque classe grande é uma merda

Slide 139

Slide 139 text

No content

Slide 140

Slide 140 text

class Customer { private Name $name; private int $customerId; } class Name { private string $firstName; private string $lastName; }

Slide 141

Slide 141 text

Não seja um extremista! para lógica de domínio para representação de dados (Entity)

Slide 142

Slide 142 text

REGRA 9 Não use getters ou setters A galera de Java pira

Slide 143

Slide 143 text

No content

Slide 144

Slide 144 text

MOTIVOS Podem ferir SRP / Encapsulamento Regra: Tell, don't ask! Não possuem significado algum "Immutable classes" pra accessors (sem tomada de decisão)

Slide 145

Slide 145 text

class Company { private EmployeesCollection $employees; public function setEmployees(EmployeesCollection $employees): void { $this­>employees = $employees; } }

Slide 146

Slide 146 text

class Company { private EmployeesCollection $employees; public function hireEmployee(Employee $employee): void { $this­>employees­>add($employee) } }

Slide 147

Slide 147 text

class Exam { private int $total; public function setTotal(int $total): void { $this­>total = $total; } }

Slide 148

Slide 148 text

class Exam { private int $total; public function addCorrectQuestion(Question $question): void { $this­>total += $question­>total(); } }

Slide 149

Slide 149 text

Getters/Setters. Evil. Period. Yegor Bugayenko https://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html

Slide 150

Slide 150 text

ALGUMAS VERDADES Lemos mais código que escrevemos Escrever bem é difícil Exige prática Requer tempo

Slide 151

Slide 151 text

Você escreve para outro dev ler Você pode ser esse outro dev Devs são burros preguiçosos Incluindo eu e você Previna problemas com devs

Slide 152

Slide 152 text

PRÓXIMOS PASSOS Hexagonal Architecture / DDD (abstrações) SOLID / Clean Code

Slide 153

Slide 153 text

Domain-Driven Design in PHP Carlos Buenosvinos, Christian Soronellas, and Keyvan Akbary https://leanpub.com/ddd-in-php

Slide 154

Slide 154 text

OBRIGADO!

Slide 155

Slide 155 text

PHP DEFENSIVO 16 dicas para prevenir problemas futuros (internamente)

Slide 156

Slide 156 text

REFERÊNCIA Extremely Defensive PHP Marco Pivetta PHPSW: Coding Practices, June 2015 @Ocramius https://www.youtube.com/watch?v=8d2AtAGJPno

Slide 157

Slide 157 text

OBJETIVOS Evitar problemas a nível de código Assume que devs irão errar em algum momento Previnir o impacto destes erros Amenizar impacto direto na Arquitetura

Slide 158

Slide 158 text

Direção Defensiva Programação Defensiva

Slide 159

Slide 159 text

REGRAS Nunca confie no código de outra pessoa Nunca confie no seu código (sua forma de programar muda ao longo do tempo)

Slide 160

Slide 160 text

POKA-YOKE "...evitar a ocorrência de defeitos em processos de fabricação e/ou na utilização de produtos." (exemplo do carro automático)

Slide 161

Slide 161 text

CONSIDERAÇÕES Essas práticas são excelentes: a longo prazo trabalhando em equipe para projetos open-source

Slide 162

Slide 162 text

REFLEXÃO "Código não é reutilizável. Abstrações são reutilizáveis." Marco Pivetta (@Ocramius)

Slide 163

Slide 163 text

#1 - INTERFACIFICATION Abstraia tudo que possa ser compartilhado

Slide 164

Slide 164 text

Abstrações / Interfaces Contratos Illuminate\Contracts\Foo

Slide 165

Slide 165 text

PSR-7: HTTP MESSAGE INTERFACES Psr\Http\Message\RequestInterface Psr\Http\Message\ResponseInterface

Slide 166

Slide 166 text

#2 - IMUTABILIDADE Evite alterar o estado de um objeto após sua inicialização

Slide 167

Slide 167 text

Value Objects em DDD Command Handler pattern

Slide 168

Slide 168 text

final class ListAllBookingsCommand { private string $branchId; private Carbon $startDate; private Carbon $endDate; public function __construct( string $branchId, Carbon $startDate, Carbon $endDate ) { $this­>branchId = $branchId; $this­>startDate = $startDate; $this­>endDate = $endDate; } // Getters }

Slide 169

Slide 169 text

#3 - NO SETTERS Setters mudam o comportamento de uma classe em tempo de execução (+1 ponto de falha / testes)

Slide 170

Slide 170 text

OOP se resume à comunicação entre objetos (envio de mensagens / comportamentos) (substantivo / verbo)

Slide 171

Slide 171 text

Dependências somente no construtor! (seu DI Container agradece!)

Slide 172

Slide 172 text

#4 - NAMED CONSTRUCTORS Dê significado quando construir um objeto new Foo()?

Slide 173

Slide 173 text

CARBON (muito útil quando se possui muitos parâmetros no construtor) mocks / dependências $date = new Carbon(/* what? */); $date = Carbon::createFromTimestamp(1559763294); $date = Carbon::createFromDatetime($dateTime); $date = Carbon::parse('2019­06­07 09:34:19');

Slide 174

Slide 174 text

class ColorTransformer { private function __construct(string $hexadecimal) {} public static function createFromRgb( int $red, int $green, int $blue ): self { // convert from RGB to HEX return new self($hexadecimal); } public static function createFromHexadecimal() {} public static function createFromHsl() {} public static function createFromCmyk() {} public function toRgb(): RgbColor {} public function toHsl(): HslColor {} public function toCmyk(): CmykColor {} public function toHexadecimal(): HexadecimalColor {} }

Slide 175

Slide 175 text

#5 - DEPENDÊNCIA OPCIONAL JAMAIS Conheça e use Null/Fake Objects

Slide 176

Slide 176 text

class UserLogin { private Logger $logger; public function setLogger(Logger $logger = null) { $this­>logger = $logger; } }

Slide 177

Slide 177 text

class UserLogin { private Logger $logger; public function __construct(Logger $logger) { // } } $userLogin = new UserLogin(new FakeLogger());

Slide 178

Slide 178 text

#6 - EVITE RETORNAR NULL Use Null Objects (evita vários ifs + favorece exceptions)

Slide 179

Slide 179 text

class NullLogger implements LoggerInterface { public function log(string $str): void { // do nothing } }

Slide 180

Slide 180 text

#7 - PUBLIC METHODS Evite métodos públicos desnecessários (melhor caso é sempre 1 por classe!) regra do substantivo / verbo

Slide 181

Slide 181 text

Stefan Priebsch ( ) | "A public method is like a child: once you've written it, you are going to maintain it for the rest of its life!" @spriebsch thePHP.cc

Slide 182

Slide 182 text

#8 - BOOLEAN COMO PARÂMETRO Evite utilizar variáveis boolean como parâmetro de método (+1 caso de teste! SEMPRE!)

Slide 183

Slide 183 text

public function delete(User $user, bool $softDelete = true): bool { // code if ($softDelete) { // code } } public function delete(User $user): bool { // code } public function softDelete(User $user): bool { // code }

Slide 184

Slide 184 text

nº parâmetros boolean * 2 = # de test cases para o método

Slide 185

Slide 185 text

#9 - ENCAPSULE ESTADOS Tome cuidado com objetos que representam estados

Slide 186

Slide 186 text

class MoneyTransfer { public function __construct( Money $amount, DateTime $transferDate ) { $this­>amount = $amount; $this­>transferDate = clone $transferDate; } } class MoneyTransfer { public function __construct( Money $amount, DateTimeImmutable $transferDate ) { $this­>amount = $amount; $this­>transferDate = $transferDate; } }

Slide 187

Slide 187 text

#10 - FINAL CLASSES POR PADRÃO "Composition over inheritance"

Slide 188

Slide 188 text

COMO NÃO FAZER? class Db { /* ... */ } class Core extends Db { /* ... */ } class User extends Core { /* ... */ } class Admin extends User { /* ... */ } class Bot extends Admin { /* ... */ } class BotThatDoesSpecialThings extends Bot { /* ... */ } class PatchedBot extends BotThatDoesSpecialThings { /* ... */

Slide 189

Slide 189 text

Não permita herança por padrão Se realmente necessário você vai voltar atrás Composition pode ser usada a qualquer momento Herança só é realmente necessária em poucos casos

Slide 190

Slide 190 text

QUANDO REALMENTE APLICAR A final class implementa uma interface Todos os métodos públicos da final class são parte da interface Abstração sempre! (mocking final classes)

Slide 191

Slide 191 text

LEMBRETE! Você pode extender interfaces interface Foo extends Bar {}

Slide 192

Slide 192 text

#11 - PRIVATE POR PADRÃO Só exponha o que é realmente necessário! (private + public)

Slide 193

Slide 193 text

protected é desnecessário com final class Propriedades são geralmente dependências Na maioria das vezes não é necessário expor dependências (Object Calisthenics #5: one dot per line!) Métodos public são ações/transações!

Slide 194

Slide 194 text

COMO NÃO FAZER? public function addMoney(Money $money): void { $this­>money[] = $money; } public function markBillPaidWithMoney(Bill $bill, Money $money): void { $this­>bills[] = $bill­>paid($money); } $bankAccount­>addMoney($money); $bankAccount­>markBillPaidWithMoney($bill, $money);

Slide 195

Slide 195 text

Métodos públicos são ações! public function payBill(Bill $bill, Money $money): void { $this­>addMoney($money); $this­>markBillPaidWithMoney($bill, $money); } private function addMoney(Money $money): void { $this­>money[] = $money; } private function markBillPaidWithMoney(Bill $bill, Money $money): void { $this­>bills[] = $bill­>paid($money); } $bankAccount­>payBill($bill, money);

Slide 196

Slide 196 text

#12 - NÃO ASSUMA "IDEMPOTÊNCIA" Chamadas consecutivas podem se alterar!

Slide 197

Slide 197 text

"Em matemática e ciência da computação, a idempotência é a propriedade que algumas operações têm de poderem ser aplicadas várias vezes sem que o valor do resultado se altere após a aplicação inicial." Wikipedia (convenhamos, isso é feio pra bosta ne!) $userId = $controller­>request()­>get('userId'); $userRoles = $controller­>request()­>get('userRoles'); $request = $controller­>request();* $userId = $request­>get('userId'); $userRoles = $request­>get('userRoles');

Slide 198

Slide 198 text

#13 - NÃO USE TIPOS mixed declare(strict_types=1) por padrão

Slide 199

Slide 199 text

Se pode ser muita coisa, encapsule! /** * @param mixed $identifier ID, e­mail address or phone number * @param string $password The provided password * @return bool */ public function login($identifier, $password): bool { // ... }

Slide 200

Slide 200 text

public function login(UserIdentifier $identifier, string $password): bool { // ... }

Slide 201

Slide 201 text

Mas ainda pode melhorar!

Slide 202

Slide 202 text

#14 - USE VALUE OBJECTS Use e abuse de objetos!!!

Slide 203

Slide 203 text

final class PasswordChanger { private UserRepository $userRepository; public function change( UserIdentifier $identifier, Password $password ): bool { // ... } } class Password { private string $password; public function __construct(string $password) { $this­>validate($password); $this­>password = $password; } }

Slide 204

Slide 204 text

Value Objects encapsulam validação! (garantia de consistência centralizada)

Slide 205

Slide 205 text

#15 - TOME CUIDADO COM Trait São ótimas, mas você pode se encrencar!

Slide 206

Slide 206 text

Em PHP Trait é Ctrl + C / Ctrl + V * Aumenta muito o acoplamento! As vezes DRY não é o melhor caso!

Slide 207

Slide 207 text

Aumenta a dependência da Trait (nem tudo deve ser "compartilhável")

Slide 208

Slide 208 text

#16 - TESTE TODOS OS CENÁRIOS Você tem certeza que testou tudo?

Slide 209

Slide 209 text

public function foo(): Identifier { if (/**/) { $this­>bar(); // 1 } if (/**/) { return $this­baz(); // 2 } return $this­>bar(); // 3 }

Slide 210

Slide 210 text

Dependendo do método vai ser foda! Tendência a diminuir a complexidade! (basicamente fazer menos coisas, e isso é bom!) Testou o caso da Exception? Testou o retorno true e também o false?

Slide 211

Slide 211 text

Não seja um extremista #ficaadica

Slide 212

Slide 212 text

OBRIGADO!

Slide 213

Slide 213 text

ARQUITETURA DE SOFTWARE o básico não tão básico que você precisa saber

Slide 214

Slide 214 text

AGENDA Arquitetura & Design Screaming Architecture Clean Architecture Primeiros Passos

Slide 215

Slide 215 text

ARQUITETURA & DESIGN

Slide 216

Slide 216 text

ARQUITETURA Visão em alto nível "Arquitetura de uma casa" Conexões e Comunicação de modo geral

Slide 217

Slide 217 text

DESIGN Estruturas e decisões em baixo nível "Estilo dos móveis de uma casa" Design Patterns

Slide 218

Slide 218 text

Robert C. Martin "O objetivo da arquitetura de so ware é minimizar os recursos humanos necessários para construir e manter um sistema."

Slide 219

Slide 219 text

DESENVOLVIMENTO So ware difícil de manter tem vida curta Arquitetura facilita desenvolvimento para o time que o mantém

Slide 220

Slide 220 text

Uma boa arquitetura começa com bons acordos Decisões em conjunto (time!) Comunicação é fundamental Mudar arquitetura é muito caro!

Slide 221

Slide 221 text

MATURIDADE Às vezes grandes projetos começam pequenos Zero (ou pouca) arquitetura Problemas de arquitetura aparecem bem depois

Slide 222

Slide 222 text

OBJETIVOS Fácil desenvolvimento / baixo custo Publicação rápida e fácil Diminuir custos com manutenção (custo mais caro)

Slide 223

Slide 223 text

ARQUITETURA EM CAMADAS "Deixe opções em aberto" Camadas independentes Casos de uso independentes Comandos independentes

Slide 224

Slide 224 text

SCREAMING ARCHITECTURE

Slide 225

Slide 225 text

"Arquitetura fala por si só" Sobre o que é seu so ware? Olhando código! Leilão? Loja virtual? Reserva de hotéis? Ou uma aplicação Laravel? Zend? Symfony?

Slide 226

Slide 226 text

"Uma boa arquitetura de so ware permite que decisões sobre frameworks, banco de dados, servidores web, e outros detalhes de ferramentas de ambiente, fiquem pra depois." Uncle Bob "Frameworks são opções a serem deixadas em aberto."

Slide 227

Slide 227 text

QUAL O FOCO? "Uma boa arquitetura enfatiza os casos de uso e os dissocia de preocupações periférias."

Slide 228

Slide 228 text

A Web é uma arquitetura?

Slide 229

Slide 229 text

Não! Web é o mecanismo de entrega Sua arquitetura não deve se preocupar com entrega A Web é apenas um detalhe

Slide 230

Slide 230 text

SOBRE TESTES

Slide 231

Slide 231 text

Mais fácil escrever (e manter) "unit tests" em arquitetura totalmente baseada em casos de uso.

Slide 232

Slide 232 text

PARA TESTAR: Você não precisa de framework Você não precisa de servidor web Você não precisa de banco de dados

Slide 233

Slide 233 text

Lembrete: seus casos de uso são desacoplados! Seus casos de uso coordenam suas classes Entity. "Seus objetos Entity devem ser objetos simples que não possuem dependência nenhuma com frameworks ou bancos de dados."

Slide 234

Slide 234 text

"Sua arquitetura deve dizer aos leitores sobre o sistema, não sobre o framework usado no sistema."

Slide 235

Slide 235 text

CLEAN ARCHITECTURE

Slide 236

Slide 236 text

ARQUITETURAS MAIS CONHECIDAS Domain-Driven Design (DDD) Hexagonal Architecture

Slide 237

Slide 237 text

DOMAIN-DRIVEN DESIGN Implementing Domain-Driven Design (Vaughn Vernon)

Slide 238

Slide 238 text

HEXAGONAL ARCHITECTURE "Hexagonal Architecture - Message-Oriented So ware Design" (Matthias Noback) https://youtu.be/K1EJBmwg9EQ

Slide 239

Slide 239 text

CARACTERÍSTICAS COMUNS Independente de frameworks (framework-as-a-tool) Testável sem qualquer elemento externo Independente da UI (Web / Console) Independente de banco de dados Independente de qualquer agente externo

Slide 240

Slide 240 text

"INDEPENDÊNCIA"

Slide 241

Slide 241 text

https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

Slide 242

Slide 242 text

THE DEPENDENCY RULE "Dependências de código devem apontar somente para dentro, na direção de políticas de nível mais alto." Nada em um círculo interno pode saber alguma coisa concreta sobre algo em um círculo externo.

Slide 243

Slide 243 text

THE DEPENDENCY RULE (AS 4 CAMADAS) 1. Entities (Enterprise Business Rules) 2. Use Cases (Application Business Rules) 3. Interface Adapters 4. Frameworks and Drivers

Slide 244

Slide 244 text

1. ENTITIES Encapsulamento de "Regras de Negócio" críticas Conjunto de classes e métodos Estes objetos não devem ser afetados por mudanças de navegação de página ou segurança. Nenhuma mudança operacional deve afetar as entidades.

Slide 245

Slide 245 text

2. USE CASES Regras de Negócio específicas da aplicação Comandam o fluxo de dados ("de" e "para" as entidades) Mudanças nesta camada não devem afetar as entidades. (CommandHandler Pattern)

Slide 246

Slide 246 text

3. INTERFACE ADAPTERS Conversão de dados externos para camadas internas ("Use Cases" e "Entities" - e vice-versa) MVC (Model, View, Controller) Ex: códigos SQL Nenhum código de camadas mais internas deve saber algo de banco de dados, por exemplo.

Slide 247

Slide 247 text

4. FRAMEWORKS AND DRIVERS Geralmente não se escreve código nesta camada Camada onde vão todos os detalhes ("Web é um detalhe", "banco de dados é um detalhe") Do lado de fora estes detalhes não podem fazer muito estrago. Uncle Bob "Não case com o framework!"

Slide 248

Slide 248 text

ATRAVESSANDO LIMITES Usando Dependency Inversion (SOLID) Exemplo: um caso de uso precisa chamar um "Presenter", mas se chamar diretamente violaria a "Dependency Rule" - nada de dentro deve saber sobre algo de fora. O caso de uso deve chamar uma interface (abstração) ("use case output port" - figura)

Slide 249

Slide 249 text

class ListActiveUsersHandler { private ViewRendererInterface $viewRenderer; public function __construct(ViewRendererInterface $viewRenderer) { $this­>viewRenderer = $viewRenderer; } public function handle(ListActiveUsersCommand $command): string { return $this­>viewRenderer ­>render('foo', [ 'data' => $command­>getData(), ]); } }

Slide 250

Slide 250 text

ATRAVESSANDO LIMITES Exemplo do que não se deve fazer Consulta ao ORM retorna um row object, não a representação do seu domínio. Você não pode passar esse row object para dentro do círculo, pois as camadas internas não devem saber nada sobre objetos das camadas de fora (BD). Doctrine ORM vs Eloquent ORM (Laravel) DTO (Data Transfer Object)

Slide 251

Slide 251 text

POR ONDE COMEÇAR? Command & CommandHandler Pattern Command seria um "DTO" Handler seria o "Use Case"

Slide 252

Slide 252 text

Command é um objeto simples imutável, que contem todos os inputs necessários para executar o "Handler" Handler é a execução do comando, com suas próprias dependências. Ele sabe que precisa do Command para processar.

Slide 253

Slide 253 text

Command para Handler

Slide 254

Slide 254 text

USE CASE: CREATE USER CreateUserCommand / CreateUserHandler

Slide 255

Slide 255 text

1. CONTROLLER ACTION POST /users UsersController@create()

Slide 256

Slide 256 text

final class CreateUserAction { private CreateUserHandler $handler; public function __construct(CreateUserHandler $handler) { $this­>handler = $handler; } public function __invoke(RequestInterface $request): ResponseInterface { $user = $this­>handler­>handle( new CreateUserCommand( $request­>getAttribute('email'), $request­>getAttribute('firstName'), $request­>getAttribute('lastName'), $request­>getAttribute('password'), ) ); return new JsonResponse($user, 201); } }

Slide 257

Slide 257 text

2. COMMAND Classe simples que guarda os dados necessários para execução da ação. Classe imutável!

Slide 258

Slide 258 text

final class CreateUserCommand implements CommandInterface { private string $email; private string $firstName; private string $lastName; private string $password; public function __construct( string $email, string $firstName, string $lastName, string $password ) { $this­>email = $email; $this­>firstName = $firstName; $this­>lastName = $lastName; $this­>password = $password; } // Getters: getEmail(), getFirstName(), getLastName(), getPassword() }

Slide 259

Slide 259 text

3. HANDLER Caso de uso propriamente dito Recebe o Command com todos os dados necessários

Slide 260

Slide 260 text

final class CreateUserHandler implements HandlerInterface { private UserRepositoryInterface $userRepository; private PasswordEncrypterInterface $passwordEncrypter; public function __construct( UserRepositoryInterface $userRepository, PasswordEncrypterInterface $passwordEncrypter ) { $this­>userRepository = $userRepository; $this­>passwordEncrypter = $passwordEncrypter; } public function handle(CreateUserCommand $command): UserEntity { return $this­>userRepository­>create([ 'email' => $command­>getEmail(), 'firstName' => $command­>getFirstName(), 'lastName' => $command­>getLastName(), 'password' => $this­>passwordEncrypter($command­>getPassword()), ]); } }

Slide 261

Slide 261 text

PRÓXIMO PASSO Adicionar um CommandBus (responsável por executar os Handlers) composer require league/tactician

Slide 262

Slide 262 text

INDICAÇÃO DE LIVRO Clean Architecture (Robert C. Martin)

Slide 263

Slide 263 text

Grossi, Junior Estudar arquitetura de so ware vai te mostrar que mágicas não existem!

Slide 264

Slide 264 text

OBRIGADO!

Slide 265

Slide 265 text

SOLID PRINCIPLES entenda antes de tentar fazer

Slide 266

Slide 266 text

AGENDA Introdução Objetivos do SOLID Os princípios Conclusão

Slide 267

Slide 267 text

INTRODUÇÃO Bom so ware começa com código limpo Na construção de uma casa / so ware: Tijolos ruins implicam em código ruim Código ruim pode ser escrito mesmo com tijolos bons

Slide 268

Slide 268 text

SOLID Atua "no tijolo" e na "construção da casa" "Os SOLID Principles nos dizem como organizar nossas funções e estruturas de dados em classes, e como essas classes devem ser conectadas." -- Robert C. Martin (Uncle Bob)

Slide 269

Slide 269 text

Em outras palavras... "Os "SOLID Principles" nos garante uma base firme a longo prazo e nos previne de fazer merda mesmo com essa base firme." -- Junior Grossi

Slide 270

Slide 270 text

ASPECTOS DO SOLID Implicação na qualidade do código Maior implicação na arquitetura (nosso foco) Foco a longo prazo da aplicação de SOLID

Slide 271

Slide 271 text

"SOLID é uma excelente forma de design, que possui impacto direto na qualidade da arquitetura de so ware adotada. Ele se encaixa perfeitamente nos padrões atuais de arquitetura de so ware." -- Junior Grossi

Slide 272

Slide 272 text

SOLID DDD (Domain-Driven Design) SOLID Hexagonal Architecture SOLID CommandHandler Pattern SOLID CQRS / Event Sourcing

Slide 273

Slide 273 text

OBJETIVOS Criação de estruturas de so ware que: Toleram mudanças facilmente São fáceis de entender São reutilizáveis

Slide 274

Slide 274 text

UM POUCO DE HISTÓRIA SOLID foi consolidado pelo Uncle Bob Eles (princípios) começaram a ser "montados" na década de 1980 (diferentes autores) Foram ordenados em uma outra ordem (ser ser SOLID) Em 2004 Michael Feathers enviou um e-mail ao Uncle Bob dizendo que aqueles princípios, se reordenados, formariam a palavra SOLID.

Slide 275

Slide 275 text

SOLID é o objetivo final a ser alcançado Exige prática, paciência e perseverança

Slide 276

Slide 276 text

OS PRINCÍPIOS SOLID (foco na arquitetura)

Slide 277

Slide 277 text

SRP The Single Responsibility Principle (S de SOLID)

Slide 278

Slide 278 text

"Uma classe deve ter somente uma razão para mudar." Robert C. Martin, 2005

Slide 279

Slide 279 text

ATENÇÃO É um dos princípios mais conhecidos É um dos princípios menos aplicados É talvez o princípio menos entendido

Slide 280

Slide 280 text

"Of all the SOLID principles, the Single Responsibility Principle (SRP) might be the least well understood. That's likely because it has a particularly inappropriate name. It is too easy for programmers to hear the name and then assume that it means that every module should do just one thing." Robert C. Martin (Uncle Bob)

Slide 281

Slide 281 text

Historicamente, o SRP tem sido descrito como: "A module should have one, and only one, reason to change."

Slide 282

Slide 282 text

Os usuários e stakeholders são o motivo de mudança: "A module should be responsible to one, and only one, user or stakeholder."

Slide 283

Slide 283 text

Eles possuem visões diferentes da mesma coisa: "A module should be responsible to one, and only one, actor."

Slide 284

Slide 284 text

SINTOMAS DE VIOLAÇÃO

Slide 285

Slide 285 text

SINTOMA 1 Duplicação Acidental

Slide 286

Slide 286 text

class Employee { public function calculatePay() {} public function reportHours() {} public function save() {} }

Slide 287

Slide 287 text

Fonte: Clean Architecture, Robert C. Martin (Uncle Bob)

Slide 288

Slide 288 text

PROBLEMAS Os autores estão totalmente acoplados Time do CFO pode afetar algo do time do COO

Slide 289

Slide 289 text

Fonte: Clean Architecture, Robert C. Martin (Uncle Bob)

Slide 290

Slide 290 text

CONSEQUÊNCIAS Time CFO altera lógica do regularHours() O método também é usado pelo COO em reportHours()

Slide 291

Slide 291 text

"O SRP diz para separar os códigos que são dependentes de diferentes atores."

Slide 292

Slide 292 text

SINTOMA 2 Merges

Slide 293

Slide 293 text

Dois devs diferentes, de diferentes times, alterando comportamento da classe Employee.

Slide 294

Slide 294 text

Diferentes comportamentos na mesma classe Resultado: merge Muitas pessoas mudando o mesmo código "separar códigos que suportam diferentes atores"

Slide 295

Slide 295 text

SOLUÇÃO? Separar ações compartilhadas em classes Regra do "substantivo + verbo" + final classes Composition over inheritance (ou composite reuse principle)

Slide 296

Slide 296 text

Quando declarar uma classe como final? Quando ela tiver apenas um método público Provavelmente ela vai implementar alguma interface

Slide 297

Slide 297 text

SOLUÇÃO?

Slide 298

Slide 298 text

Fonte: Clean Architecture, Robert C. Martin (Uncle Bob)

Slide 299

Slide 299 text

Agrupar as responsabilidades em Facade pattern Fonte: Clean Architecture, Robert C. Martin (Uncle Bob)

Slide 300

Slide 300 text

DICA PARA SRP Um método público por classe Este método faz alguma coisa (ação/verbo) Abstração mais fácil (Dependency Inversion)

Slide 301

Slide 301 text

OCP The Open-Close Principle (O de SOLID)

Slide 302

Slide 302 text

"Um artefato de so ware deve ser aberto para extensão mas fechado para modificação." Bertrand Meyer, 1988

Slide 303

Slide 303 text

Adicionar algo não implica em mudar algo Adicionar comportamentos Separação de acordo com SRP

Slide 304

Slide 304 text

EXTRATO BANCÁRIO Home-banking: página HTML do extrato (números negativos em vermelho)

Slide 305

Slide 305 text

Adicionar exportação em PDF para impressão P&B (números negativos em vermelho não vão aparecer) Primeiro pensamento? Adicionar um if if ($source === 'report') { $number = '(' . $number . ')'; } else { $number = '' . $number . ''; }

Slide 306

Slide 306 text

Você alterou a classe para adicionar um comportamento

Slide 307

Slide 307 text

"Uma boa arquitetura de so ware reduziria a quantidade de código alterado para próximo do mínimo. O ideal seria zero."

Slide 308

Slide 308 text

OCP (funções diferentes para diferentes ações) (SRP em conjunto) StatementDataGenerator (dados) StatementHtmlDecorator (cor vermelha) StatementReportDecorator (parênteses)

Slide 309

Slide 309 text

Dependency Inversion

Slide 310

Slide 310 text

class StatementGenerator { private RedColorFormatter $formatter; public function __construct(RedColorFormatter $formatter) { $this­>formatter = $formatter; } public function generate(): string { return $this­>formatter­>format( $this­>getData() ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Slide 311

Slide 311 text

public function __construct(RedColorFormatter $formatter) { $this­>formatter = $formatter; } class StatementGenerator 1 { 2 private RedColorFormatter $formatter; 3 4 5 6 7 8 9 public function generate(): string 10 { 11 return $this­>formatter­>format( 12 $this­>getData() 13 ); 14 } 15 } 16

Slide 312

Slide 312 text

Como mudar o "formatter" em runtime? Dependency Inversion

Slide 313

Slide 313 text

public function __construct(FormatterInterface $formatter) { $this­>formatter = $formatter; } class RedColorFormatter implements FormatterInterface {} class ParenthesesFormatter implements FormatterInterface {} class NegativeSignFormatter implements FormatterInterface {}

Slide 314

Slide 314 text

// HTML (Web) $generator = new StatementGenerator( new RedColorFormatter() ); // Report $generator = new StatementGenerator( new ParenthesesFormatter() );

Slide 315

Slide 315 text

SRP OCP DIP

Slide 316

Slide 316 text

LSP The Liskov Substitution Principle (L de SOLID)

Slide 317

Slide 317 text

"Se q(x) é uma propriedade demonstrável dos objetos x de tipo T. Então q(y) deve ser verdadeiro para objetos y de tipo S onde S é um subtipo de T." Barbara Liskov, 1988

Slide 318

Slide 318 text

No content

Slide 319

Slide 319 text

"Uma classe base deve poder ser substituída pela sua classe derivada."

Slide 320

Slide 320 text

Fonte: Clean Architecture, Robert C. Martin (Uncle Bob)

Slide 321

Slide 321 text

class Billing { private LicenseInterface $licence; public function __construct(LicenseInterface $license) { $this­>license = $license; } public function execute() { return $this­>license­>calcfee( $this­>fooBar() ); } class PersonalLicense implements LicenseInterface {} class BusinessLicense implements LicenseInterface {}

Slide 322

Slide 322 text

O PROBLEMA DO QUADRADO/RETÂNGULO (exemplo de violação do LSP)

Slide 323

Slide 323 text

Fonte: Clean Architecture, Robert C. Martin (Uncle Bob)

Slide 324

Slide 324 text

O problema é o comportamento da classe base, do ponto de vista que quem vai usar. O usuário espera o comportamento de um retângulo (2 lados de medidas diferentes)

Slide 325

Slide 325 text

"teria de adicionar um if do lado do User pra saber como se comportar quando é um retângulo ou quadrado" $rectangle = $this­>rectangle; $rectangle­>setWidth(5); $rectangle­>setHeight(2); $this­>assertEquals(10, $r­>area());

Slide 326

Slide 326 text

LSP + ARQUITETURA O conceito de "base" pode ser estendido

Slide 327

Slide 327 text

EXEMPLO Comparador de preços de taxis

Slide 328

Slide 328 text

Usuário recebe a lista de preços e serviços Você "escolhe" um motorista, que possui uma URL (no serviços externo) purplecab.com/driver/bob

Slide 329

Slide 329 text

Você sempre envia dados adicionais pickupAddress | pickupTime | destinationAddress

Slide 330

Slide 330 text

PUT purplecab.com/driver/bob pickupAddress=foo& pickupTime=123456789& destinationAddress=bar

Slide 331

Slide 331 text

No content

Slide 332

Slide 332 text

ISP The Interface Segregation Principle (I de SOLID)

Slide 333

Slide 333 text

Talvez o princípio mais simples de entender

Slide 334

Slide 334 text

"Nenhum cliente deve ser forçado a depender de métodos que ele não usa" Robert C. Martin, 2002

Slide 335

Slide 335 text

HISTÓRIA Consultoria na Xerox (impressoras) Cada impressora tinha uma função (ou mais de uma) Classe Job com várias funções Dificuldade de manter pequenas mudanças em pequenas funções

Slide 336

Slide 336 text

No content

Slide 337

Slide 337 text

interface Animal { public function walk(); public function fly(); public function swim(); }

Slide 338

Slide 338 text

class Bird implements Animal { public function walk() {} // OK public function fly() {} // OK public function swim() {} // ? } class Frog implements Animal { public function walk() {} // OK public function fly() {} // ? public function swim() {} // OK } class Cat implements Animal { public function walk() {} // OK public function fly() {} // ? public function swim() {} // ? }

Slide 339

Slide 339 text

Implicações a nível de linguagem (Java - necessidade de recompilar novamente) Implicações a nível de arquitetura (Adicionando comportamentos desnecessários) "gato não voa, nem nada!"

Slide 340

Slide 340 text

segregation / separação interface WalkInterface { public function walk(); } interface FlyInterface { public function fly(); } interface SwimInterface { public function swim(); }

Slide 341

Slide 341 text

class Bird implements WalkInterface, FlyInterface { public function walk() {} public function fly() {} } class Frog implements WalkInterface, SwimInterface { public function walk() {} public function swim() {} } class Cat implements WalkInterface { public function walk() {} }

Slide 342

Slide 342 text

Interface? Menor possível! (interfaces grandes podem ferir ISP)

Slide 343

Slide 343 text

DIP The Dependency Inversion Principle (D de SOLID)

Slide 344

Slide 344 text

"Os sistemas mais flexíveis são aqueles cujas dependências do código fonte são referências apenas à abstrações, não à classes concretas" Robert C. Martin, 1994

Slide 345

Slide 345 text

CONCEITOS

Slide 346

Slide 346 text

DEPENDÊNCIAS Classes que são necessárias para uma outra Dependency Injection

Slide 347

Slide 347 text

class ListAllUsersAction { private MysqlUserRepository $userRepository; public function __construct(MysqlUserRepository $userRepository) { $this­>userRepository = $userRepository; } public function __invoke(): JsonResponse {} }

Slide 348

Slide 348 text

Dependências somente no construtor! Constructor Injection

Slide 349

Slide 349 text

Setter Injection Method Injection public function setLogged(Logger $logger): void { $this­>logger = $logger; } public function login(Logger $logger): bool { $logger­>log('User logged in'); }

Slide 350

Slide 350 text

ABSTRAÇÕES São interfaces, ou contratos, ou qualquer coisa parecida Dizem apenas o que as classes concretas devem fazer, não como fazer interface UserRepositoryInterface { public function getAll(): Collection; public function find(Uuid $uuid): User; }

Slide 351

Slide 351 text

CLASSES CONCRETAS A implementação por si só ("como") Podem variar por N motivos class MysqlUserRepository implements UserRepositoryInterface { private $dependency1; private $dependency2; public function getAll() {} public function find(Uuid $uuid): User {} }

Slide 352

Slide 352 text

Mudar interfaces implica em mudar as implementações Mudar implementações nem sempre implica em mudar as interfaces

Slide 353

Slide 353 text

"Interface são menos voláteis do que implementações" "Indeed, good so ware designers and architects work had to reduce the volatility of interfaces. They try to find ways to add functionality to implementations without making changes to the interfaces." Robert C. Martin "Code is not reusable, code is crap! Abstractions are reusable." Marco Pivetta (@Ocramius)

Slide 354

Slide 354 text

ALGUMAS REGRAS Não referencie classes concretas voláteis (Dependency Injection) Não derive de classes concretas voláteis (evite herança) Não sobrescreva funções concretas (crie classes abstratas) Nunca mencione o nome de nada concreto ou volátil (só pra reforçar)

Slide 355

Slide 355 text

PROBLEMA Criar instâncias de implementações com base em abstrações (implementações possuem dependências)

Slide 356

Slide 356 text

SOLUÇÕES Dependency Injection Container Abstract Factory Pattern

Slide 357

Slide 357 text

Dependency Injection Container $this­>app­>bind( UserRepositoryInterface::class, function (Application $app) { return new MysqlUserRepository( new PDO( $dsn = sprintf( 'mysql:dbname=%s;host=%s', $app­>config['dbname'], $app­>config['dbhost'], ), $user = $app­>config['dbuser'], $password = $app­>config['dbpassword'] ) ); } );

Slide 358

Slide 358 text

class ListAllUsersAction { private UserRepositoryInterface $userRepository; public function __construct( UserRepositoryInterface $userRepository ) { $this­>userRepository = $userRepository; } public function __invoke(): JsonResponse {} }

Slide 359

Slide 359 text

Abstract Factory Pattern "Abstract Factory is a creational design pattern that lets you produce families of related objects without specifying their concrete classes." https://refactoring.guru/design-patterns/abstract-factory https://designpatternsphp.readthedocs.io

Slide 360

Slide 360 text

class ProductFactory { private const SHIPPING_COSTS = 50; public function createShippableProduct(int $price): Product { return new ShippableProduct( $price, self::SHIPPING_COSTS ); } public function createDigitalProduct(int $price): Product { return new DigitalProduct($price); } }

Slide 361

Slide 361 text

CONCLUSÃO SOLID não é fácil Exige maturidade do time (muito OOP) Quase sempre tem interfaces no meio Exige um entendimento muito amplo do domínio

Slide 362

Slide 362 text

SUGESTÃO Hexagonal Architecture Domain-Driven Design

Slide 363

Slide 363 text

OBRIGADO!